Archilyzer · Source

archilyzer

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

commit feef1dd454f68e27d7702271bc2d19fd8779394a
parent abcfb4233f72ce9f921e78ebd16618afef19f738
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri, 26 Jun 2026 14:53:16 -0400

Phase 1: consolidate per-kind job metadata into one table

Before this, per-kind knowledge was scattered across four sites: the
DRAINABLE_KINDS set (registry.ts), the JOB_KIND_LABELS map (editor
jobKindLabels.ts), the runJobSpec switch, and the spec-presence bookmark
check. This adds common/jobs/jobKinds.ts as the single source of truth for
a kind's label, drainability, bookmarkability, queue-key strategy, and
(future) scheduler tier.

- registry.ts: drop DRAINABLE_KINDS; re-export isDrainableKind from jobKinds
  (import path for existing callers unchanged).
- editor jobKindLabels.ts: re-export jobKindLabel from the shared table.
- jobKinds.test.ts: pins the pre-refactor drainable set + label map verbatim,
  proving the move is byte-for-byte behavior-preserving (label-less replayable
  kinds still fall back to the raw kind).

Data-only, zero behavior change. Typecheck (common+editor) clean; jobKinds +
registry unit tests green.

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

Diffstat:
Acommon/jobs/jobKinds.test.ts | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acommon/jobs/jobKinds.ts | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcommon/jobs/registry.ts | 24++++--------------------
Meditor/app/jobs/jobKindLabels.ts | 34++++++----------------------------
4 files changed, 319 insertions(+), 48 deletions(-)

