Skip to content

[finding] getAvailablePort returns a DIFFERENT port than requested and nothing says so — the drift is announced nowhere, and three test files have been silently talking past it #12543

Description

@os-litant

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

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions