Skip to content

gen-sdui-manifest.sh hardcodes port 5180 and one log path, so two concurrent runs in a container can dump the OTHER run's server #9578

Description

@os-steve

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:

  1. 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.

  2. 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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions