Summary
scripts/lib/render-smoke.test.mjs's case "a paint landing just under the deadline is not failed by the render timer" (added by #2147) leaves too little slack for process startup, so it fails intermittently under load — and because test:scripts is the 4th step in validate's && chain, a flake there aborts npm run local:gate before it reaches any client. Each occurrence costs a full re-run of the ~20-minute gate.
The margin
const paintAt = 250;
const r = await runRenderSmoke({
...stub(`setTimeout(() => { console.log(MARKER); … }, ${paintAt});`),
timeoutMs: paintAt + 150, // 400ms
surviveMs: 400,
});
The stub is a real node -e child that paints 250ms after its own start, while the 400ms deadline runs from spawn. So the whole budget for spawn + Node boot + module init is 150ms. Node's cold start alone is routinely near or past that on a loaded machine.
Observed
Three failures in one session on an otherwise-idle-ish dev machine, two of them back to back:
✖ a paint landing just under the deadline is not failed by the render timer (416.44ms)
AssertionError: expected pass, got: child did not render "MCP Servers" within 400ms
✖ … (407.93ms) # same, next run
Both times it passes 3/3 immediately afterwards when run in isolation (node --test scripts/lib/render-smoke.test.mjs), which is what identifies it as a margin problem rather than a defect in runRenderSmoke.
Why not just raise the number
The tightness is deliberate and load-bearing — the comment says so: the render deadline has to expire while the survival window is still open, because that overlap is the bug being pinned. So the fix is not "make timeoutMs bigger"; it has to keep timeoutMs < surviveMs while stopping the child's startup from eating the render budget. Options worth weighing:
- Have the stub signal readiness and start its paint timer from a point the harness observes, so the measured interval excludes Node boot.
- Scale both numbers off a measured spawn baseline taken once per run.
- Inject a clock so the case is deterministic rather than wall-clock timed.
Acceptance
Found while running npm run local:gate for #2171.
Summary
scripts/lib/render-smoke.test.mjs's case "a paint landing just under the deadline is not failed by the render timer" (added by #2147) leaves too little slack for process startup, so it fails intermittently under load — and becausetest:scriptsis the 4th step invalidate's&&chain, a flake there abortsnpm run local:gatebefore it reaches any client. Each occurrence costs a full re-run of the ~20-minute gate.The margin
The stub is a real
node -echild that paints 250ms after its own start, while the 400ms deadline runs from spawn. So the whole budget forspawn+ Node boot + module init is 150ms. Node's cold start alone is routinely near or past that on a loaded machine.Observed
Three failures in one session on an otherwise-idle-ish dev machine, two of them back to back:
Both times it passes 3/3 immediately afterwards when run in isolation (
node --test scripts/lib/render-smoke.test.mjs), which is what identifies it as a margin problem rather than a defect inrunRenderSmoke.Why not just raise the number
The tightness is deliberate and load-bearing — the comment says so: the render deadline has to expire while the survival window is still open, because that overlap is the bug being pinned. So the fix is not "make
timeoutMsbigger"; it has to keeptimeoutMs < surviveMswhile stopping the child's startup from eating the render budget. Options worth weighing:Acceptance
forbidOutput, the exit-before-paint case, the survival assertions).Found while running
npm run local:gatefor #2171.