Archilyzer · Source

archilyzer

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

commit 9993290b532327fcff20c626f4e84437d91bfc99
parent bdc6ffd2a3362fee0b2f35694d3e53d8a5d162cc
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Wed, 29 Jul 2026 12:23:14 -0400

Two bugs the live sweep found that no test would have

Ran the orchestrator for real, scoped to one small channel (`teamrcn`, 7 videos,
0.27 audio-hours) against ollama and the real corpus. It armed with its scope
persisted, launched the per-channel job, generated 7 digests with correct
provenance, refreshed the snapshot and ended clean. A second process then
resumed from settings alone, found nothing to do, launched ZERO jobs and exited
— which is the whole of the B2 claim: a restart re-derives from disk and re-does
nothing.

It also surfaced two things:

**Every channel's progress bar would have stalled one short, forever.** The run
reported `current: 7, target: 8`. The eighth directory has no transcript, so
`runDigestBatch` correctly drops it before it is ever a candidate — but
`countMissingDigests` counted it, sizing the bar against work the batch will
never do. Corpus-wide that is 2,987 videos, so most channels would never read as
finished, and over 81 days "never finishes" is indistinguishable from wedged.
`countMissingDigests` now applies the same eligibility the batch does, which is
the agreement digestTarget.ts's header exists to enforce.

**Stopping a sweep left its scope behind.** The write was guarded on
`sweepEnabled`, so stopping an already-disarmed sweep skipped it entirely and
`sweepChannels` kept the old value — after which the next "Start Digest Sweep"
would silently cover only some weeks-old channel list and report itself done.
Verified fixed: settings now read `sweepEnabled=false sweepChannels=[]`.

Neither was reachable from a unit test or the e2e suite; both needed the thing
to actually run.

Measured in passing: 41.4 s of wall clock for 989 audio-seconds — ~151 s per
audio-hour, uncontended, on 2.4-minute videos. Consistent with the recorded
"short videos cost ~2x per audio-hour" against the 90 s corpus average, and NOT
a substitute for the contended-vs-uncontended measurement that still matters.

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

Diffstat:
Mcommon/controller/digestBatch.ts | 15+++++++++++++++
Mcommon/controller/digestSweep.ts | 18++++++++++++++++--
Mplans/FACTS.md | 18++++++++++++++++++
3 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/common/controller/digestBatch.ts b/common/controller/digestBatch.ts @@ -546,6 +546,21 @@ export async function countMissingDigests( : await readdir(dataDir).catch(() => [] as string[]); let missing = 0; for (const id of dirs) { + // SAME ELIGIBILITY AS THE BATCH, and it has to be. A video with no + // normalized transcript is a transcription problem, not a digest one: + // runDigestBatch drops it before it is ever a candidate, so counting it here + // sizes the progress bar against work the batch will never do and the bar + // stalls one short of complete — for the rest of the run. + // + // Measured: `teamrcn` reported target 8, current 7. The eighth directory is + // a channel-id-named folder with no transcript. Corpus-wide that is 2,987 + // videos, i.e. most channels would never show as finished, which over an + // 81-day sweep is indistinguishable from wedged. This is exactly the + // counter-disagreement digestTarget.ts's header was written about. + const duration = await readDurationFast( + path.join(dataDir, id, CUES_JSON_FILENAME), + ); + if (duration === null || duration <= 0) continue; const record = await loadDigest(path.join(dataDir, id)); const fresh = sections.every((section) => isSectionFresh(record, section, target), diff --git a/common/controller/digestSweep.ts b/common/controller/digestSweep.ts @@ -377,11 +377,25 @@ export async function resumeDigestSweepIfEnabled( // resurrect a sweep the operator had just stopped. export async function stopDigestSweep(): Promise<boolean> { const settings = getSettings(); - if (settings.digest.sweepEnabled) { + // Note the second clause: guarding on `sweepEnabled` alone means a stop on an + // already-disarmed sweep skips the write entirely and leaves the SCOPE behind + // — which is how a stale scope survives to narrow the next sweep. Measured: + // after a stop, settings still read sweepChannels ["teamrcn"]. + if ( + settings.digest.sweepEnabled || + settings.digest.sweepChannels.length > 0 + ) { // Awaited so a restart cannot resurrect a sweep the operator just stopped. await writeSettings({ ...settings, - digest: { ...settings.digest, sweepEnabled: false }, + digest: { + ...settings.digest, + sweepEnabled: false, + // Cleared too. A leftover scope is worse than no scope: the next + // "Start Digest Sweep" would silently cover only the channels of a + // sweep somebody stopped weeks ago, and report itself finished. + sweepChannels: [], + }, }); } const live = getSingleton().live; diff --git a/plans/FACTS.md b/plans/FACTS.md @@ -773,6 +773,7 @@ every one of the 21 is accounted for below. Do not chase these. | `settings.spec` social-links | 1 | fails on base (known placeholder collision) | | `site-scope.spec` dashboard stats | 1 | fails on base | | `cut-release.spec` (both) | 2 | expected with a dirty working tree — the spec rewrites the real `editor/CHANGELOG.md` and makes a git commit | +| `scheduler.spec:28` — queues due channels | 1 | fails on base (verified 2026-07-29 against `bbaa887`) | | `sync-break-on-existing-tree.spec` | 1 | **passes in isolation** — order-dependent flake, not a base failure | | `undownloaded.spec:53` | 1 | **passes in isolation** (all 4 in 22.6 s) — the CPU-contention flake already recorded below, seen again 2026-07-27 | @@ -829,6 +830,23 @@ A frontmatter parser for Phase 1.5 must be hand-rolled (three scalar/list field --- +**Re-verified 2026-07-29** after the backfill-readiness work: **368 passed / 22 +failed of 390** — one better than the previous run. All 22 are in the table +above. Two were isolated rather than assumed: + +| Spec | Isolation result | +| --- | --- | +| `duplicate-shorts.spec:328` | **was a real regression, now fixed.** New review buttons carried `aria-label="confirm duplicate cluster <id>"`, and `getByLabel` matches by SUBSTRING, so the card's own `duplicate cluster <id>` locator resolved to 3 elements. Renamed; 3/3 green. **Third occurrence of this collision in this repo.** | +| `scheduler.spec:28` | fails on base too — verified by checking out `bbaa887` and re-running. Add it to the base-failure set. | + +Common tests **374** (was 356). Export playwright **150/150**. `tsc` clean in all +three packages. + +**`cut-release.spec` mutates the repo, and it did again**: it rewrote +`editor/CHANGELOG.md` AND created a real `Release editor 9.9.10` commit. Reset +after the run. Budget for this every time the full suite is run with a dirty or +in-progress changelog. + ## Smoke test results (2026-07-25) Run against a real 176-minute podcast transcript (`UndertheTeaVT/data/5JRFDQ7TtZ8`, 3877