Archilyzer · Source

archilyzer

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

commit bc0161c26db215b060048898e9808fec4cbcc4cf
parent 4b391c6f9e8e32883550e235d4dd64861da65a0e
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Thu, 24 Sep 2026 13:07:13 -0400

operations: in-flight units are job rows (inFlight.jobId, fromInFlight)

`AutoRunnerInFlight` gains `jobId?`, set inside the runner's two existing
`onChildJob` callbacks (the private childJobIds map stays private), so a
download unit names the registry job running it; transcription / digest /
backfill units are tasks on the runner's job and carry none.
`common/views/jobRows.ts` gains the pure adapter `fromInFlight(unit, {kind,
runnerJobId, detail})` → a `JobRowView` with the new `source: "runner"`:
id = the unit's jobId, else `<runnerJobId|kind>:<videoId>`; `inRegistry`
says whether it has a job page; a unit's `note` replaces the caller's
"rule N · leaf" detail and drops the synthetic videoId. InFlightList keeps
its "In flight" box and draws each unit as `<JobRow variant="compact">`
(links, elapsed; no actions, no tasks). A compact / card row links
`Job <id>` only when a runner row is `inRegistry`.

The `autoQueueStatus` payload gains that optional field; the
phase3-view-numbers tool does not cover that view.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mcommon/controller/autoRunner.ts | 7+++++--
Mcommon/views/jobRowView.ts | 6++++--
Mcommon/views/jobRows.test.ts | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcommon/views/jobRows.ts | 31+++++++++++++++++++++++++++++++
Meditor/app/jobs/components/JobRow.tsx | 8+++++---
Meditor/app/operations/components/InFlightList.tsx | 76++++++++++++++++++++++++++++++++--------------------------------------------
6 files changed, 136 insertions(+), 51 deletions(-)

