Skip to content

[finding] gen-sdui-manifest-collision's harness cannot guarantee its own BUSY precondition — it binds the fixed base port 5180, so a concurrent sdui run reds an unrelated PR #10370

Closed

Description

@os-zhuang

Filed unassigned by the domain:devx PM seat (#6023), session session_01DdCnBGcHeufjrq7drTD3wt, from a CI failure on PR #10365 (#9870) — a PR whose diff is a gate script, a README and a changeset, and which touches nothing in this area.

Duplicate search first (gen-sdui-manifest-collision, BUSY_HELD, port/registry wording): no open card carries this symptom. #10167 (the TOCTOU picker) and #9578 (the hardcoded port) are both closed and genuinely fixed; #10261 (registry convergence) is pm:on-hold and is a different subject.

⭐ The picker is not at fault. The guard is working. The harness is the gap.

This matters because the obvious reading is wrong, and the test's own header warns against exactly that misreading.

Observed on Test Core (1/3), run 32394885598, head 4ce4cc39:

Error: Command failed: bash /tmp/sdui-collision-6Yonmo/harness.sh
 ❯ runHarness scripts/gen-sdui-manifest-collision.test.ts:260:15

DEV_ARGV=pnpm --filter @object-ui/console exec vite dev --port 4321 --strictPort
BUSY_HELD=no
BUSY_PORT=5180
PICKED_WITH_BUSY=5181

Read those last three lines together: PICKED_WITH_BUSY=5181 is the picker doing the right thing. It was asked to avoid 5180 and it returned 5181. What failed is BUSY_HELD=no — the harness's own occupier could not bind 5180, so the precondition for interpreting PICKED_WITH_BUSY was never established.

The test refuses to grade on that, deliberately, at :285:

expect(seen.BUSY_HELD, JSON.stringify(seen)).toBe('yes');

and its header says why (:68-69):

failure read as "the picker does not skip a busy port". It was not that. So BUSY_HELD is asserted before PICKED_WITH_BUSY is believed: a precondition

⇒ The vacuity guard fired correctly and converted a vacuous pass into an honest red. Without it this run would have gone green — PICKED_WITH_BUSY ≠ BUSY_PORT — while proving nothing. ⛔ Nothing here argues for weakening or removing that assertion; it is the reason this is diagnosable at all.

The actual gap

The harness picks its BUSY port as the fixed base, 5180, and binds it directly. It has no way to guarantee it wins that bind. Meanwhile sdui_pick_free_port — since #10217 — claims ports through an O_EXCL registry at ${SDUI_PORT_RESERVATION_DIR:-${TMPDIR:-/tmp}/sdui-port-reservations} and scans upward from the same base.

So on a runner where any concurrent sdui activity holds 5180, the harness loses its bind, BUSY_HELD=no, and an unrelated PR goes red. The registry made the picker concurrency-safe; the test that verifies the picker was left binding a fixed port by hand.

⚠️ Stated as the leading hypothesis, not as established: I have not reproduced this, and I have not identified what held 5180 on that runner. What is established is that BUSY_HELD=no means the harness lost the bind, and that the picker's own answer was correct on the same run.

Why it is worth a card rather than a re-run

The cost is borne by whoever is unlucky: a green PR is reddened, and on the merge queue that is a full-queue rebuild for everyone behind it. #10167's card measured that cost for the previous incarnation of this same file.

There is also a sharper reason. The repo just spent two cards (#10167, #10212) making port acquisition claim-before-probe in the scripts. This is the same defect one layer out, in the harness that tests them — and it is invisible to those fixes by construction, because the harness deliberately does not use the picker it is testing.

Shape of a fix — a lead, not a decision

The harness needs a BUSY port it can be sure of. Candidates, unmeasured:

  1. Bind first, then derive. Let the occupier bind an ephemeral port (:0), read back what it got, and pass that as the base — so the port it holds is one it provably owns. Changes what "busy" means from "a fixed number" to "the one we hold", which is the property the assertion actually needs.
  2. Reserve through the registry, so the harness participates in the same protocol as the picker. ⚠️ Weakens the test in one direction: case 7 (a port held from OUTSIDE the registry is skipped) exists precisely to cover the non-participant, so this must not replace that leg.
  3. Retry the bind on a different port until held, capped, and fail loudly if it never wins.

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

Refs: #10167 / PR #10217 (the TOCTOU picker fix and the reservation registry) · #10212 / PR #10263 (the same fix for publish-smoke.sh) · #10261 (the two registries, on hold) · #9578 (the original hardcoded-port card).

Activity

  1. os-zhuang commented on Aug 20, 2026

    @os-zhuang
    ContributorAuthor

    ⭐ SECOND occurrence, different PR, byte-identical output — this is now a live queue-eviction cause, not a latent finding

    domain:devx PM seat. This card was filed at 17:06Z from PR #10365 and stated its mechanism as "the leading hypothesis, not as established — I have not reproduced this." It has now happened again, and the corroboration is about as tight as a second natural occurrence can be.

    PR #10396 (#9748 — a durability log-level fix) was dequeued from the merge queue at 19:11Z, reason: CI_FAILURE, run 32406085715, job Test Core (1/6). Its harness output:

    BUSY_HELD=no
    BUSY_PORT=5180
    PICKED_WITH_BUSY=5181
    

    Set against this card's original capture from PR #10365 (run 32394885598, Test Core (1/3)):

    BUSY_HELD=no
    BUSY_PORT=5180
    PICKED_WITH_BUSY=5181
    

    Identical, line for line. Different PR, different run, different shard count, ~2 hours apart. PICKED_WITH_BUSY=5181 is again the picker answering correctly — asked to avoid 5180, it returned 5181 — and again the failure is BUSY_HELD=no: the harness's own occupier lost its bind on the fixed base port. Everything this card says about the picker being blameless and the guard working holds on the second instance too.

    What the second instance adds

    ⛔ Still not reproduced, and still nobody has identified what holds 5180

    Two natural occurrences with matching output corroborate the mechanism; they do not substitute for a repro, and I have not identified the concurrent process holding 5180 on either runner. That remains the open question a fix has to answer or design around. The three fix shapes in the body are unchanged and unmeasured — shape 1 (bind an ephemeral :0, read back what you got, use that as the base) still looks strongest, because it makes the occupier's port one it provably owns, which is the property the BUSY_HELD assertion actually needs.

    ⛔ And, restating the card's own warning because a second red makes it tempting: nothing here argues for weakening or deleting expect(seen.BUSY_HELD).toBe('yes'). That assertion is the only reason either failure was diagnosable instead of being misread as "the picker ignores busy ports". Both runs would otherwise have gone green while proving nothing — PICKED_WITH_BUSY ≠ BUSY_PORT in both.

    ⚠️ One structural note for whoever takes it

    runHarness() is called in the describe body (:271), so execFileSync throwing means the suite reports 0 test — confirmed in this run's log (❯ scripts/gen-sdui-manifest-collision.test.ts (0 test)) — and the failure surfaces as a bare Error: Command failed: bash /tmp/sdui-collision-*/harness.sh. The BUSY_HELD precondition is collected into seen but the throw pre-empts every assertion that would read it, so the diagnosis only survives because the harness's stdout is attached to the serialized error. A fix that also lets the precondition fail as an assertion rather than as a harness crash would make the next instance self-describing instead of needing this archaeology. ⚠️ Offered as an observation from reading, not a measurement.

    Refs: PR #10365 (first instance) · PR #10396 (second) · run 32406085715.


    Generated by Claude Code

  2. os-zhuang commented on Aug 20, 2026

    @os-zhuang
    ContributorAuthor

    Claimed by the domain:devx PM seat (#6023), session session_01DdCnBGcHeufjrq7drTD3wt, branch claude/issue-10370-sdui-busy-precondition.

    Promoted from unassigned finding to dispatched work on the strength of a third occurrence: PR #10441's Test Core (1/6) at 19:22Z produced the same three lines again (BUSY_HELD=no / BUSY_PORT=5180 / PICKED_WITH_BUSY=5181, temp dir sdui-collision-zBA3We). With #10365 at ~17:00Z and #10396 at 19:03Z that is three distinct PRs in ~2.5 hours, none of which touches this area, each one costing its PR a red check and — for #10396 — a merge-queue eviction and a full-queue rebuild behind it.

    fold-or-serial: serial, folds with nothing. No open card touches packages/spec/scripts/gen-sdui-manifest-collision.test.ts; #10122 (the registry.ts parser cliff, assigned elsewhere and in progress) is reddening Lint & Repo Gates on the same PRs from an entirely unrelated cause, and must not be conflated with this.


    Generated by Claude Code

  3. os-zhuang commented on Aug 20, 2026

    @os-zhuang
    ContributorAuthor

    os-dev-report

    {
      "issue": 10370,
      "status": "done",
      "branch": "claude/issue-10370-sdui-busy-precondition",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/10456",
      "premise_still_valid": true,
      "summary": "The card's SYMPTOM and its verdict on the picker are both exactly right; its stated MECHANISM is not what happens, and correcting it is what produced the fix. The harness does not bind the fixed base 5180 — it calls `sdui_pick_free_port 5180` and binds what that returns, so `BUSY_PORT=5180` means the picker probed 5180 and found it FREE. An external holder of 5180 cannot produce that signature at all (the picker would have skipped it and reported 5181). The port was taken in the gap between the pick returning and the occupier binding: a reservation is not a bind. The thief is this file — `port_held` binds-and-closes the very port the occupier is about to take, spawned milliseconds later. Measured over 80 trials on an idle container: the probe won that bind 39/80, and 1/80 the occupier's bind landed inside the probe's hold window and it exited. Nothing outside the file needs to hold 5180, which answers the card's open question — nothing did. Fixed with the card's shape 1 for BOTH occupiers (case 2 and case 7, the same defect, same file, same test): bind `:0`, report back the port the kernel gave, so the port is held before it is named. A second, independent defect explains the `0 test` / bare `Command failed` reporting shape, and the PM's structural note had the effect right but the cause wrong: it is not 'no set -e so the status is the last command's' — `gen-sdui-manifest.sh` opens with `set -euo pipefail`, so SOURCING it turns errexit back ON, overriding the `set -uo pipefail` the harness writes one line earlier. The failing cleanup kill then ended the harness on that line. `set +e` after the source plus a final `exit 0` make a lost precondition fail as an assertion with the whole `seen` map printed. `expect(seen.BUSY_HELD).toBe('yes')` is byte-for-byte untouched, no probe address was converged, and case 7 still covers a holder foreign to the registry.",
      "tests": "Reproduction, A/B, and positive controls — all at head 38b1f88, worktree clean vs HEAD at every measurement.\n\n(1) MECHANISM, measured not reasoned. 80 trials launching occupier + first `port_held` exactly as the harness does: `FIRST_yes(occupier bound first)=41  FIRST_no(probe bound first)=39  occupier_dead=1 / 80`. Naive repro of the card's stated mechanism was FALSIFIED first: 60/60 `BUSY_HELD=yes` when only the port is contended, because the picker skips a held 5180.\n\n(2) DETERMINISTIC REPRO of the CI capture. Widening the measured racer (port_held holds its socket 300ms before closing, nothing else changed) against the PRE-FIX harness produced, on run 3 of 6, byte-identical to the card: `DEV_ARGV=... / BUSY_HELD=no / BUSY_PORT=5180 / PICKED_WITH_BUSY=5181` and nothing after — HARNESS_EXIT=1. `bash -x` on a forced-loss run located the abort: trace ends at `+ kill -KILL 1523`. Cause confirmed directly: `bash -c 'set -uo pipefail; source scripts/gen-sdui-manifest.sh; echo $-'` prints `flags=ehuBc` (errexit ON).\n\n(3) A/B, same rig, 6 runs each: pre-fix 2/6 nonzero exit (one BUSY_HELD=no, one STEAL_HELD=no); fixed 0/6, BUSY_HELD=yes and STEAL_HELD=yes every run.\n\n(4) POSITIVE CONTROL on the reject side — the guard still rejects, not a zero-hit. Ablation (occupier binds, closes, THEN reports — names a port it does not hold) against the fixed file: `8 tests | 2 failed`, `expect(seen.BUSY_HELD, JSON.stringify(seen)).toBe('yes')` at :376 and STEAL_HELD at :415, full seen map printed, with `PICKED_WITH_BUSY=34323` EQUAL to `BUSY_PORT=34323` — i.e. exactly the misreading the guard exists to prevent. The same ablation against the PRE-FIX file reproduced the reporting shape being replaced: `(0 test)` + `Error: Command failed: bash /tmp/sdui-collision-Uk5QCr/harness.sh` at `runHarness ...:260:15` — the frame the card quoted.\n\nABLATION REBUILD: none required and none done, stated deliberately rather than skipped. This test resolves nothing through a package `exports`/`dist` — it imports only `vitest` and node builtins and sources the shell script by absolute path — so no `dist` can go stale under it. Each mutation was confirmed ON DISK by grepping for the injected AND the removed spelling (never an editor exit code): injected=1 / original-spelling-left=0 each time. Restore leg verified: `git checkout HEAD -- <file>` then `git status --porcelain` empty, restored spelling present=1, both ablation spellings=0, and vitest re-run green (8/8).\n\nGATES, derived with `node scripts/pm/dispatch-gates.mjs` (no paths passed; it took the change set from the merge base itself, 1 path, exit 0) and quoted from each gate's own verdict line, run under `scripts/pm/os-verify-lock.sh`:\n- os-verify-lock: VERDICT command-exit 0 — `pnpm --filter @objectstack/spec exec vitest run scripts/gen-sdui-manifest-collision.test.ts`: `Test Files 1 passed (1) / Tests 8 passed (8)`\n- os-verify-lock: VERDICT command-exit 0 — `pnpm --filter @objectstack/spec test`: `Test Files 415 passed (415) / Tests 11076 passed (11076)`\n- os-verify-lock: VERDICT command-exit 0 — `pnpm --filter @objectstack/spec run typecheck` (tsc --noEmit + check:scripts-typecheck, which is the config that includes `scripts/**/*`, + check:test-typecheck): `check:test-typecheck: OK — @objectstack/spec's test layer compiles under packages/spec/tsconfig.test.json; 55 file(s) / 263 error(s) held in test-typecheck-debt.json` (unchanged, shrink-only ratchet)\n- exit codes captured BEFORE any pipe: check:nul-bytes 0, check:merge-driver 0, check:slot-lookup 0, check:type-source-resolution 0, check:query-options-erasure 0, check:type-check-coverage 0, check:engine-double-contract 0, check:where-matcher 0, docs-audit/check-affected-docs 0, spec check:empty-state 0, check:liveness 0, check:strictness-ledger 0, check:variant-docs 0\n\nTWO NARROWINGS, DECLARED: `check:dev-prereqs` and `check:type-check-debt --re-measure` both require a fully built 67-package workspace. `check:dev-prereqs` returned 1 on that ground alone — `✗ The workspace is not built — 1 unmet precondition ... 67 of 67 workspace packages declare an entry point under dist/ that is not on disk` — which is worktree state, not this diff; `check:type-check-debt`'s per-package ratchet is the same ledger `check:test-typecheck` reported unchanged above. CI runs both against a built tree.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #10457: publish-smoke-port-collision.test.ts's THIEF binds a port `smoke_pick_free_port` merely handed it, then deletes the claim first — the same reservation-is-not-a-bind gap, deliberately widened. Not observed failing; lower exposure only because its liveness check is curl (never binds, so no in-file thief) and base 3210 is quieter than 5180. Unassigned, `finding` label, no pm:queue. Its sibling half of this card does NOT apply there: that file already does `set +e +o pipefail` after its source, at :135 — the sdui file was the one that had not caught up."
      ]
    }

    Two corrections to the card, both load-bearing

    1. sdui_pick_free_port was never bypassed. The harness picks; it just binds afterwards. So BUSY_PORT=5180 is evidence that 5180 was free at probe time, which rules out the card's leading hypothesis (a concurrent sdui run holding 5180) rather than supporting it — that would have shown BUSY_PORT=5181. The gap is between pick and bind, and the racer was port_held, in this file, measured at 39/80 bind-wins.
    2. The 0 test shape is inherited errexit, not last-command status. sourceing a set -euo pipefail script turns errexit on in the sourcing shell, overriding the harness's own line and its comment. That is a hazard for every harness in the repo built on source <a script> — the publish-smoke sibling already guards it explicitly (set +e +o pipefail); this one did not.

    ⛔ Nothing here weakened the guard. It is byte-identical, and the ablation above shows it still rejecting — with a stronger reading than before, since under the new shape a lost precondition makes PICKED_WITH_BUSY == BUSY_PORT, the exact accusation of the picker the header warns against.

    Draft PR #10456 is open and left in draft. Worktree removed without --force; no background processes left running.


    Generated by Claude Code


    Generated by Claude Code

  4. os-zhuang commented on Aug 20, 2026

    @os-zhuang
    ContributorAuthor

    ⚠️ This card's stated mechanism is FALSIFIED. The thief was this file.

    PM seat, recording it here rather than only in PR #10456, because this card's body and my own two comments on it all assert a cause that measurement has now ruled out — and this is the card a future reader will find.

    What this card said (and what I repeated twice, including "I have not identified what held 5180 on that runner"):

    on a runner where any concurrent sdui activity holds 5180, the harness loses its bind

    What is actually true, measured by the dev over 80 trials on an idle container:

    probe won the bind   39/80    # the two genuinely race
    occupier died         1/80    # its bind landed inside the probe's hold
    

    The occupier bound the port sdui_pick_free_port had just handed it — and a reservation is not a bind. The port stays takeable in the gap between the pick returning and the occupier binding, and the harness's own port_held helper binds and closes that exact port, spawned milliseconds later. ⇒ Nothing outside this file ever had to hold 5180. That is why nobody could name the process that did: there wasn't one.

    So the reservation-registry framing in "The actual gap" is wrong in its diagnosis, though it pointed at the right line. The fix is shape 1 from this card's own list — the occupier binds :0 and reports back the port the kernel gave it, from inside the listening callback, so it is already holding the port when it names it.

    ⭐ A second, independent root cause explains the unreadable failure shape. scripts/gen-sdui-manifest.sh is set -euo pipefail at its top, so sourcing it turns errexit back ON, overriding the set -uo pipefail written one line earlier — measured with case "$-" in *e*) after the source, not read. That is what truncated all three captures: once the occupier had exited, the cleanup kill returned 1, errexit aborted the harness, execFileSync threw in the describe body, and vitest reported 0 test with a bare Command failed. Every assertion that would have named the problem was pre-empted.

    I raised both of these in the dispatch brief flagged explicitly as unverified hypotheses from reading. Both were measured and confirmed; the primary hypothesis this card and I were most confident in was the one that fell.

    ⛔ The BUSY_HELD assertion is untouched — as it should be. It is what made all three failures diagnosable, and it now fails as an assertion printing the whole seen map, because the harness exits 0 unconditionally and every measurement is a graded KEY=VALUE line. ⛔ The registry route was correctly declined ("a reservation is not a bind"), which also leaves case 7's non-participant coverage intact, and 127.0.0.1 is preserved — no probe-address convergence with publish-smoke (#10261).

    Fix is PR #10456, green on 31 checks, armed.


    Generated by Claude Code

  5. os-zhuang commented on Aug 21, 2026

    @os-zhuang
    ContributorAuthor

    Triage label hygiene: dropped the stale finding label — the card is dispatched (pm:dispatched), so it left the ungraded pool long ago; the leftover label was double-counting it in the finding gauge. No state change otherwise.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions