Filed unassigned by the dev seat working #9399 (dev-server orphan / flock leak). Not fixed there — different defect class (collision, not lifecycle), and that card's file surface was scoped to the cleanup path. Recording it because it was hit while measuring, not from a code read.
The shape
scripts/gen-sdui-manifest.sh uses two fixed, unqualified names for per-run state:
- the dev server port,
5180, used both to start the server and as BASE_URL for the dump - the log path,
/tmp/sdui-dump-dev.log
Agent dispatch containers run several agents against one filesystem and one network namespace, so both names are shared across concurrent runs. Two consequences, of different severity:
Log clobber (benign, noisy). The second run truncates the first run's /tmp/sdui-dump-dev.log. The failure branch of the script points a reader at that log, so a diagnosing agent can be reading another run's output.
Possible silently-wrong dump (this is the one worth grading). The script does not pass --strictPort. Vite's documented default is to increment to the next free port when the requested one is taken, rather than failing. If that holds for the pinned vite, then when run B starts while run A's server owns 5180, run B's server comes up on 5181 while run B's wait loop and BASE_URL still point at 5180 — so run B curls A's server, succeeds, and dumps A's manifest as its own. The output is a real manifest, the exit code is 0, and nothing in the run says which tree it describes. That result then feeds the ADR-0082 declaration-parity ratchet.
What is verified vs. what is not
- Verified: the port and log path are literals with no per-run qualifier; no
--strictPort is passed; BASE_URL is derived from the same literal the wait loop probes. - Not verified: that the pinned vite actually auto-increments rather than failing to bind. That is vite's documented default, but it was not exercised here — the objectui build tree (
.cache/objectui-*) is absent from a fresh worktree, so no real vite was started. Confirm this before grading severity: if vite refuses to start instead, this collapses to consequence (1) plus a confusing 90-second wait, which is much cheaper.
Note for whoever measures it: ss and netstat are not installed in these containers, so a socket table cannot be used to check which port is bound — use curl or lsof (docs/qa/platform-checklist/RUNNER.md).
Suggested shape (not a decision)
Pick a free port per run and thread it through both uses, or pass --strictPort so a collision fails loudly instead of resolving to a neighbour's server; qualify the log path per run. The --strictPort half is the cheap one and is independently useful — it converts a silent wrong answer into a visible failure even if per-run ports are not adopted.
Filed unassigned by the dev seat working #9399 (dev-server orphan / flock leak). Not fixed there — different defect class (collision, not lifecycle), and that card's file surface was scoped to the cleanup path. Recording it because it was hit while measuring, not from a code read.
The shape
scripts/gen-sdui-manifest.shuses two fixed, unqualified names for per-run state:5180, used both to start the server and asBASE_URLfor the dump/tmp/sdui-dump-dev.logAgent dispatch containers run several agents against one filesystem and one network namespace, so both names are shared across concurrent runs. Two consequences, of different severity:
Log clobber (benign, noisy). The second run truncates the first run's
/tmp/sdui-dump-dev.log. The failure branch of the script points a reader at that log, so a diagnosing agent can be reading another run's output.Possible silently-wrong dump (this is the one worth grading). The script does not pass
--strictPort. Vite's documented default is to increment to the next free port when the requested one is taken, rather than failing. If that holds for the pinned vite, then when run B starts while run A's server owns 5180, run B's server comes up on 5181 while run B's wait loop andBASE_URLstill point at 5180 — so run B curls A's server, succeeds, and dumps A's manifest as its own. The output is a real manifest, the exit code is 0, and nothing in the run says which tree it describes. That result then feeds the ADR-0082 declaration-parity ratchet.What is verified vs. what is not
--strictPortis passed;BASE_URLis derived from the same literal the wait loop probes..cache/objectui-*) is absent from a fresh worktree, so no real vite was started. Confirm this before grading severity: if vite refuses to start instead, this collapses to consequence (1) plus a confusing 90-second wait, which is much cheaper.Note for whoever measures it:
ssandnetstatare not installed in these containers, so a socket table cannot be used to check which port is bound — usecurlorlsof(docs/qa/platform-checklist/RUNNER.md).Suggested shape (not a decision)
Pick a free port per run and thread it through both uses, or pass
--strictPortso a collision fails loudly instead of resolving to a neighbour's server; qualify the log path per run. The--strictPorthalf is the cheap one and is independently useful — it converts a silent wrong answer into a visible failure even if per-run ports are not adopted.