diff --git a/scripts/gate-lane-exclusions.json b/scripts/gate-lane-exclusions.json index 50fd3c75a..135a98084 100644 --- a/scripts/gate-lane-exclusions.json +++ b/scripts/gate-lane-exclusions.json @@ -9,13 +9,6 @@ "receipt": "A: base 562192e2c green 3/3 in two envs (dev50-planner 01:00Z, orch-advisor primary 01:01Z) + dev-driver uncontended green at 3324f6811. B: tip b8d446069 single-file green 3/3 (01:29Z gate worktree) vs gate-suite red under load (verdict foreignLoad recorded).", "owner": "qitem-20260721000001-ps-stall-driver", "expiry": "2026-08-28" - }, - { - "suite": "vitest:ui", - "reason": "Load-margin UI-leg reds under gate/wave contention, leg-covered. SPECIMEN 1 (p5-mobile, slice-11): FIXED by B13 2026-08-21 - struck from this reason, passes in-suite; kept in history via the verdict artifacts. SPECIMEN 2 (opr-0.4.1.13-table-crash STRESS): passes uncontended with 7-8x headroom on BOTH v22.22.1 (760ms) and v26.5.0 (614ms) vs 5s budget - r1 ran the finder's own runtime to kill the runtime hypothesis; red only under loaded parallel suite. Same class, same receipts shape. Resident dies at expiry or when specimen 2 is fixed.", - "receipt": "A: gate run 1 at c5635c229 UI leg green 1723-pass (same fixture, lighter load). B: tip b8d446069 single-file green 9/9 under jsdom config (01:30Z) vs gate-suite red 2-fail under load. Historic: slice-11 row records pass-alone at 2339/2335ms base/candidate. AMENDED 2026-08-21 02:4x: specimen-2 A/B - uncontended green 760ms/614ms both runtimes (r1) vs loaded-suite red; specimen-1 struck (B13 fix, r1 CLEAR c2f0cc7b + door test).", - "owner": "opr-0.4.1.13-table-crash STRESS (load-margin family; slice-11 fix landed, B13)", - "expiry": "2026-08-28" } ] } \ No newline at end of file diff --git a/scripts/gate-lane-run.mjs b/scripts/gate-lane-run.mjs index 1414b4f8c..eae188a69 100644 --- a/scripts/gate-lane-run.mjs +++ b/scripts/gate-lane-run.mjs @@ -41,15 +41,18 @@ export function renderRefusal(result, port) { } /** - * HONESTY gaps closed here: the gate runs ALL THREE legs — typecheck (lint) AND vitest (test, the - * workspaces) AND vitest:ui (test:ui — which `npm test` EXCLUDES). All legs run (not fail-fast) so the - * verdict records every result; green = every leg ok. `exec(cmd)` is injected → `{ok, code}`. + * The gate runs BOTH legs — typecheck (lint) AND vitest (test: repo scripts + the supported + * workspaces: daemon, cli, tui). All legs run (not fail-fast) so the verdict records every result; + * green = every leg ok. `exec(cmd)` is injected → `{ok, code}`. + * + * The web UI (packages/ui) is deliberately NOT a gate leg. Founder ruling 2026-08-21: the web UI + * went best-effort experimental at the 0.5.0 TUI pivot and releases must not spend gate time or + * block on it. `npm run test:ui` still exists for manual runs; it gates nothing. */ export async function runLegs(exec) { const specs = [ - { name: "typecheck", cmd: "npm run lint" }, // gap-2 leg A: tsc ×4 (incl. P9 typecheck:prep) - { name: "vitest", cmd: "npm run test" }, // gap-2 leg B: test:repo + workspaces - { name: "vitest:ui", cmd: "npm run test:ui" }, // gap-1: the leg npm test omits + { name: "typecheck", cmd: "npm run lint" }, // tsc ×4 (incl. P9 typecheck:prep) + { name: "vitest", cmd: "npm run test" }, // test:repo + supported workspaces (no packages/ui) ]; const legs = []; for (const s of specs) { diff --git a/scripts/gate-lane-run.test.mjs b/scripts/gate-lane-run.test.mjs index ff8f7de08..21898fdce 100644 --- a/scripts/gate-lane-run.test.mjs +++ b/scripts/gate-lane-run.test.mjs @@ -5,9 +5,9 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { renderRefusal, runLegs, buildVerdict, runGate, observeForeignLoad, cleanStaleVendoredBundle } from "./gate-lane-run.mjs"; -// F1 gate-lane runner logic (arch d6a6c1db, 5 pins). HONESTY (gap 1): the gate runs test:ui (npm test -// excludes it); (gap 2) green = typecheck AND vitest BOTH legs. P5: refusal teaches the port constant, -// names the gate holder (pid/started-at) or honest-unknown for a foreign squatter, always hard-refuse. +// F1 gate-lane runner logic (arch d6a6c1db, 5 pins). Legs: typecheck AND vitest (repo + supported +// workspaces). The web-UI leg is ruled OUT (founder, 2026-08-21) — see the absence test below. +// P5: refusal teaches the port constant, names the gate holder or honest-unknown, always hard-refuse. test("P5 refusal — gate holder is NAMED (pid/started-at) + teaches the port constant, always refuses", () => { const t = renderRefusal({ reason: "gate-holder", holder: { pid: 4242, startedAt: "2026-08-07T09:00:00Z" } }, 40404); @@ -25,23 +25,26 @@ test("P5 refusal — foreign squatter is HONEST-UNKNOWN + still teaches the port assert.match(t, /refus/i); }); -test("HONESTY gap-1: runLegs runs ALL THREE legs incl. test:ui (npm test excludes ui)", async () => { +test("runLegs runs the two supported legs — and the web-UI leg is ruled OUT, never present", async () => { const ran = []; const exec = async (cmd) => { ran.push(cmd); return { ok: true, code: 0 }; }; const legs = await runLegs(exec); const names = legs.map((l) => l.name); assert.ok(names.includes("typecheck"), "typecheck leg"); - assert.ok(names.includes("vitest"), "vitest leg (workspaces)"); - assert.ok(names.includes("vitest:ui"), "vitest:ui leg — the excluded-from-npm-test honesty gap"); + assert.ok(names.includes("vitest"), "vitest leg (repo + supported workspaces)"); + // Founder ruling 2026-08-21: the web UI is best-effort experimental since the 0.5.0 TUI pivot — + // the gate must not run or block on packages/ui. This absence is deliberate; do not re-add. + assert.ok(!names.includes("vitest:ui"), "no web-UI leg — ruled out, not forgotten"); + assert.ok(ran.every((c) => !/test:ui/.test(c)), "gate never invokes test:ui"); assert.ok(legs.every((l) => l.ok)); }); -test("HONESTY gap-2: green = typecheck AND vitest BOTH — one failed leg fails the gate", async () => { - const exec = async (cmd) => ({ ok: !/ui/.test(cmd), code: /ui/.test(cmd) ? 1 : 0 }); // ui fails +test("green = typecheck AND vitest BOTH — one failed leg fails the gate", async () => { + const exec = async (cmd) => ({ ok: !/lint/.test(cmd), code: /lint/.test(cmd) ? 1 : 0 }); // typecheck fails const legs = await runLegs(exec); const v = buildVerdict({ legs, foreignLoad: { advisory: [] }, startedAt: "t0", endedAt: "t1" }); assert.equal(v.gate, "fail"); - assert.equal(v.legs.find((l) => l.name === "vitest:ui").ok, false); + assert.equal(v.legs.find((l) => l.name === "typecheck").ok, false); }); test("advisory foreign-load — counts foreign node/vitest/tsc processes + loadavg (never the gate's own pid)", () => { @@ -130,7 +133,7 @@ test("SELF-DESCRIBING (pm GATE CONDITION): verdict.smoke tracks WHAT RAN via run // smoke:true fails). Both directions + both effect checks together forbid a constant that lies. const real = await gateWith(false); assert.equal(real.verdict.smoke, false, "the real run seals smoke:false"); - assert.equal(real.ran.length, 3, "smoke:false coincides with all 3 legs routed through realExec (the effect)"); + assert.equal(real.ran.length, 2, "smoke:false coincides with both legs routed through realExec (the effect)"); // FAIL-SAFE — buildVerdict without a smoke arg defaults false, never a silent true. assert.equal(buildVerdict({ legs: [], foreignLoad: { advisory: [] }, startedAt: "t0", endedAt: "t1" }).smoke, false); }); diff --git a/scripts/gate-lane.mjs b/scripts/gate-lane.mjs index 2c1a90da8..9e7143dcd 100644 --- a/scripts/gate-lane.mjs +++ b/scripts/gate-lane.mjs @@ -2,7 +2,7 @@ // F1 gate-lane runner ENTRYPOINT (arch d6a6c1db, mechanism (B) bound-port, 5 pins). Wires the machine-wide // mutex (gate-lane-lock) + the honesty-gap legs + the C2 verdict (gate-lane-run) into one gate: // acquire lane (non-blocking) → refuse+teach+exit-nonzero on contention (P5) → observe foreign load -// (advisory, recorded) → run BOTH legs (typecheck AND vitest AND vitest:ui — the two honesty gaps) → +// (advisory, recorded) → run BOTH legs (typecheck AND vitest) → // write the C2 verdict (green carries the load it ran under) → release (kernel also frees on death). import { spawnSync, execSync } from "node:child_process"; import { writeFileSync, readFileSync, mkdirSync, realpathSync, unlinkSync, lstatSync } from "node:fs";