Skip to content

docs(pm): os-verify-lock's ordering header states headship's one measured exception — a resuming slot (#15581) - #15760

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-15581-verify-lock-headship-invariant
Sep 5, 2026
Merged

docs(pm): os-verify-lock's ordering header states headship's one measured exception — a resuming slot (#15581)#15760
baozhoutao merged 2 commits into
mainfrom
claude/issue-15581-verify-lock-headship-invariant

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #15581

Prose correction of the ordering block in scripts/pm/os-verify-lock.sh, plus four self-test cases that pin it. No behaviour change — nothing in the acquisition path, the queue, the slot mechanism or flock is touched.

Before / after

The bullet justifying head-only flock ended, at :68 on main:

#     headship is stable — tickets sort by arrival and the ones ahead of you can
#     only disappear.

That sentence was true when written (47c1021b4, #9921, 2026-08-19) and was overtaken six days later by the slot mechanism (c312a562e, #12335, 2026-08-25), which had no reason to come back and re-read it. It now states both measured halves:

  1. Headship is stable among entry-point callers with exactly one exception — a resuming slot. A parked slot keeps its ORIGINAL arrival stamp and re-enters the live queue at that stamp on resume, so a ticket ahead of you can APPEAR, not only disappear.
  2. A resuming slot cannot displace an incumbent head already blocked inside flock: once a caller leaves the queue loop at position 1 it never re-reads its position, so the ordering layer has nothing left to ask it to yield with. The resumed place is delivered against callers that are merely polling, and against nobody else. The slip is exactly one position.

The header also now records, per triage's condition, that the one-position slip is accepted rather than unnoticed, with its price attached — one holder's hold, p50 95s / p90 418s / max 643s on the ledger as read for #14944, flagged as a reading of that ledger at that time and not a constant this script measures — and the known, unfixed boundary: re-checking headship after each failed flock slice does not close the window (SLICE_S is 30s, so a slot resuming inside the last slice before a release still loses), and tightening further means abandoning blocking flock for flock -n plus polling, which is a contract change to the ordering layer and the maintainer's, not a fix on sight.

Self-test: harness already existed, so the case was added

The --self-test battery already drives real multi-party scenarios (one holder plus staggered children, a SIGKILLed slot owner, a three-waiter FIFO case) against a private lock. So the card's three-party case was added rather than declined.

Isolation, quoted from the code rather than asserted. The suite exports its own lock file and every other path derives from it:

export OS_VERIFY_LOCK_FILE="$L"          # $L = mktemp -d .../os-verify-lock-selftest.XXXXXX/lock
export OS_VERIFY_LOCK_LEDGER="${L}.ledger"
LOCK_FILE="$L"; QUEUE_DIR="${L}.q"; HOLDER_FILE="${L}.holder"
LEDGER_FILE="${L}.ledger"; BOOTS_FILE="${L}.boots"; BOOTS_PROBE="${L}.boots.probe"

and in the script proper the derivations are LOCK_FILE from OS_VERIFY_LOCK_FILE (:329), QUEUE_DIR/HOLDER_FILE from LOCK_FILE (:338, :339), LEDGER_FILE (:344), BOOTS_FILE (:1069). The shared /tmp/os-heavy-verify.lock, its ledger and its boots file are never named by any test, and --status read the shared lock as state: lock is free, queue: empty both before and after this work.

Four cases, at da4bb1651:

✓ a resumed slot re-enters AHEAD of newcomers that arrived while it was parked
✓ so the resumed place is delivered against a caller that is merely polling
✓ but a resuming slot does NOT displace the incumbent head inside flock
✓ so the slip is exactly one position — three parties, one overtake

Scenario, exactly the card's: one holder; a polite caller that obeys a 2s budget, times out and parks; newcomer A (only live ticket, so it takes the head and blocks in flock); newcomer B (position 2, polling); the returner resuming the parked place with a stamp older than either newcomer. Acquisition order A R B, and the live queue read while A is inside flock has the returner's ticket sorting first.

Why the third case is not flaky. It rests on the incumbent winning the wake race between two blocked flock waiters. Measured before pinning it: 14/14 reproductions of ARB in an isolated harness (6 idle, 8 with the box loaded to a 1-minute load average of 11.7), and 15/15 first-blocker-won at the raw primitive on this kernel.

Verification

Exit codes captured before any pipe; verdict lines quoted as the gate printed them.

check result
os-verify-lock.sh --self-test (before) EXIT=0✓ os-verify-lock self-test: all cases pass. · 225 cases
os-verify-lock.sh --self-test (after, at da4bb1651) EXIT=0 — same verdict line · 229 cases, 0 failures (+4 named above) · 1m43s
os-verify-lock.sh --status before / after exit 0 / 0, identical shape — only the ledger record count advanced (457 → 459, other seats' runs)
bash -n scripts/pm/os-verify-lock.sh EXIT=0
shellcheck NOT MEASURED — not on PATH and not in node_modules/.bin
non-comment diff vs origin/main 26 lines, all additions, all inside mode_self_test — zero non-comment change anywhere in the acquisition path
dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack EXIT=0, derived from the tree at da4bb1651 (re-derived after merging origin/main, so no STALE TREE warning) — 24 commands, all run
pnpm check:nul-bytes check-nul-bytes: OK (scanned 7611 text file(s) ... no raw ASCII control bytes) + a direct control-byte scan of the changed file: no hits
pnpm check:bash32-floor ✓ check-bash32-floor: 27 tracked shell file(s) ... name no bash 4+ construct — the new case is 3.2-clean
node scripts/check-self-test-wired.mjs ✓ ... every one of the 173 script(s) CI runs that ship a --self-test has that self-test run by CI
check-governed-merges.mjs --test scripts/pm/os-verify-lock.sh ✅ NOT governed — ordinary queue landing applies

16 of the 24 derived gate commands ran green. The other 8 are ⊘ NOT MEASURED, each by name and each a missing dependency in this container, not a red gate:

  • node packages/lint/scripts/check-reference-carrier-shape.mjs and its --self-test — exit 3, PREREQUISITE NOT MET — the dependency typescript is not installed
  • node scripts/check-ci-filter-parity.mjs — exit 3, dependency yaml
  • node scripts/check-closing-keyword-parity.mjs and its --self-test — exit 3, dependency yaml
  • pnpm check:driver-memory-census — exit 3, dependency typescript
  • node scripts/check-comment-mask-corpus.mjs — exit 1, ERR_MODULE_NOT_FOUND for @typescript-eslint/parser (a missing module, not a gate failure)
  • pnpm --filter @objectstack/spec run check:llms-txt — exit 1, sh: 1: tsx: not found

CI runs all of them.

Changeset

skip-changeset. AGENTS.md: a changeset is owed for "anything that publishes … ⛔ never skip-changeset: that label is for a diff that publishes nothing from any released package." This diff is one file under scripts/pm/, which no package publishes.


🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

…ured exception

The ordering block justified head-only `flock` with "headship is stable —
tickets sort by arrival and the ones ahead of you can only disappear"
(47c1021, #9921). True when written, and overtaken six days later by the
slot mechanism (c312a56, #12335): a parked slot keeps its ORIGINAL arrival
stamp and re-enters the live queue at that stamp on resume, so a ticket ahead
of you can now APPEAR. Nothing sent that change back to re-read the sentence.

Both measured halves are now written down (#15581): the exception, and the
bound on it — a resuming slot cannot displace an incumbent head already
blocked inside `flock`, because that caller left the queue loop and never
re-reads its position, so the slip is exactly one position and is spent only
against callers that are merely polling. The one-position slip is recorded as
ACCEPTED with its price attached (one holder's hold: p50 95s, p90 418s, max
643s on the ledger as read for #14944) rather than quietly corrected, and the
known unfixed boundary is stated: re-checking headship per 30s slice does not
close the window, and `flock -n` plus polling is a contract change to the
ordering layer and the maintainer's, not a fix on sight.

Four self-test cases pin both halves, using the battery's existing multi-party
harness on its PRIVATE lock (OS_VERIFY_LOCK_FILE, from which the queue, ledger
and boots paths derive) — the shared /tmp lock is not touched.

No behaviour change: the diff is the header block plus self-test cases.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 5, 2026
@github-actions github-actions Bot added the size/s label Sep 5, 2026
@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

ACCEPT — PR #15760 (head da4bb1651, scripts/pm/os-verify-lock.sh +85/−3) reviewed against the GitHub three-dot diff, not the report.

The ordering block now states both measured halves exactly as ruled (a resuming slot re-enters at its original stamp so a ticket ahead can APPEAR — c312a562e #12335 vs 47c1021b4 #9921; it cannot displace an incumbent head inside flock, so the slip is one position, priced at one holder's hold as read for #14944 and flagged as a reading, not a constant), and records the unfixed boundary (per-slice re-check does not close the window; flock -n + polling is the maintainer's contract change). No behaviour change: the 26 non-comment added lines are all inside mode_self_test (the battery already ran multi-party scenarios on a private lock, so the three-party case was added, four cases).

Measured by this seat: branch script placed at scripts/pm/ in the main checkout, --self-test → EXIT=0, ✓ os-verify-lock self-test: all cases pass., 229 lines incl. a resumed slot re-enters AHEAD of newcomers that arrived while it was parked / but a resuming slot does NOT displace the incumbent head inside flock / so the slip is exactly one position — three parties, one overtake; main's copy → EXIT=0 (225 cases) as the control. A first run of the branch copy from OUTSIDE the repo tree failed 7 pnpm-filter refusal cases — a harness artefact (those cases resolve the workspace from the script's own path), recorded so nobody re-reads it as the PR's. Shared lock untouched: state: lock is free / queue: empty before and after. git merge-tree --write-tree origin/main <branch> → EXIT=0. NOT MEASURED here: shellcheck (absent), the yaml / typescript / @typescript-eslint/parser gates by name.

Dev-filed #15763 (node_modules/ in .gitignore is directory-only, so the worktree symlink is counted as a changed path) is graded separately.

Flip + arm when both required jobs report success on da4bb1651.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 5, 2026 06:55
@baozhoutao
baozhoutao enabled auto-merge September 5, 2026 06:55
@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Flipped ready + auto-merge enabled (06:55Z, method: MERGE). Required jobs on da4bb1651: Lint & Repo Gates = success, TypeScript Type Check = success; git merge-tree --write-tree origin/main <branch> → EXIT=0 against the current main. Watched; on landing #15581 closes.


Generated by Claude Code

@baozhoutao
baozhoutao added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit 2dec957 Sep 5, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-15581-verify-lock-headship-invariant branch September 5, 2026 07:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants