Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit df86e89f789a560a2bbf3ec188c972829900133f
parent 0bac8912a74c74a1eecbf39e2ec6acd240efa01c
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Sat, 22 Aug 2026 18:45:47 -0400

registry: declare an operation's group and what one unit costs

Three surfaces each kept their own hardcoded idea of which operations belong
together — the transit line, the /channels columns, the lane card — and the last
time a kind was added, one of them kept summing three unrelated operations into
a single station. `group` makes that one declaration; operationsGroupLabel
derives a set's name so a lane holding a mix falls back to "Derived data"
instead of advertising one member's label.

`costBasis` is the fact that hid. attribution-text is armed, reachable on 11,337
videos of one channel, and its unit is the transcript CHUNK, not the video —
roughly 194,000 model calls corpus-wide against one completed video. No screen
could say so, because every screen said "Backfill". Stating the unit beside the
backlog is what makes the number legible; there is deliberately no threshold and
no editorialising, because a "this is a lot" cutoff is a magic number the next
operation gets wrong.

Additive and declarative. Nothing reads either field yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Diffstat:
Mcommon/lib/backfillKinds.test.ts | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcommon/lib/backfillKinds.ts | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 153 insertions(+), 0 deletions(-)

diff --git a/common/lib/backfillKinds.test.ts b/common/lib/backfillKinds.test.ts @@ -10,7 +10,10 @@ import { resolveBackfillKinds, orderByDependencies, operationCatalog, + operationCostBasis, + operationGroup, operationLabel, + operationsGroupLabel, digestLaneFor, diarizationLaneFor, laneYieldsToTranscription, @@ -1193,6 +1196,62 @@ test("the catalog covers every operation, dispatched here or not", () => { } }); +test("every catalogued operation declares a group and a cost basis", () => { + // Both are read unconditionally by the UI — the transit line groups stations + // by `group`, and every armed operation prints `costBasis` beside its + // backlog. An entry missing either renders a blank where a fact should be, + // which is the failure mode that let attribution-text sit armed at ~194,000 + // model calls while every screen called it "Backfill". + for (const op of operationCatalog()) { + assert.ok(op.group, `${op.id} has no group`); + assert.ok(op.costBasis.length > 0, `${op.id} has no costBasis`); + } +}); + +test("the three speaker operations share one group; digest does not", () => { + assert.equal(operationGroup("diarization"), "speakers"); + assert.equal(operationGroup("attribution-diarized"), "speakers"); + assert.equal(operationGroup("attribution-text"), "speakers"); + assert.equal(operationGroup("digest"), "digest"); + assert.equal(operationGroup("download"), "media"); + assert.equal(operationGroup("transcription"), "transcript"); + // An id the catalog does not know is null, NOT filed under the first group. + assert.equal(operationGroup("no-such-operation"), null); +}); + +test("a set's label is derived, so a mixed lane cannot claim one member's name", () => { + // The whole reason this is derived: the backfill lane holds three speaker + // operations today and its station can honestly say "Speakers". Add a kind + // from another group and it degrades to the generic name rather than + // continuing to advertise a label that now describes two thirds of it. + assert.equal( + operationsGroupLabel([ + "diarization", + "attribution-diarized", + "attribution-text", + ]), + "Speakers", + ); + assert.equal( + operationsGroupLabel(["diarization", "digest"]), + "Derived data", + ); + assert.equal(operationsGroupLabel([]), "Derived data"); + // Unknown ids contribute nothing rather than poisoning a single-group set. + assert.equal(operationsGroupLabel(["digest", "no-such-op"]), "Digest"); +}); + +test("attribution-text's cost basis states the CHUNK unit, not the video", () => { + // The measured fact that hid behind the word "backfill": this lane's unit is + // the transcript chunk, so its 11,337 reachable videos are on the order of + // 194,000 model calls. Every other lane in the table is per-video, and a + // reader who assumes that of this one is wrong by ~17x. + assert.match(operationCostBasis("attribution-text"), /chunk/); + assert.match(operationCostBasis("attribution-diarized"), /per video/); + assert.match(operationCostBasis("diarization"), /per video/); + assert.equal(operationCostBasis("no-such-operation"), ""); +}); + // The accounting rule the whole feature turns on: reachable work and // needs-re-acquiring are never added together. test("counts keep reachable work and needs-re-acquiring apart", () => { diff --git a/common/lib/backfillKinds.ts b/common/lib/backfillKinds.ts @@ -243,11 +243,55 @@ export type BackfillRunOutcome = | "disabled" | "failed"; +// WHICH PIPELINE THIS OPERATION BELONGS TO, as a declared field rather than a +// list somebody maintains in a component. +// +// Three surfaces need this same grouping and each used to hardcode its own copy: +// the channel transit line (which station does this operation live under), the +// /channels table (which columns sit together), and the lane card (which +// operations does this queue actually hold). Three hardcoded lists is three +// places to forget when a kind is added — and the last time one was added, the +// transit line kept summing three unrelated operations into one station because +// nobody updated its list. +// +// Deriving the label from the group also means a lane holding a MIX cannot go +// stale: it falls back to "Derived data" rather than naming two of its three +// members. +export type OperationGroup = "media" | "transcript" | "digest" | "speakers"; + +export function groupLabel(group: OperationGroup): string { + switch (group) { + case "media": + return "Media"; + case "transcript": + return "Transcript"; + case "digest": + return "Digest"; + case "speakers": + return "Speakers"; + } +} + export type BackfillKind = { id: string; label: string; // One line of UI copy: what this backfill is, in the operator's terms. hint: string; + // The pipeline this operation belongs to. See OperationGroup. + group: OperationGroup; + // WHAT ONE UNIT OF THIS OPERATION COSTS, as a phrase — "one audio pass per + // video", "~1 model call per transcript chunk". + // + // The fact this exists to surface: an operation can be armed at enormous cost + // and read as a quiet row. attribution-text is reachable on 11,337 videos of + // one channel and roughly 194,000 model calls corpus-wide, it has completed + // ONE video, and every screen that mentioned it said only "Backfill". Printing + // the unit beside the backlog is what makes 11,337 legible. + // + // Deliberately NO threshold and no editorialising. A "this is a lot" cutoff + // would be a magic number the next operation gets wrong, and the operator is + // the one who decides what is too expensive. + costBasis: string; // What a `deferred` video of THIS kind is waiting for, and what an operator // can do about it. Belongs to the kind, not to the card: BackfillStage used to // hardcode "too long to diarize under the current limit", which was correct @@ -322,6 +366,8 @@ const diarization: BackfillKind = { id: "diarization", label: "Speaker diarization", hint: "Speaker turns captured from the audio, written to diarization.json beside the transcript.", + group: "speakers", + costBasis: "one pass over the audio per video", // The wording BackfillStage used to hardcode for every kind at once. deferredHint: "too long to diarize under the current limit — raise or clear Max audio hours in Settings to include them", @@ -491,6 +537,10 @@ const attributionText: BackfillKind = { id: "attribution-text", label: "Speaker names (from the transcript)", hint: "Speakers reconstructed from the transcript alone, for videos with no diarization. Cheaper to reach, worse than the diarized lane, and it never overwrites one.", + group: "speakers", + // The expensive one, and the reason costBasis is a field. A transcript is + // many chunks; this is the only lane in the table whose unit is not the video. + costBasis: "~1 model call per transcript chunk", tier: "lane", lane: { queueKey: BACKFILL_QUEUE, contendsFor: "network" }, enabled: (settings) => @@ -540,6 +590,8 @@ const attributionDiarized: BackfillKind = { id: "attribution-diarized", label: "Speaker names (from the audio)", hint: "Names put to the speaker clusters in diarization.json — about one model call per video, and better than the text-only lane. Needs diarization to have run first.", + group: "speakers", + costBasis: "~1 model call per video", tier: "lane", // The dependency the hint has always stated in prose. Declaring it is what // turns "needs diarization to have run first" from a sentence an operator @@ -672,6 +724,8 @@ const digest: BackfillKind = { id: "digest", label: "Digest", hint: "Chapters and tags generated from the transcript by a local or metered model. Needs a transcript first.", + group: "digest", + costBasis: "~1 model call per transcript chunk", // See the `deferred` branch in state() below for the measurement behind this // wording. It says "run the normalize pass" and NOT "it clears itself", // because nothing automatic ever will. @@ -881,6 +935,8 @@ export type ExternalOperation = { id: string; label: string; hint: string; + group: OperationGroup; + costBasis: string; lane: BackfillLane; dependsOn?: readonly string[]; dispatch: "external"; @@ -891,6 +947,8 @@ export const EXTERNAL_OPERATIONS: readonly ExternalOperation[] = [ id: "download", label: "Download", hint: "Fetching the media. Dispatched by the auto-download runner and the per-channel pipeline actions.", + group: "media", + costBasis: "one fetch per video, over the network", // Really one queue per platform (downloadQueueKey), not a single key. Named // here as the shape rather than the exact key, because the catalog's job is // the dependency graph, not dispatch. @@ -901,6 +959,8 @@ export const EXTERNAL_OPERATIONS: readonly ExternalOperation[] = [ id: "transcription", label: "Transcription", hint: "Turning audio into a transcript. Dispatched by the auto-transcribe runner across the worker pool.", + group: "transcript", + costBasis: "one pass over the audio per video, on a worker", lane: { queueKey: TRANSCRIPTION_QUEUE, contendsFor: "gpu" }, dependsOn: ["download"], dispatch: "external", @@ -913,6 +973,8 @@ export type OperationDescriptor = { id: string; label: string; hint: string; + group: OperationGroup; + costBasis: string; lane: BackfillLane; dependsOn?: readonly string[]; dispatch: BackfillDispatch; @@ -925,6 +987,8 @@ export function operationCatalog(): OperationDescriptor[] { id: k.id, label: k.label, hint: k.hint, + group: k.group, + costBasis: k.costBasis, lane: k.lane, dependsOn: k.dependsOn, dispatch: "backfill" as const, @@ -939,6 +1003,36 @@ export function operationLabel(id: string): string { return operationCatalog().find((o) => o.id === id)?.label ?? id; } +// The group an operation belongs to, or null for an id the catalog does not +// know. Null rather than a fallback group: a caller grouping by this must be +// able to tell "unknown" from "media", and silently filing a dangling id under +// the first group would put it on the wrong station. +export function operationGroup(id: string): OperationGroup | null { + return operationCatalog().find((o) => o.id === id)?.group ?? null; +} + +// What one unit of an operation costs, in words. Empty string for an unknown +// id, so a surface can print it unconditionally without a placeholder. +export function operationCostBasis(id: string): string { + return operationCatalog().find((o) => o.id === id)?.costBasis ?? ""; +} + +// The label for a SET of operations — a lane card, a transit-line station, a +// column group. One group means that group's name; a mix means "Derived data". +// +// DERIVED, never hardcoded, which is the point: a lane that gains a kind from a +// different group degrades to the honest generic name instead of continuing to +// advertise a label that now describes two thirds of what it holds. +export function operationsGroupLabel(ids: ReadonlyArray<string>): string { + const groups = new Set<OperationGroup>(); + for (const id of ids) { + const group = operationGroup(id); + if (group) groups.add(group); + } + if (groups.size !== 1) return "Derived data"; + return groupLabel([...groups][0]); +} + // The digest operation's id, named once. Surfaces that read one specific // operation off a snapshot (the digest stage card, the dashboard's coverage // instrument) need this string, and a typo in it fails the way a missing