Archilyzer · Source

archilyzer

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

commit 9cc983adaa23b1d16fa2849758515ad7d33395a0
parent 09a21743f1c24b6497ff4ed23a0eeec9510cfb23
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri, 11 Sep 2026 18:36:39 -0400

e2e: a tick that queued nothing could not say why, and the floor a spec inherits is not the fixture's

TWO THINGS, one of which is why the other could not be diagnosed.

THE ASSERTION THREW AWAY THE ANSWER. `scheduler.spec.ts:29` read
`expect(r1.queued).toEqual(["slow-a"])`, and a tick has FOUR ways to queue
nothing: it never ran (`reason: "tick already running"`), the scheduler is off
(`reason: "scheduler disabled"`), the selector held the channel back, or the
launch was refused and its error recorded — `skipped.push({ slug, reason:
result.error })` in runTick.ts, which is where a low-disk preflight, a
per-platform rate-limit cooldown and the unreachable-media guard all land. All
four print `Received: []` and name nothing. This one ran red at `09a2174` and
then would not reproduce: eight green runs at that sha with a clean tree,
including an exact replication of the failing command, so whatever emptied
`queued` was session state that had since gone. The assertion now carries the
tick's own `reason` and slow-a's own skip line, which is the difference between
a recurrence that explains itself and this. (slow-b's "not due" is a normal skip
and stays out of the claim.)

THE DISK FLOOR A SPEC INHERITS IS THE PRODUCT'S, NOT THE FIXTURE'S.
`helpers.writeSettings` overwrites test-settings.json wholesale rather than
merging it with fixtures/test-settings.default.json, which resetData copied in
moments earlier — so a spec that does not name `minFreeDiskGB` does not get the
fixture's 0, it gets MIN_FREE_DISK_GB_DEFAULT = 5 GB. That arms the low-disk
gate and makes the spec's result depend on how much room the HOST has left, and
it fails in the worst shape available: pipelineActions' preflight returns
`{ ok: false }`, the action never runs, and the spec reports an empty list with
no mention of disk. Eighteen spec files call writeSettings without naming the
key and none of them is about disk. This is not hypothetical on this machine —
`/home` is at 6.9 G free, and channel-storage.spec.ts:209 failed 4/4 on exactly
it during slice 3 (a move is charged floor + resume margin = 7 GB) and was fixed
by writing the key out by hand at :219-221. Defaulting it in the helper is that
fix for everyone; an explicit value still wins, which is what disk-space.spec.ts
and backfill.spec.ts's "the disk floor refuses to re-acquire anything" rely on.

widget.spec.ts is the one spec that wanted the gate ARMED and was getting it by
accident, from the same gap: its cases reach for the widget's "Disk space" cell,
which the layout only draws while the gate is configured. It names the floor now
rather than inheriting one, which is the same discipline disk-space.spec.ts has
always had. (Found by the full suite, not by reasoning: 521/1 with this change,
that being the one.)

