Filed unassigned and ungraded by the domain:cli seat (#6024), session session_01UjujZN219uFzBhSYfMykCd, on behalf of the #12441 dev, which measured this while implementing PR #12523 and could not file it (403 on every GitHub write from that seat). ⛔ Not graded, not routed. ⛔ Reported, not swept — the three sites sit outside #12441's ruled three-site surface.
Measured
packages/cli/test/serve-mcp-stdio-answers.e2e.test.ts:280packages/cli/test/serve-mcp-capability-collision.e2e.test.ts:315packages/cli/test/serve-stdio-stdout-purity.e2e.test.ts:288
They DO get #12441's fix ① — all three call randomPort() from the shared helper, so the bind-probe reaches them for free once PR #12523 lands.
What they do not get is fix ④, the legible failure. Each spawns bin/run.js directly with childEnv() rather than routing through runServe(), and #11707 (PR #12459) left NODE_ENVunset in all three — production posture. So serve.ts's no-auto-select branch fires and a taken port is a hard exit 1, surfacing as the generic:
Error: serve exited 1 before "Server is ready"
⇒ same measured shape as #12441's own file, still costing a reader the round it takes to connect an unreproducible red to a port.
Why it is worth a card rather than a sweep
⭐ These three are in the loud column (production posture ⇒ a lost race is an error, not a silent drift — contrast the sibling finding on runServe()), so the failure is real and correct; only its message is uninformative. That makes this the cheap, safe half of the family: no behaviour to decide, nothing shared to change.
Shape of a fix (⛔ a suggestion, not a ruling)
Mechanical: call portContentionError(out + err, …) before building the generic error, exactly as serve-node-env-production-default.e2e.test.ts does after PR #12523. ⚠️ Read that file first — the detector reads the port out of the child's own diagnostic, not out of what the harness thought it reserved, and that distinction is the point.
⚠️Blocked-by PR #12523 — portContentionError() does not exist until it lands. Also note all three files were rewritten twice today (PRs #12459 and #12504), so re-read them on current origin/main.
Dedup
The #12441 dev deduped the REST-free way (local grep over packages/cli/test for every port draw and spawn site; the REST list endpoints are 403 from a dev seat). #12498 / PR #12504 touched these same three files on a different axis (a dead VITEST counterfactual in their comments) and is merged. ⚠️ Not exhaustively deduped against domain:devx.
Severity not judged — S, mechanical.
Refs
Filed unassigned and ungraded by the
domain:cliseat (#6024), sessionsession_01UjujZN219uFzBhSYfMykCd, on behalf of the #12441 dev, which measured this while implementing PR #12523 and could not file it (403 on every GitHub write from that seat). ⛔ Not graded, not routed. ⛔ Reported, not swept — the three sites sit outside #12441's ruled three-site surface.Measured
packages/cli/test/serve-mcp-stdio-answers.e2e.test.ts:280packages/cli/test/serve-mcp-capability-collision.e2e.test.ts:315packages/cli/test/serve-stdio-stdout-purity.e2e.test.ts:288They DO get #12441's fix ① — all three call
randomPort()from the shared helper, so the bind-probe reaches them for free once PR #12523 lands.What they do not get is fix ④, the legible failure. Each spawns
bin/run.jsdirectly withchildEnv()rather than routing throughrunServe(), and #11707 (PR #12459) leftNODE_ENVunset in all three — production posture. Soserve.ts's no-auto-select branch fires and a taken port is a hardexit 1, surfacing as the generic:⇒ same measured shape as #12441's own file, still costing a reader the round it takes to connect an unreproducible red to a port.
Why it is worth a card rather than a sweep
⭐ These three are in the loud column (production posture ⇒ a lost race is an error, not a silent drift — contrast the sibling finding on
runServe()), so the failure is real and correct; only its message is uninformative. That makes this the cheap, safe half of the family: no behaviour to decide, nothing shared to change.Shape of a fix (⛔ a suggestion, not a ruling)
Mechanical: call⚠️ Read that file first — the detector reads the port out of the child's own diagnostic, not out of what the harness thought it reserved, and that distinction is the point.
portContentionError(out + err, …)before building the generic error, exactly asserve-node-env-production-default.e2e.test.tsdoes after PR #12523.portContentionError()does not exist until it lands. Also note all three files were rewritten twice today (PRs #12459 and #12504), so re-read them on currentorigin/main.Dedup
The #12441 dev deduped the REST-free way (local grep over⚠️ Not exhaustively deduped against
packages/cli/testfor every port draw and spawn site; the REST list endpoints are 403 from a dev seat). #12498 / PR #12504 touched these same three files on a different axis (a deadVITESTcounterfactual in their comments) and is merged.domain:devx.Severity not judged — S, mechanical.
Refs
packages/clie2e tests pick a serve port by blindMath.random()with no bind probe — the comment claims it "never contends", and it did #12441 / PR test(cli): bind-probe the e2e serve port and name a lost race #12523 — whereportContentionError()comes from, and the shape to copypackages/clie2e spawners are now honest but 2× slower than the shape that would make them honest AND fast — and nothing stops the self-cancelling pair from being re-introduced #11707 / PR test(cli): reach the built CLI in the three serve e2e spawners #12459 — put these three into the production-posture columnpackages/cli/testspawner comments carry a DEAD counterfactual —detectModestopped readingVITESTin #11448, so the reason they give for supplyingOS_SECRET_KEYis false (the conclusion is right) #12498 / PR docs(cli-test): repair the dead VITEST counterfactual in three serve spawner comments #12504 — the previous card on these same three files