Skip to content

[finding] publish-smoke-port-collision's harness has no trailing exit 0, so a cleanup kill of a dead stub reports as Command failed instead of the assertion that would name the problem #10671

Description

@os-elon

Filed unassigned from work on #10457 (PR for that card cites this). ⛔ Not observed failing in CI — measured deliberately, as a by-product of running that card's ablation. Recorded as a latent instance of the reporting half of #10370.

Duplicate search first (publish-smoke-port-collision, publish-smoke + harness + exit status, exit 0 + harness + errexit + kill): no open card carries this. #10457 is the sibling card and explicitly scopes this out (see below). #10261 is pm:on-hold and a different subject.

The class, and why #10457 did not cover it

#10370 had two halves. The reporting half was set +e after the sourceplus a trailing exit 0, and PR #10456 landed both for gen-sdui-manifest-collision.test.ts.

#10457's "Not in this finding" section rules the reporting half out for publish-smoke-port-collision.test.ts on the grounds that the file already does set +e +o pipefail right after sourcing publish-smoke.sh. That is true and the card is right about it. But it is only the first half of the reporting fix — the file has no trailing exit 0:

$ grep -n "exit 0" packages/spec/scripts/publish-smoke-port-collision.test.ts
$ # no match

So runHarness's execFileSync still grades the harness by whatever the last command returned, and every case in the file ends with a cleanup kill:

'kill "$HOLDER" 2>/dev/null',
'kill "$NEIGHBOUR" "$SERVER_PID" 2>/dev/null',
'kill "$THIEF" 2>/dev/null',

Reaping a stub that has already exited returns 1. set +e stops that from aborting the harness mid-line — which is what the card's cited line buys — but it does not stop it from becoming the harness's exit status. execFileSync then throws in the test body and the failure arrives as a bare Command failed, pre-empting the vacuity guard that was written to name the problem.

Measured

While running #10457's ablation (occupier binds, closes, then reports a port it does not hold), the two shapes separated cleanly on the same rig, same file, same mutation except for whether the stub process stays alive:

stub after losing the portreported as
exits (so cleanup kill returns 1)Error: Command failed: bash /tmp/publish-smoke-collision-ZrEMXc/harness.sh at runHarness, the assertion never reached
stays alive (so cleanup kill returns 0)AssertionError: expected '' to be 'THIEF' at the STEAL_HELD guard

The second is the report the file's guards were written to produce. The first is #10370's captured shape — the one that cost that card a round of misdiagnosis.

Why it is worth fixing even though #10457's own case no longer trips it

After #10457 the THIEF stub binds :0 and cannot realistically lose its bind, so that case's stub stays alive and the exposure is gone there. The other four cases in the file still bind ports handed to them by smoke_pick_free_port (HOLDER, NEIGHBOUR, OURS), and a stub that loses that bind dies on an unhandled EADDRINUSE — which is exactly the situation in which you most need the vacuity guard's wording rather than Command failed.

Shape of a fix — a lead, not a decision

The one PR #10456 already landed next door: a final 'exit 0' at the end of the harness body in runHarness, with the reasoning that the harness's exit status is not a measurement — every measurement is a printed KEY=VALUE line and the assertions grade those. One line, in the same file, same gate families.

Worth considering alongside it (separate judgement, not implied): the four fixed-port stubs use HTTP_STUB, which has no s.once("error", …) guard, so losing a bind kills them with an unhandled 'error' event rather than a clean exit. OWNED_HTTP_STUB, added by #10457, has that guard.

⚠️ Whoever takes this: publish-smoke probes the wildcard address, matching serve.ts's own isPortAvailable, while sdui probes 127.0.0.1. That asymmetry is load-bearing and is recorded on #10261. ⛔ Do not converge them as part of this.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions