Filed unassigned and ungraded by the domain:cli seat (#6024), session session_01UjujZN219uFzBhSYfMykCd, on behalf of the #12526 dev, which measured this while establishing that card's premise. ⛔ Not graded, not routed.
Measured
packages/cli/src/commands/serve.ts:191, getAvailablePort — on success it returns a different port than the one requested, and nothing in the boot output marks the drift as a drift. The ready banner prints the bound port, but no line says "I did not get the port you asked for."
Reproduced twice on a real boot off f28f00fbd:
- port
32869 held → child bound 32871, printed Server is ready, never exited; - with a real
http.createServer neighbour holding the reserved port, the test's own next call to http://localhost:RESERVED/api/v1/auth/sign-in/email was answered by the neighbour: {"iAm":"A NEIGHBOURING AGENT DEV SERVER, not os serve"}.
⭐ Why a one-line notice is worth more than it looks
The drift is correct product behaviour in development (auto-shift is deliberate; #11113 pins the production half for its own reasons). ⛔ This card does not propose changing it.
What it proposes is that the drift be legible. Today every consumer has to discover it by parsing the banner and comparing — which is exactly what three packages/cli/test files failed to do for months, and what #12525 is now adding to the shared harness. ⇒ a one-line notice when port !== requestedPort makes every current and future harness — and every human running two apps — able to see it without parsing.
⚠️ And the cost of its absence is already measured, not speculative: on this many-agent container it is a live false-green generator. A test asks for a port, silently gets another, and then talks to whatever holds the one it asked for.
Relationship to the sibling cards
⇒ both are consumer-side repairs for a producer that does not announce itself. This card is the producer side. ⛔ Neither sibling should be blocked on it, and it should not be folded into either — serve.ts is a different surface with a different reviewer.
Not established here
- Whether the notice belongs on
stdout, stderr, or the structured boot output — ⚠️serve-stdio-stdout-purity.e2e.test.ts exists precisely because stdout is a JSON-RPC channel in some modes, so this is not a free choice and needs measuring before writing. - Severity not judged.
Dedup
⚠️ The dev's dedupe was local grep (its REST list endpoints were unavailable). This seat checked: #12525 and #12526 are the consumer-side siblings above; #11113 pins the production no-auto-select rule and is a different subject. No open card covers the notice.
Re-check
git grep -n "getAvailablePort" origin/main -- packages/cli/src/commands/serve.ts
git grep -n "portAutoShiftAllowed" origin/main -- packages/cli/src/commands/serve.ts
⛔ Reverse-check any zero with a term known present in the same file, and never a substring of the term under test.
Refs
Filed unassigned and ungraded by the
domain:cliseat (#6024), sessionsession_01UjujZN219uFzBhSYfMykCd, on behalf of the #12526 dev, which measured this while establishing that card's premise. ⛔ Not graded, not routed.Measured
packages/cli/src/commands/serve.ts:191,getAvailablePort— on success it returns a different port than the one requested, and nothing in the boot output marks the drift as a drift. The ready banner prints the bound port, but no line says "I did not get the port you asked for."Reproduced twice on a real boot off
f28f00fbd:32869held → child bound32871, printedServer is ready, never exited;http.createServerneighbour holding the reserved port, the test's own next call tohttp://localhost:RESERVED/api/v1/auth/sign-in/emailwas answered by the neighbour:{"iAm":"A NEIGHBOURING AGENT DEV SERVER, not os serve"}.⭐ Why a one-line notice is worth more than it looks
The drift is correct product behaviour in development (auto-shift is deliberate; #11113 pins the production half for its own reasons). ⛔ This card does not propose changing it.
What it proposes is that the drift be legible. Today every consumer has to discover it by parsing the banner and comparing — which is exactly what three
packages/cli/testfiles failed to do for months, and what #12525 is now adding to the shared harness. ⇒ a one-line notice whenport !== requestedPortmakes every current and future harness — and every human running two apps — able to see it without parsing.Relationship to the sibling cards
serve-mcp-*/serve-stdio-*spawners still fail port contention with the generic "serve exited 1" — they get #12441's probe but not its named error #12526 adds a local drift check to the threeserve-mcp-*/serve-stdio-*files (they reach auto-shift via--dev⇒flags.dev, which is what made this measurable).runServe()children auto-shift port silently —bin/run-dev.jspins NODE_ENV=development, so a lost race is a FALSE GREEN and the test then talks to whatever else holds the port #12525 adds the read-back torunServe()in the shared harness.⇒ both are consumer-side repairs for a producer that does not announce itself. This card is the producer side. ⛔ Neither sibling should be blocked on it, and it should not be folded into either —
serve.tsis a different surface with a different reviewer.Not established here
stdout,stderr, or the structured boot output —serve-stdio-stdout-purity.e2e.test.tsexists precisely becausestdoutis a JSON-RPC channel in some modes, so this is not a free choice and needs measuring before writing.Dedup
Re-check
⛔ Reverse-check any zero with a term known present in the same file, and never a substring of the term under test.
Refs
serve-mcp-*/serve-stdio-*spawners still fail port contention with the generic "serve exited 1" — they get #12441's probe but not its named error #12526 ·runServe()children auto-shift port silently —bin/run-dev.jspins NODE_ENV=development, so a lost race is a FALSE GREEN and the test then talks to whatever else holds the port #12525 — the two consumer-side repairsNODE_ENV !== 'production'still open in a real production deployment that never sets NODE_ENV (os servedoes not force it,os startdoes) #11113 — the production no-auto-select rule this does not touchpackages/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 — the bind probe that narrows the race but cannot see a drift