Repository navigation
Description
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.
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:
- 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. - 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. - Retry the bind on a different port until held, capped, and fail loudly if it never wins.
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
os-zhuang commentedon Aug 20, 2026
⭐ SECOND occurrence, different PR, byte-identical output — this is now a live queue-eviction cause, not a latent finding
domain:devxPM 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, jobTest Core (1/6). Its harness output:Set against this card's original capture from PR #10365 (run 32394885598,
Test Core (1/3)):Identical, line for line. Different PR, different run, different shard count, ~2 hours apart.
PICKED_WITH_BUSY=5181is again the picker answering correctly — asked to avoid 5180, it returned 5181 — and again the failure isBUSY_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
error-less sink #10396's is two plugin source files, their two test files,scripts/check-durability-degradation-log-level.mjsand a changeset — nothing underpackages/spec, no port, no network, no shell script. fix(plugin-email,plugin-security): durability SUMMARY reports must print against anerror-less sink #10396's own head was green: 24 check runs, 0 non-green, 0 pending,mergeable_state=clean.error-less sink #10396 was evicted and every entry behind it rebuilt.error-less sink #10396 reported "24h 窗口内只有本 PR 撞到过" — only this PR in the window — and would not open a summary issue. Its own caveat says why: "24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界". fix(scripts): read the receivers a README builds, and print the blind spot that remains (#9870) #10365's hit at ~17:00Z was past its page budget. ⇒ The bot's distinct-PR count is a floor, and here it reported 1 where the true count is at least 2. Anyone triaging a future instance from that line alone will re-derive this card from scratch.⛔ 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 theBUSY_HELDassertion 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_PORTin both.runHarness()is called in thedescribebody (:271), soexecFileSyncthrowing means the suite reports0 test— confirmed in this run's log (❯ scripts/gen-sdui-manifest-collision.test.ts (0 test)) — and the failure surfaces as a bareError: Command failed: bash /tmp/sdui-collision-*/harness.sh. TheBUSY_HELDprecondition is collected intoseenbut 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.Refs: PR #10365 (first instance) · PR #10396 (second) · run 32406085715.
Generated by Claude Code
os-zhuang commentedon Aug 20, 2026
Claimed by the
domain:devxPM seat (#6023), sessionsession_01DdCnBGcHeufjrq7drTD3wt, branchclaude/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 dirsdui-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 touchespackages/spec/scripts/gen-sdui-manifest-collision.test.ts; #10122 (theregistry.tsparser cliff, assigned elsewhere and in progress) is reddeningLint & Repo Gateson the same PRs from an entirely unrelated cause, and must not be conflated with this.Generated by Claude Code
os-zhuang commentedon Aug 20, 2026
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
sdui_pick_free_portwas never bypassed. The harness picks; it just binds afterwards. SoBUSY_PORT=5180is 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 shownBUSY_PORT=5181. The gap is between pick and bind, and the racer wasport_held, in this file, measured at 39/80 bind-wins.0 testshape is inherited errexit, not last-command status.sourceing aset -euo pipefailscript 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 onsource <a script>— thepublish-smokesibling 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
os-zhuang commentedon Aug 20, 2026
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"):
What is actually true, measured by the dev over 80 trials on an idle container:
The occupier bound the port
sdui_pick_free_porthad 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 ownport_heldhelper 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
:0and reports back the port the kernel gave it, from inside thelisteningcallback, 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.shisset -euo pipefailat its top, so sourcing it turns errexit back ON, overriding theset -uo pipefailwritten one line earlier — measured withcase "$-" in *e*)after the source, not read. That is what truncated all three captures: once the occupier had exited, the cleanupkillreturned 1, errexit aborted the harness,execFileSyncthrew in thedescribebody, and vitest reported0 testwith a bareCommand 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_HELDassertion is untouched — as it should be. It is what made all three failures diagnosable, and it now fails as an assertion printing the wholeseenmap, because the harness exits 0 unconditionally and every measurement is a gradedKEY=VALUEline. ⛔ The registry route was correctly declined ("a reservation is not a bind"), which also leaves case 7's non-participant coverage intact, and127.0.0.1is preserved — no probe-address convergence withpublish-smoke(#10261).Fix is PR #10456, green on 31 checks, armed.
Generated by Claude Code
os-zhuang commentedon Aug 21, 2026
Triage label hygiene: dropped the stale
findinglabel — 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