// The band TYPE and the two pure functions over it. // // SPLIT FROM buildBands.ts DELIBERATELY, and the reason is a build error rather // than tidiness: the builder imports channelSnapshot, which imports runYtdlp, // which imports execa — so a client component importing the builder drags a // server-only process runner into the browser bundle, and `next build` fails // with a module-not-found on `node:child_process`. (Nothing catches that in // e2e, which runs in dev mode and never prerenders.) // // So StateBand and every client surface import THIS, and the builder stays // server-side. Nothing here may import anything but types. // // LIVES UNDER components/pipelines/ RATHER THAN operations/, because three // surfaces at three scales now render the same five fills: the corpus rail on // /operations, the per-channel strip on /channels, and the station foot on a // channel page. That they are literally one model, one component and one // palette is the reason the three read as one instrument rather than three // charts that happen to rhyme. // The five populations a pipeline can put a video in, plus the denominator. // // Rendered as fill PATTERNS first and hue second — solid / hatched / dotted / // hollow — because report-to-video's claim rail established here BY MEASUREMENT // that no four-colour palette clears all-pairs colour-blindness. Pattern // survives CVD, greyscale, and a dim laptop at 2am, which is when this console // is actually read. export type OperationBand = { id: string; label: string; // How many videos this pipeline has an OPINION about. null = at least one // channel cannot say. eligible: number | null; // Videos it is done with. null for the same reason. present: number | null; // What the lane can do RIGHT NOW — missing + stale + partial. The one // saturated segment on the page: glancing down the rail shows where the // colour is, i.e. where work can happen at all. reachable: number; // Waiting on an operation THIS SYSTEM produces (attribution on diarization, // digest on transcription). Hatched: it will clear itself, upstream. blocked: number; // The media is gone. Hollow: there is nothing to do here, and an opt-in // re-download is the only thing that would change it. missingInput: number; // Held by a gate — stale cues, a duration window. Dotted. deferred: number; // WHAT ONE VIDEO OF THIS OPERATION COSTS — its cost basis — in words, from // the registry. // // The fact that hid behind a shared lane name: an operation can be armed at // enormous cost and read as a quiet row, because "11,337 reachable" is the // same shape of number whether the cost basis is one audio pass per video // or ~1 model call per transcript CHUNK — a ~17x difference on the same // figure. Carried on the band so every surface that draws a backlog can // state its cost basis beside it, with no threshold and no editorialising. costBasis: string; // Whether the operation is dispatched by this system at all. False for the // two external pipelines, which are here because the rail's whole point is // that you never have to switch lanes to learn that this one is idle BECAUSE // another one is. dispatched: boolean; }; // Sum that returns null if ANY term is null. Same rule digestEligibleKnown uses // in the widget payload: a partial sum is a denominator smaller than its own // numerator, which is a worse lie than admitting the number is not knowable. export function sumOrNull( values: ReadonlyArray, ): number | null { let total = 0; for (const v of values) { if (v == null) return null; total += v; } return total; } // The fraction of a band that is `present`, or null when either half is unknown. // Null renders as an unfilled outline — never as 0, which on a fully-digested // channel would read as "nothing digested". export function bandCoverage(band: OperationBand): number | null { if (band.present == null || band.eligible == null || band.eligible <= 0) { return null; } return Math.min(1, band.present / band.eligible); } // ── THE FILL VOCABULARY, AND THE PURE READINGS OF A BAND ──────────────────── // // These live HERE, beside the type, and not beside the component that draws // them — for the same client/server reason the file header gives, in the other // direction. StateBand.tsx is `"use client"`, and a plain function exported // from a client module CANNOT BE CALLED by a server component: Next throws // "attempted to call bandSentence() from the server". The channel page's // station foot is a server component and needs the sentence and the headline, // so the pure functions belong on the shared, directive-free side. // // `pnpm build` does not catch this. The route is force-dynamic, so nothing // prerenders it and the error only appears on a request — which is what e2e // found and the build did not. // The five populations, in the order they are laid down. `present` first so a // band reads left-to-right as "done → can do → cannot do yet → cannot do at // all", which is the same direction the channel transit line runs. export const SEGMENTS = [ { key: "present", label: "done", // Solid, muted. The artifact exists; it is not where attention goes. className: "bg-muted-foreground/45", }, { key: "reachable", label: "can run now", // THE one saturated segment. className: "bg-info", }, { key: "blocked", label: "blocked upstream", // Hatched — waiting on an operation this system produces, so it will clear // itself without anyone doing anything. className: "bg-warning/25 [background-image:repeating-linear-gradient(45deg,currentColor_0_1px,transparent_1px_4px)] text-warning/70", }, { key: "deferred", label: "held by a gate", // Dotted — a gate (stale cues, a duration window) is holding it. className: "bg-transparent [background-image:radial-gradient(currentColor_0.5px,transparent_0.5px)] [background-size:3px_3px] text-muted-foreground", }, { key: "missingInput", label: "media gone", // Hollow outline — there is nothing to do. An opt-in re-download is the // only thing that would ever change it, and it must never look like work. className: "bg-transparent ring-1 ring-inset ring-border", }, ] as const; export type SegmentKey = (typeof SEGMENTS)[number]["key"]; export function segmentValue(band: OperationBand, key: SegmentKey): number { if (key === "present") return band.present ?? 0; return band[key]; } // The denominator a band's segments are drawn against. // // `eligible` when it is known. When it is NOT — a snapshot can predate the // field, and snapshots lapse — falling back to the sum of the work counts would // silently redraw the band as "100% accounted for", so instead the band renders // as an un-filled outline and says `coverage unknown`. Unknown is not zero, and // it is not full either. export function bandDenominator(band: OperationBand): number | null { if (band.eligible != null && band.eligible > 0) return band.eligible; return null; } // The sentence a band carries in its tooltip and its accessible name. // // THE FIGURES ARE STATED SEPARATELY AND NEVER SUMMED — the same rule the rail's // figure row holds, moved somewhere it applies to every scale at once. On the // measured corpus `reachable` and `missingInput` are four orders of magnitude // apart on diarization; one "remaining" number would say the same thing about a // lane that is finished and a lane that cannot start. export function bandSentence(band: OperationBand): string { const parts: string[] = [`${band.reachable.toLocaleString()} can run now`]; if (band.blocked > 0) { parts.push(`${band.blocked.toLocaleString()} blocked upstream`); } if (band.deferred > 0) { parts.push(`${band.deferred.toLocaleString()} held by a gate`); } if (band.missingInput > 0) { parts.push(`${band.missingInput.toLocaleString()} have no media left`); } const denominator = bandDenominator(band); parts.push( band.present == null || denominator == null ? "coverage unknown" : `${band.present.toLocaleString()} done of ${denominator.toLocaleString()}`, ); return parts.join(" · "); } // THE ONE POPULATION THAT DOMINATES WHAT IS LEFT, in three words. // // Not a total and not a percentage — the shape of the remainder is the thing // that varies across this corpus, and naming its largest part is the shortest // true sentence about a pipeline. "11,333 no media" and "11,329 to do" are the // same size of number and mean opposite things; a single "remaining" figure // would render them identically. export function bandHeadline(band: OperationBand): string { const candidates: Array<[number, string]> = [ [band.reachable, "to do"], [band.blocked, "blocked"], [band.missingInput, "no media"], [band.deferred, "held"], ]; let best: [number, string] | null = null; for (const c of candidates) { if (c[0] > (best?.[0] ?? 0)) best = c; } if (!best) { // Nothing outstanding. Whether that is "finished" depends on a denominator // we may not have, so say what is known and no more. return bandDenominator(band) === null ? "coverage unknown" : "nothing to do"; } return `${best[0].toLocaleString()} ${best[1]}`; } // ── A BAND FOR THE OPERATIONS A SWEEP HAS IN SCOPE ────────────────────────── // // The plan on /operations draws one strip per channel, and the strip has to // re-draw when an operation is ticked off — in the browser, with no round trip. // So the fold lives here, on the directive-free side, beside the type. // // THIS IS THE ONE PLACE SUMMING ACROSS OPERATIONS IS LEGITIMATE, and it is worth // being precise about why, because every other surface is forbidden from doing // it. The rule that forbids it (buildBands' header, backfillLaneEntriesOf's) is about a // FIGURE WITH NO COST BASIS: adding diarization's videos to attribution-text's // videos gives a number that is neither, because one video of the first costs // an audio pass and one video of the second ~1 model call per transcript chunk. // // A sweep plan is not that number. The sweep dispatches one operation on one // video at a time, so the basis here IS "one operation on one video" — and every // segment of this band is counted in it. The rows are directly comparable // because they all sit under the SAME scope, which is the control immediately // above them. Change the scope and every row changes together. // // What it still must not do is invent a denominator: `eligible` and `present` // fold with sumOrNull, so one operation that cannot say makes the whole band // say so — an outline, never 0%. export function bandForScope({ id, label, costBasis, counts, }: { id: string; label: string; costBasis: string; counts: ReadonlyArray<{ reachable: number; blocked: number; missingInput: number; deferred: number; eligible: number | null; present: number | null; }>; }): OperationBand { const band: OperationBand = { id, label, costBasis, eligible: 0, present: 0, reachable: 0, blocked: 0, missingInput: 0, deferred: 0, dispatched: true, }; for (const c of counts) { band.reachable += c.reachable; band.blocked += c.blocked; band.missingInput += c.missingInput; band.deferred += c.deferred; band.eligible = sumOrNull([band.eligible, c.eligible]); band.present = sumOrNull([band.present, c.present]); } return band; }