diff --git a/common/jobs/jobKinds.test.ts b/common/jobs/jobKinds.test.ts @@ -0,0 +1,77 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { isDrainableKind, jobKindLabel, getJobKind } from "./jobKinds"; + +// Run with: pnpm --filter yt-dlp-transcript-common exec tsx --test jobs/jobKinds.test.ts +// +// Phase 1 of the queue refactor moved the drainable-kinds set and the label map +// into one metadata table (jobKinds.ts). These snapshots pin the PRE-refactor +// values verbatim, so the consolidation is provably behavior-preserving: every +// kind that drained before still drains, every label is byte-identical, and +// kinds that previously had no label still fall back to their raw kind. + +// Verbatim copy of the old DRAINABLE_KINDS set from registry.ts. +const OLD_DRAINABLE = [ + "whisper-all", + "whisper-bucket-downloaded-no-transcript", + "download-from-playlist", + "download-missing", + "download-missing-subs", + "sync", + "retry-bucket", + "auto-transcribe", + "auto-download", +]; + +// Verbatim copy of the old JOB_KIND_LABELS map from editor jobKindLabels.ts. +const OLD_LABELS: Record<string, string> = { + "auto-transcribe": "Auto-transcribe", + "auto-download": "Auto-download runner", + "auto-download-unit": "Auto-download", + "whisper-all": "Transcribe all", + "whisper-bucket-downloaded-no-transcript": "Transcribe downloaded audio", + "download-from-playlist": "Download from playlist", + "download-missing": "Download missing", + "download-missing-subs": "Download missing subs", + "import-one": "Import video", + "redownload-archive": "Archive source video", + "retry-bucket": "Retry", + "clean-audio-transcribed": "Clean audio", + "check-kept-deleted": "Check kept videos", + "persist-kept": "Persist kept videos", + "backup-saved-videos": "Back up saved videos", + "verify-saved-video-backup": "Verify saved-video backup", + sync: "Sync", +}; + +test("drainable kinds match the old DRAINABLE_KINDS set exactly", () => { + const drainSet = new Set(OLD_DRAINABLE); + for (const kind of OLD_DRAINABLE) { + assert.equal(isDrainableKind(kind), true, `${kind} should drain`); + } + // No kind in the table drains unless it was in the old set. + for (const kind of Object.keys(OLD_LABELS)) { + assert.equal( + isDrainableKind(kind), + drainSet.has(kind), + `${kind} drainability changed`, + ); + } + // Unknown kinds are not drainable. + assert.equal(isDrainableKind("no-such-kind"), false); +}); + +test("labels match the old JOB_KIND_LABELS map exactly", () => { + for (const [kind, label] of Object.entries(OLD_LABELS)) { + assert.equal(jobKindLabel(kind), label, `label for ${kind}`); + } +}); + +test("label-less and unknown kinds fall back to the raw kind", () => { + // Replayable kinds that never had a label entry must still fall back. + assert.equal(jobKindLabel("store-playlist"), "store-playlist"); + assert.equal(jobKindLabel("transcode-failures"), "transcode-failures"); + // Genuinely unknown kind. + assert.equal(jobKindLabel("totally-unknown"), "totally-unknown"); + assert.equal(getJobKind("totally-unknown"), undefined); +}); diff --git a/common/jobs/jobKinds.ts b/common/jobs/jobKinds.ts @@ -0,0 +1,232 @@ +// The single source of truth for what a job `kind` IS. Before this table, the +// same per-kind knowledge was scattered across four places: the DRAINABLE_KINDS +// set (registry.ts), the JOB_KIND_LABELS map (editor jobKindLabels.ts), the +// switch in editor runJobSpec.ts, and the spec-presence bookmark check. Phase 1 +// of the queue refactor consolidates the drainable + label data here; later +// phases consume `defaultTier` (scheduler) and lean on `bookmarkable`. +// +// Adding a new job kind should mean adding ONE entry here (plus its replay +// handler in editor/app/jobs/jobReplayRegistry.ts if it is bookmarkable). + +// How a kind picks its registry queueKey. Descriptive only — the real key is +// still computed by the action that creates the job (channelQueueKey / +// downloadQueueKey / ""). "parallel" means queueKey === "" (runs unserialized). +export type QueueKeyStrategy = "parallel" | "platform" | "custom"; + +// Scheduler priority tiers. A higher-priority tier's queued jobs run before a +// lower one's; FIFO within a tier. Consumed starting Phase 4 (the scheduler); +// today every non-background job is effectively "foreground". +export type SchedulerTier = "urgent" | "foreground" | "background"; + +export type JobKindMeta = { + kind: string; + // Human-readable label for the UI. Optional: a kind without one falls back to + // its raw machine kind (preserving the prior JOB_KIND_LABELS fallback), so a + // new kind is never invisible. + label?: string; + // The Drain (soft-cancel) button is only offered for running jobs of a + // drainable kind — i.e. one whose controller honors the drain signal (stops + // starting new sub-operations, lets in-flight ones finish). + drainable: boolean; + // Whether this kind's action attaches a replayable JobSpec (and thus has a + // replay handler). Actual bookmark-ability still keys off spec PRESENCE on the + // record at runtime — this flag just says the kind CAN be bookmarked/retried. + bookmarkable: boolean; + // Descriptive: how the action derives its queueKey. Not consumed as logic. + queueKeyStrategy: QueueKeyStrategy; + // Fallback scheduler tier when a record is not explicitly background. Left + // undefined today so Phase 4 maps it to "foreground" (unchanged behavior). + defaultTier?: SchedulerTier; +}; + +// One entry per kind known to the system. `label` is included only where the +// old JOB_KIND_LABELS had one, so jobKindLabel() behavior is byte-identical. +const JOB_KINDS: Record<string, JobKindMeta> = { + "auto-transcribe": { + kind: "auto-transcribe", + label: "Auto-transcribe", + drainable: true, + bookmarkable: false, + queueKeyStrategy: "parallel", + }, + "auto-download": { + kind: "auto-download", + label: "Auto-download runner", + drainable: true, + bookmarkable: false, + queueKeyStrategy: "parallel", + }, + "auto-download-unit": { + kind: "auto-download-unit", + label: "Auto-download", + drainable: false, + bookmarkable: false, + queueKeyStrategy: "platform", + }, + "whisper-all": { + kind: "whisper-all", + label: "Transcribe all", + drainable: true, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "whisper-bucket-downloaded-no-transcript": { + kind: "whisper-bucket-downloaded-no-transcript", + label: "Transcribe downloaded audio", + drainable: true, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "download-from-playlist": { + kind: "download-from-playlist", + label: "Download from playlist", + drainable: true, + bookmarkable: true, + queueKeyStrategy: "platform", + }, + "download-missing": { + kind: "download-missing", + label: "Download missing", + drainable: true, + bookmarkable: true, + queueKeyStrategy: "platform", + }, + "download-missing-subs": { + kind: "download-missing-subs", + label: "Download missing subs", + drainable: true, + bookmarkable: true, + queueKeyStrategy: "platform", + }, + "import-one": { + kind: "import-one", + label: "Import video", + drainable: false, + bookmarkable: false, + queueKeyStrategy: "custom", + }, + "redownload-archive": { + kind: "redownload-archive", + label: "Archive source video", + drainable: false, + bookmarkable: false, + queueKeyStrategy: "custom", + }, + "retry-bucket": { + kind: "retry-bucket", + label: "Retry", + drainable: true, + bookmarkable: true, + queueKeyStrategy: "platform", + }, + "clean-audio-transcribed": { + kind: "clean-audio-transcribed", + label: "Clean audio", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "check-kept-deleted": { + kind: "check-kept-deleted", + label: "Check kept videos", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "persist-kept": { + kind: "persist-kept", + label: "Persist kept videos", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "backup-saved-videos": { + kind: "backup-saved-videos", + label: "Back up saved videos", + drainable: false, + bookmarkable: false, + queueKeyStrategy: "custom", + }, + "verify-saved-video-backup": { + kind: "verify-saved-video-backup", + label: "Verify saved-video backup", + drainable: false, + bookmarkable: false, + queueKeyStrategy: "custom", + }, + sync: { + kind: "sync", + label: "Sync", + drainable: true, + bookmarkable: true, + queueKeyStrategy: "platform", + }, + // Replayable kinds that never had a JOB_KIND_LABELS entry: label omitted so + // jobKindLabel() keeps falling back to the raw kind (unchanged behavior). + "store-playlist": { + kind: "store-playlist", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "clear-failed-transcriptions": { + kind: "clear-failed-transcriptions", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "transcode-failures": { + kind: "transcode-failures", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "transcode-untranscoded": { + kind: "transcode-untranscoded", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "remove-failed-transcodings": { + kind: "remove-failed-transcodings", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "clear-failed-transcodings": { + kind: "clear-failed-transcodings", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "clean-extra-audio-formats": { + kind: "clean-extra-audio-formats", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, + "remove-wrong-format-audio": { + kind: "remove-wrong-format-audio", + drainable: false, + bookmarkable: true, + queueKeyStrategy: "custom", + }, +}; + +export function getJobKind(kind: string): JobKindMeta | undefined { + return JOB_KINDS[kind]; +} + +// Human-readable label, falling back to the raw kind for unknown/label-less +// kinds (a new kind is never invisible). +export function jobKindLabel(kind: string): string { + return JOB_KINDS[kind]?.label ?? kind; +} + +export function isDrainableKind(kind: string): boolean { + return JOB_KINDS[kind]?.drainable ?? false; +} + +export function isBookmarkableKind(kind: string): boolean { + return JOB_KINDS[kind]?.bookmarkable ?? false; +} diff --git a/common/jobs/registry.ts b/common/jobs/registry.ts @@ -351,23 +351,7 @@ export function newJobId(): string { return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; } -// Batch kinds whose controllers honor the drain signal (stop starting new -// sub-operations, let in-flight ones finish). The Drain button is only offered -// for running jobs of these kinds. -const DRAINABLE_KINDS = new Set<string>([ - "whisper-all", - "whisper-bucket-downloaded-no-transcript", - "download-from-playlist", - "download-missing", - "download-missing-subs", - "sync", - "retry-bucket", - // The auto-queue runners: drain stops launching new units, lets in-flight - // finish, then the loop returns and the job completes. See autoRunner.ts. - "auto-transcribe", - "auto-download", -]); - -export function isDrainableKind(kind: string): boolean { - return DRAINABLE_KINDS.has(kind); -} +// Which kinds honor the drain signal now lives in the job-kind metadata table +// (jobKinds.ts), the single source of truth. Re-exported here so existing +// importers of `isDrainableKind` from the registry keep resolving unchanged. +export { isDrainableKind } from "./jobKinds"; diff --git a/editor/app/jobs/jobKindLabels.ts b/editor/app/jobs/jobKindLabels.ts @@ -1,28 +1,6 @@ -// Human-readable labels for job `kind` values. The registry stores terse -// machine kinds (e.g. "whisper-all", "auto-transcribe"); the UI shows these. -// Unknown kinds fall back to the raw value so a new kind is never invisible. -const JOB_KIND_LABELS: Record<string, string> = { - "auto-transcribe": "Auto-transcribe", - // The long-lived orchestrator loop (one per kind) vs. the per-video download - // it now launches as a real job on the channel's platform queue. - "auto-download": "Auto-download runner", - "auto-download-unit": "Auto-download", - "whisper-all": "Transcribe all", - "whisper-bucket-downloaded-no-transcript": "Transcribe downloaded audio", - "download-from-playlist": "Download from playlist", - "download-missing": "Download missing", - "download-missing-subs": "Download missing subs", - "import-one": "Import video", - "redownload-archive": "Archive source video", - "retry-bucket": "Retry", - "clean-audio-transcribed": "Clean audio", - "check-kept-deleted": "Check kept videos", - "persist-kept": "Persist kept videos", - "backup-saved-videos": "Back up saved videos", - "verify-saved-video-backup": "Verify saved-video backup", - sync: "Sync", -}; - -export function jobKindLabel(kind: string): string { - return JOB_KIND_LABELS[kind] ?? kind; -} +// Human-readable labels for job `kind` values now live in the shared job-kind +// metadata table (common/jobs/jobKinds.ts), the single source of truth. This +// module re-exports the lookup so existing editor imports stay unchanged. The +// registry stores terse machine kinds (e.g. "whisper-all"); the UI shows these, +// falling back to the raw value so a new kind is never invisible. +export { jobKindLabel } from "yt-dlp-transcript-common/jobs/jobKinds";