diff --git a/common/controller/autoRunner.ts b/common/controller/autoRunner.ts @@ -184,6 +184,9 @@ export type AutoRunnerInFlight = { // note renders the note instead of a video link. Absent for every ordinary // unit, which is how the surfaces stay unchanged. note?: string; + // The registry job running this unit, when it is one (a download unit is a + // child job on its platform queue); absent for a task on the runner's job. + jobId?: string; }; // Why the runner is up but dispatching nothing. Every one of these was already @@ -1890,7 +1893,7 @@ async function runLoop( targets: picked.scan.targets, tracker, onLog, - onChildJob: (jid) => childJobIds.set(pick.videoId, jid), + onChildJob: (jid) => { childJobIds.set(pick.videoId, jid); const u = live.inFlight.get(pick.videoId); if (u) u.jobId = jid; }, }); return; } @@ -1905,7 +1908,7 @@ async function runLoop( onLog, signal: runSignal, drainSignal: ctx.drainSignal, - onChildJob: (jid) => childJobIds.set(pick.videoId, jid), + onChildJob: (jid) => { childJobIds.set(pick.videoId, jid); const u = live.inFlight.get(pick.videoId); if (u) u.jobId = jid; }, }); } catch (err) { result = { outcome: "failed" }; diff --git a/common/views/jobRowView.ts b/common/views/jobRowView.ts @@ -94,6 +94,8 @@ export type JobRowView = { // jobs/jobDetail.ts from the job's own replay spec; absent for every kind // that has nothing to add, so no existing row changes. detail?: string; - // Which adapter built it. Never rendered; tests and the merge read it. - source: "registry" | "archive" | "slot"; + // Which adapter built it. Never rendered; tests and the merge read it — + // except "runner": an auto-queue lane's in-flight unit (fromInFlight), which + // links to a job page only when `inRegistry` says it has one. + source: "registry" | "archive" | "slot" | "runner"; }; diff --git a/common/views/jobRows.test.ts b/common/views/jobRows.test.ts @@ -5,6 +5,7 @@ import type { JobListEntry } from "../jobs/listJobs"; import { createScheduler } from "../jobs/scheduler"; import { fromEntry, + fromInFlight, fromRecord, fromSlot, isLive, @@ -340,3 +341,61 @@ test("fromSlot builds the phantom, and a stuck row sorts above a running one", ( ]); assert.equal(ordered[0].id, phantom.id); }); + +test("fromInFlight: a download unit carries its registry job id", () => { + const row = fromInFlight( + { + videoId: "v1", + leafId: "leaf-a", + channelSlug: "alpha", + startedAt: 1_000, + jobId: "01JOBDOWNLOAD", + }, + { kind: "download", runnerJobId: "01RUNNER", detail: "rule 1 · alpha" }, + ); + assert.equal(row.id, "01JOBDOWNLOAD"); + assert.equal(row.inRegistry, true); + assert.equal(row.source, "runner"); + assert.equal(row.status, "running"); + assert.equal(row.background, true); + assert.equal(row.kind, "download"); + assert.equal(row.channelSlug, "alpha"); + assert.equal(row.videoId, "v1"); + assert.equal(row.startedAt, 1_000); + assert.equal(row.detail, "rule 1 · alpha"); +}); + +test("fromInFlight: a task unit gets a synthetic id and no job page", () => { + const unit = { + videoId: "v2", + leafId: "leaf-b", + channelSlug: "beta", + startedAt: 2_000, + }; + const withRunner = fromInFlight(unit, { + kind: "transcription", + runnerJobId: "01RUNNER", + }); + assert.equal(withRunner.id, "01RUNNER:v2"); + assert.equal(withRunner.inRegistry, false); + // No runner job known (a lane between runs): the lane kind keys it. + const bare = fromInFlight(unit, { kind: "transcription", runnerJobId: null }); + assert.equal(bare.id, "transcription:v2"); + assert.equal(bare.detail, undefined); +}); + +test("fromInFlight: a note replaces the detail and drops the synthetic video id", () => { + const row = fromInFlight( + { + videoId: "metadata-scan alpha", + leafId: "metadata-scan", + channelSlug: "alpha", + startedAt: 3_000, + note: "metadata scan · 12 titles", + }, + { kind: "download", detail: "rule 1 · alpha" }, + ); + assert.equal(row.detail, "metadata scan · 12 titles"); + assert.equal(row.videoId, undefined); + assert.equal(row.id, "download:metadata-scan alpha"); +}); diff --git a/common/views/jobRows.ts b/common/views/jobRows.ts @@ -5,6 +5,7 @@ import type { JobListEntry } from "../jobs/listJobs"; import { jobSpecDetail } from "../jobs/jobDetail"; import type { ChannelStat } from "../controller/channels"; import type { QueueView } from "../jobs/scheduler"; +import type { AutoRunnerInFlight } from "../controller/autoRunner"; import type { JobRowView, StuckReason } from "./jobRowView"; // THE ADAPTERS, THE ORDER AND THE MERGE — pure, and directive-free by @@ -332,3 +333,33 @@ export function isLive(r: JobRowView): boolean { Boolean(r.stuck) ); } + +// AN AUTO-QUEUE LANE'S IN-FLIGHT UNIT, as a job row — so the operations board +// draws what the runner is doing with the same row every other surface draws +// a job with. A download unit is a real registry job (its id rides on the unit +// as `jobId`, set when the runner's child job starts); a transcription, digest +// or backfill unit is a task on the runner's own job and has no page of its +// own, so its id is synthetic and `inRegistry` is false. +// +// `detail` ("rule 3 · newest first from alpha") is the caller's: the leaf +// sentence is an editor helper over the lane's policy, not a pure fact of the +// unit. A unit with a `note` (the download lane's channel-scoped metadata +// scan, whose `videoId` names no directory) says the note instead, and carries +// no videoId, so nothing links to a video that is not there. +export function fromInFlight( + unit: AutoRunnerInFlight, + ctx: { kind: string; runnerJobId?: string | null; detail?: string }, +): JobRowView { + return { + id: unit.jobId ?? `${ctx.runnerJobId ?? ctx.kind}:${unit.videoId}`, + kind: ctx.kind, + status: "running", + channelSlug: unit.channelSlug, + videoId: unit.note ? undefined : unit.videoId, + startedAt: unit.startedAt, + background: true, + inRegistry: Boolean(unit.jobId), + detail: unit.note ?? ctx.detail, + source: "runner", + }; +} diff --git a/editor/app/jobs/components/JobRow.tsx b/editor/app/jobs/components/JobRow.tsx @@ -118,9 +118,11 @@ export function statusColor(status: string): string { } } -// A row with a page at /jobs/<id>. -function linksToJob(_job: JobRowView): boolean { - return true; +// A row with a page at /jobs/<id>. A runner's in-flight unit that is a task +// on its runner's job (a transcription, a digest) is not a registry job, so it +// does not link to one; a download unit is, and carries its id. +function linksToJob(job: JobRowView): boolean { + return job.source !== "runner" || Boolean(job.inRegistry); } function videoHref(job: JobRowView): string | null { diff --git a/editor/app/operations/components/InFlightList.tsx b/editor/app/operations/components/InFlightList.tsx @@ -1,12 +1,18 @@ "use client"; -import Link from "next/link"; import type { AutoQueueKindStatus } from "yt-dlp-transcript-common/views/autoQueueStatus"; -import { type Channel, formatElapsed, leafOrder, leafSentence } from "./dispatch"; +import { fromInFlight } from "yt-dlp-transcript-common/views/jobRows"; +import { JobRow } from "../../jobs/components/JobRow"; +import { type Channel, leafOrder, leafSentence } from "./dispatch"; // What the runner is doing RIGHT NOW. `runner.inFlight` has always been in the // status payload — video id, owning channel, the leaf that claimed it and when // it started — and the old page rendered a count and threw the rest away. +// +// Each unit is drawn as a JOB ROW (jobs/components/JobRow.tsx, compact), off +// the pure adapter `fromInFlight` — the same row the widget and /jobs draw. A +// download unit is a registry job and links to its page; a transcription, +// digest or backfill unit is a task on the runner's job and does not. export function InFlightList({ status, @@ -30,52 +36,34 @@ export function InFlightList({ {[...items] .sort((a, b) => a.startedAt - b.startedAt) .map((item) => { + // WHICH RULE CLAIMED IT, as the ladder numbers them. A unit that + // is not a video (the download lane's metadata-scan unit, whose + // `videoId` is a synthetic key naming no directory) carries a + // `note`, which fromInFlight says instead — no "rule N" for a leaf + // that exists in no tree, and no link to a video that is not there. const at = leaves.findIndex((l) => l.id === item.leafId); const leaf = at >= 0 ? leaves[at] : null; + const detail = `${at >= 0 ? `rule ${at + 1}` : item.leafId}${ + leaf ? ` · ${leafSentence(leaf, channels)}` : "" + }`; + const row = fromInFlight(item, { + kind: status.kind, + runnerJobId: status.runner.jobId, + detail, + }); return ( - <li - key={item.videoId} - className="flex flex-wrap items-baseline gap-x-2 gap-y-0.5 text-sm" - > - <span - aria-hidden="true" - className="size-1.5 shrink-0 self-center rounded-full bg-info animate-pulse motion-reduce:animate-none" + <li key={item.videoId} className="text-sm"> + <JobRow + job={row} + variant="compact" + show={{ + actions: false, + tasks: false, + links: true, + elapsed: true, + }} + now={now} /> - {/* A UNIT THAT IS NOT A VIDEO SAYS SO INSTEAD OF LINKING. - The download lane's metadata-scan unit is channel-scoped: - its `videoId` is a synthetic key naming no directory, so the - usual link would be a 404 and the usual "rule N" would name - a leaf that exists in no tree. It carries a `note`, and a - unit with one renders the note — see AutoRunnerInFlight. */} - {item.note ? ( - <> - <Link - href={`/channels/${item.channelSlug}`} - className="font-mono text-foreground underline underline-offset-2 hover:text-brand" - > - {item.channelSlug} - </Link> - <span className="text-xs text-muted-foreground"> - {item.note} - </span> - </> - ) : ( - <> - <Link - href={`/channels/${item.channelSlug}/videos/${item.videoId}`} - className="font-mono text-foreground underline underline-offset-2 hover:text-brand" - > - {item.channelSlug}/{item.videoId} - </Link> - <span className="text-xs text-muted-foreground"> - {at >= 0 ? `rule ${at + 1}` : item.leafId} - {leaf ? ` · ${leafSentence(leaf, channels)}` : ""} - </span> - </> - )} - <span className="ml-auto tabular-nums text-xs text-muted-foreground"> - {now !== null ? formatElapsed(now - item.startedAt) : "—"} - </span> </li> ); })}