diff --git a/scripts/__tests__/run-vitest-watchdog.test.mjs b/scripts/__tests__/run-vitest-watchdog.test.mjs index 3d9f4f791b..191dbf0ec2 100644 --- a/scripts/__tests__/run-vitest-watchdog.test.mjs +++ b/scripts/__tests__/run-vitest-watchdog.test.mjs @@ -61,6 +61,21 @@ test("deriveBudgetMs: clamps to floor and ceiling", () => { ); }); +test("deriveBudgetMs: shard floor pins heavy slices above the false-kill window", () => { + // FNXC:TestInfrastructure 2026-06-20-21:52: + // Regression guard for the 5min -> 15min shard-floor raise. A value whose + // expected×multiplier lands in the *old* un-clamped window (300s..900s) must + // now clamp UP to the 15min floor. 150s × 3.5 = 525s, which was returned + // verbatim under the old 5min floor but is below the new one. Pinning the + // concrete floor value here means an accidental revert to 5min fails loudly + // instead of silently re-tightening the engine/core slices into SIGKILLs. + assert.equal(CLASS_BUDGET_BANDS.shard.floor, 15 * 60_000); + assert.equal( + deriveBudgetMs({ klass: "shard", expectedDurationMs: 150_000, timingsFresh: true }), + CLASS_BUDGET_BANDS.shard.floor, + ); +}); + test("deriveBudgetMs: unknown class falls back to the changed band", () => { assert.equal(deriveBudgetMs({ klass: "nonexistent" }), CLASS_BUDGET_BANDS.changed.ceiling); }); diff --git a/scripts/lib/run-vitest-watchdog.mjs b/scripts/lib/run-vitest-watchdog.mjs index 362ca0623a..1294836a0d 100644 --- a/scripts/lib/run-vitest-watchdog.mjs +++ b/scripts/lib/run-vitest-watchdog.mjs @@ -36,16 +36,21 @@ const MINUTE = 60_000; */ export const CLASS_BUDGET_BANDS = { // One CI shard command (may fan out across several packages via --filter). - // Floor is 15min, not 5min: the heaviest shard slices (@fusion/engine and - // @fusion/core, each split [1/2]+[2/2]) are import- and real-git-subprocess - // heavy, and the committed scripts/test-timings.json undercounts that - // overhead (most files bucket to the 100ms floor; the snapshot total is far - // below current wall-clock). A "fresh" but undercounting snapshot was - // tightening these slices to ~340-405s and SIGKILLing healthy runs on slower - // CI runners — the exact false-kill the band is meant to prevent (see the - // deriveBudgetMs note above and KTD-2). 15min sits comfortably above the - // observed CI need while still bounding a true hang far under the job's - // 60min ceiling. Mirrors the dashboard-lane heavy-lane floor. + /* + FNXC:TestInfrastructure 2026-06-20-21:51: + The shard watchdog floor is 15min, not 5min. The heaviest CI shard slices + (@fusion/engine and @fusion/core, each split [1/2]+[2/2]) are import- and + real-git-subprocess heavy, and the committed scripts/test-timings.json + undercounts that overhead (most files bucket to the 100ms floor, so the + snapshot total sits far below current wall-clock). A "fresh" but undercounting + snapshot was tightening these slices to ~340-405s and SIGKILLing healthy runs + on slower CI runners (engine[2/2]=145s, core[2/2]=283s locally, both pass) — + the exact false-kill the floor/ceiling band exists to prevent (see the + deriveBudgetMs note above and plan KTD-2). 15min sits above the observed CI + need while still bounding a true hang far under the job's 60min ceiling. + The dashboard-lane floor is also 15min but for separate historical reasons; + the two values are not coupled and may diverge. + */ shard: { floor: 15 * MINUTE, ceiling: 30 * MINUTE }, // One local changed-file package invocation. changed: { floor: 2 * MINUTE, ceiling: 20 * MINUTE },