The new case in disk-space.spec.ts is what would have caught it: a spec that
names no floor gets the gate off, asserted both on the Settings field and on the
/api/jobs/active payload the indicator reads. It is deterministic — it does not
wait for a full disk to notice.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Meditor/e2e/disk-space.spec.ts | 31+++++++++++++++++++++++++++++++
Meditor/e2e/helpers.ts | 21++++++++++++++++++++-
Meditor/e2e/scheduler.spec.ts | 19++++++++++++++++++-
Meditor/e2e/widget.spec.ts | 8++++++++
4 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/editor/e2e/disk-space.spec.ts b/editor/e2e/disk-space.spec.ts @@ -272,3 +272,34 @@ test("active-jobs API and monitor widget report low disk", async ({ page }) => { expect(offPayload.disk.enabled).toBe(false); expect(offPayload.disk.low).toBe(false); }); + +// THE HARNESS'S OWN INVARIANT, pinned here because breaking it is invisible. +// +// writeSettings() replaces test-settings.json wholesale rather than merging it +// with fixtures/test-settings.default.json, so a spec that does not name +// minFreeDiskGB used to inherit the PRODUCT default (5 GB) instead of the +// fixture's 0 — arming the low-disk gate for every action in that spec and +// making its result depend on how much room the host has left. Eighteen spec +// files write settings without naming the key; none of them is about disk. +// +// The failure that costs is not a red assertion about disk, it is a red +// assertion about something else: the preflight in pipelineActions returns +// `{ ok: false }`, the action never runs, and the spec reports an empty list. +// This asserts the fixture's intent survives a spec's own write. +test("a spec that names no disk floor gets the gate switched off", async ({ + page, +}) => { + await resetData("test-pipeline"); + await writeSettings({ adminTitle: "Test Admin" }); + + await page.goto("/settings"); + await expect(page.locator('input[name="minFreeDiskGB"]')).toHaveValue("0"); + + // And the gate itself agrees — the same payload the indicator reads. + const res = await page.request.get("/api/jobs/active"); + const payload = (await res.json()) as { + disk: { enabled: boolean; low: boolean }; + }; + expect(payload.disk.enabled).toBe(false); + expect(payload.disk.low).toBe(false); +}); diff --git a/editor/e2e/helpers.ts b/editor/e2e/helpers.ts @@ -73,8 +73,27 @@ export async function resetData(fixtureName: string | null = null) { await fetch(`${baseUrl}/api/test/invalidate-cache`).catch(() => {}); } +// THE DISK FLOOR IS OFF IN TESTS UNLESS A SPEC ASKS FOR IT. +// +// This writes the settings file WHOLESALE — it does not merge with +// fixtures/test-settings.default.json, which resetData() copied in moments +// earlier — so every key that fixture sets is gone the instant a spec calls +// this. For `minFreeDiskGB` that does not fall back to something neutral: it +// falls back to the PRODUCT default, MIN_FREE_DISK_GB_DEFAULT = 5 GB, which +// arms the low-disk gate and makes the spec's outcome depend on how much room +// the HOST has left. Eighteen spec files call this without naming the key, and +// on a nearly-full disk their every media action is refused by the preflight in +// pipelineActions.lowDiskError() — silently, because the refusal is a returned +// `{ ok: false }` that no assertion reads. (channel-storage.spec.ts writes the +// key out by hand for exactly this reason; this is that fix, for everyone.) +// +// An explicit value still wins, which is what disk-space.spec.ts and +// backfill.spec.ts's "the disk floor refuses to re-acquire anything" rely on. export async function writeSettings(settings: Record<string, unknown>) { - await writeFile(testSettingsFile, JSON.stringify(settings, null, 2)); + await writeFile( + testSettingsFile, + JSON.stringify({ minFreeDiskGB: 0, ...settings }, null, 2), + ); await fetch(`${baseUrl}/api/test/invalidate-cache`).catch(() => {}); } diff --git a/editor/e2e/scheduler.spec.ts b/editor/e2e/scheduler.spec.ts @@ -67,10 +67,27 @@ test("scheduler queues due channels, skips not-due, and dedups running ones", as }); // First tick: slow-a is due and gets queued; slow-b is not due. + // + // ASSERTED WITH THE TICK'S OWN EXPLANATION ALONGSIDE IT, deliberately. This + // read `expect(r1.queued).toEqual(["slow-a"])` on its own, and a tick has + // FOUR ways to queue nothing: it never ran (`reason: "tick already + // running"`), the scheduler is off (`reason: "scheduler disabled"`), the + // channel was held back by the selector, or the launch was refused and its + // error recorded — `skipped.push({ slug, reason: result.error })` in + // runTick.ts, which is where a low-disk preflight, a rate-limit cooldown and + // the unreachable-media guard all land. All four print the same + // `Received: []`, so a red run names none of them; this one names whichever + // it was. const tick1 = await page.request.post("/api/scheduler/tick"); expect(tick1.ok()).toBeTruthy(); const r1 = (await tick1.json()) as TickResult; - expect(r1.queued).toEqual(["slow-a"]); + expect({ + queued: r1.queued, + reason: r1.reason, + // slow-b's "not due" line belongs in skipped and is not a fault, so only + // slow-a's own skip is part of the claim. + refusedSlowA: r1.skipped.filter((s) => s.slug === "slow-a"), + }).toEqual({ queued: ["slow-a"], reason: undefined, refusedSlowA: [] }); expect(r1.queued).not.toContain("slow-b"); // Second tick (no time passed): slow-a's sync is still running, so it's diff --git a/editor/e2e/widget.spec.ts b/editor/e2e/widget.spec.ts @@ -81,6 +81,14 @@ const BACKFILL_SETTINGS = { }; const TWO_WORKERS = { + // NAMED, not inherited. Several cases below reach for the widget's "Disk + // space" cell, which the layout only draws while the gate is CONFIGURED — + // and writeSettings replaces test-settings.default.json wholesale, so this + // spec used to get the gate armed by accident, from the product default that + // filled the gap the fixture's 0 left. It is a positive floor because the + // cell has to exist, not because the number matters: every assertion here is + // about position, and the cell renders whether or not the floor is met. + minFreeDiskGB: 5, workers: [ { id: "gpu", name: "GPU", kind: "local", enabled: true, priority: 0, appId: "whisper-cpp", config: {} }, { id: "cpu", name: "CPU", kind: "local", enabled: true, priority: 1, appId: "whisper-cpp", config: {} },