Split out of #12850 by the triage seat (session session_01Aujz2zykf5LXt3T98gRsGe) at that card's own request — it recorded two mechanizable items from the #12772 patch rounds (PR #12843) and left "fold in or split" to the grader. Split, for the reason given on #12850: the two have different failure shapes, and folding them would let each hide the other.
The item, as recorded on #12850
the sibling divergence class from the same PR (CI's ~4GB tsc heap ceiling vs the container default making check:type-check-debt --re-measure OOM only on CI) was closed within the claim by running the re-measure under NODE_OPTIONS=--max-old-space-size=4096; whether that belongs in the gate's own harness (so every local run is CI-shaped) is a second, smaller mechanizable item.
Why it is worth its own card
The gate runs locally and passes, then OOMs on CI. That is not the #12850 shape (a gate never named in the derived set, so never run at all) — it is a gate that was named, was run, and answered from a differently-shaped machine than the one whose verdict counts.
⇒ A dev who does everything right — reads the dispatch prompt, runs every named gate, sees green — still ships a red CI. The gate's local green is not a claim about CI, and nothing says so.
This is the same family this repo keeps repairing on the measurement side: #12528 already ruled that wall-clock readings taken in the agent container are of a contended box and are not the cost on a CI runner. That ruling is about timings; this is the memory-ceiling face of the same "the container is not the runner" problem, and unlike timings it has a cheap mechanical fix.
Shape of the fix — constraints, not a design
Re-check
node scripts/pm/dispatch-gates.mjs --self-test | tail -1
# and the gate itself, with and without an explicit ceiling:
pnpm check:type-check-debt --re-measure
NODE_OPTIONS=--max-old-space-size=4096 pnpm check:type-check-debt --re-measure
⚠️ Instrument note: a local run that passes proves nothing about CI here — that asymmetry is the defect. Confirm against a real CI run of the step.
Provenance
#12850 (the sibling item, kept separate) · #12843 (the PR that hit both) · #12772 (the p0 the patch rounds served) · #12528 (closed — the container-is-not-the-runner ruling, timings face).
Split out of #12850 by the triage seat (session
session_01Aujz2zykf5LXt3T98gRsGe) at that card's own request — it recorded two mechanizable items from the #12772 patch rounds (PR #12843) and left "fold in or split" to the grader. Split, for the reason given on #12850: the two have different failure shapes, and folding them would let each hide the other.The item, as recorded on #12850
Why it is worth its own card
The gate runs locally and passes, then OOMs on CI. That is not the #12850 shape (a gate never named in the derived set, so never run at all) — it is a gate that was named, was run, and answered from a differently-shaped machine than the one whose verdict counts.
⇒ A dev who does everything right — reads the dispatch prompt, runs every named gate, sees green — still ships a red CI. The gate's local green is not a claim about CI, and nothing says so.
This is the same family this repo keeps repairing on the measurement side: #12528 already ruled that wall-clock readings taken in the agent container are of a contended box and are not the cost on a CI runner. That ruling is about timings; this is the memory-ceiling face of the same "the container is not the runner" problem, and unlike timings it has a cheap mechanical fix.
Shape of the fix — constraints, not a design
--re-measureis CI-shaped wherever it runs, rather than leavingNODE_OPTIONS=--max-old-space-size=4096as something each caller must remember. A workaround that lives in one PR's shell history is not a fix.~4GBis what the feat(metadata): versioned ADR-0087 forward conversion at the artifact-ingestion door #12843 rounds used and it worked, but the authority for the value is the CI runner's actual ceiling — ⛔ do not enshrine a number measured on a contended agent box ([finding]os-verify-lockserialises locked heavy jobs but NOT gate scripts — so every timing card measures a contended box while believing the lock gave it an idle one #12528 is exactly that lesson).check:dispatcher-error-vocabularyor path derivation — that is dispatch-gates:check:dispatcher-error-vocabularyis invisible to path derivation — a new error/notice code literal ships a green local union and a red CI (measured on the #12843 patch rounds) #12850.Re-check
Provenance
#12850 (the sibling item, kept separate) · #12843 (the PR that hit both) · #12772 (the p0 the patch rounds served) · #12528 (closed — the container-is-not-the-runner ruling, timings face).