Uh oh!
There was an error while loading. Please reload this page.
Shift-close releases the seat; linger mode fences the briefing's named tails (pm-dispatch protocol) - #12682
Merged
Merged
Conversation
…named tails Executes the 2026-08-27 maintainer ruling (「落协议」 on the three-part shift-close design; proposal quote carried in the protocol text) fixing the round-open mutex's two false positives observed when a summoned engine-lane PM self-recused against a predecessor that had already closed out. Three ruled changes, in the clauses where the four readings live (SKILL.md) and where the close sequence lives (seat-post-protocol.md): 1. Close-out briefing = seat release. The recency window now applies only to un-closed activity; a briefing that is the latest event releases the seat immediately. Close sequence defined: seat-post body/title refresh first, briefing comment as the final stroke; the successor's release condition is the briefing's presence, and a lagging title is a repairable half-state fixed on seating, never a blocker. 2. Linger mode (离任留守), the default post-close custody: the outgoing PM may keep driving the items named in its briefing to merge — no queue reads, no new dispatches, no un-named cards; residual timers only touch the named set; one tail-clear note when the last tail merges. The successor seats immediately and fences the named set declaratively (same shape as the hot-file serial queue). Title vocabulary is not extended: a closed seat shows vacant, the linger register is one line in the description section. 3. Reading-3 cross-seat filter + explicit-summon override: only claims claiming this seat's lane dispatch count as occupation (claims carry session ID and seat identity); with a briefing present a summoned session seats directly, with none the conservative confirm-termination path stays. Non-goals held: 宁少跑 unchanged for the un-closed case; no new labels or title state words; check-half-states.mjs and the dead-claim/liveness rules untouched. Both files stay at their zero-headroom ratchet ceilings (1005/1005, 105/105); every added line is paid by same-file cuts whose surviving homes are listed in the PR body. Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq
os-zhuang
approved these changes
Aug 27, 2026
os-zhuang
marked this pull request as ready for review
August 27, 2026 09:51
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#12678
Governed protocol change (
.claude/skills/pm-dispatch/**) — DRAFT for human merge, per the governed-surface rulings.Ruling (verbatim, untranslated)
Maintainer proposal-question (2026-08-27, live PM chat):
The seat answered with the three-part design on the card; the maintainer's execution ruling on that exact shape, verbatim: 「落协议」. The three parts are the ruled scope — not a menu.
The three changes
1. Close-out briefing = seat release — the SKILL.md 开轮互斥读法 clause is rewritten. The recency window is scoped to un-closed activity only (近期窗口只管未收班活动; the recusal condition now reads 未收班活动任一未满一个轮次 ⇒ 自退). New release rule: 收班简报 = 前任「不再写」的显式声明,是释放标记不是锁 — a briefing that is the latest event seats the successor immediately; a lagging title is a repairable half-state fixed on the way in (⛔ 不是阻塞); the briefing's named tails are fenced. Close-sequence ordering is defined in the seat-post-protocol.md 收班 bullet (收班序列): seat-post body/title refresh first, briefing comment as the final stroke; the successor's release condition is the briefing's presence.
2. Linger mode (离任留守) — new bullet in seat-post-protocol.md, carried as the extension of the 2026-08-25 清标即落地 ruling (谁评审谁落地; provenance lives in contract-review.md and is pointed at, not copied). Post-close write surface = exactly the briefing-named items (accepted PRs awaiting queue, review-chain carriers mid-landing); ⛔ no queue reads, no new dispatches, no un-named cards; residual timers only touch the named set; one tail-clear note on the seat post when the last tail merges. The successor seats immediately and fences the named set declaratively (same collision shape as the hot-file serial queue). Title vocabulary NOT extended: a closed seat shows the vacant state word; the linger register (who lingers, which tails) is one line in the 说明 section. The exit checklist gains the linger branch in steps ② and ⑥ and marks the briefing/audit comment as the final stroke in ⑤.
3. Reading-③ cross-seat filter + explicit-summon override — reading ③ counts only claims claiming this seat's lane dispatch (只计认领本席车道派发的); review-chain / Director-seat strokes on lane cards are cross-seat work, never occupation (claims carry session ID and seat identity). The maintainer's explicit summon is the arbiter: briefing present ⇒ seat directly; no briefing (predecessor possibly dead mid-round) ⇒ the conservative path stays — ask the maintainer to confirm termination, the confirmation seats.
Non-goals held (binding on the card)
check-half-states.mjsuntouched; the dead-claim reclaim path and the over-24h liveness rules untouched (the liveness bullet was not edited).references/dispatch-runbook.mduntouched (274/274): the four-readings detail rows do NOT live there — its mutex content is the tail-page reading recipe whose own text says 读数定义在主文件, so the release semantics belong to the SKILL.md clause.Zero-headroom ratchet accounting (net 0 per file)
pnpm check:pm-skill-ratchet— exit 0 (captured before any pipe), verdict lines:Growth: the SKILL.md Routine/mutex paragraph grew 14 → 16 lines (+2); the seat-post 收班/离任留守/退场 block grew 20 → 21 lines (+1) plus the ruled content inside it. Every added line is paid by a same-file cut.
Cut ledger — SKILL.md (each cut names its surviving home):
Cut ledger — seat-post-protocol.md:
Re-wrap declaration: line re-flow is confined to the rewritten paragraph and bullets; sentences surviving verbatim inside them (Routine principle ③; exit-checklist steps ①③⑦) re-wrapped only because neighboring sentence lengths changed. No untouched passage was re-wrapped to buy lines.
Gates (exits captured before any pipe; run at commit 2119da4, the PR head, after the final commit)
Derived union:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(exit 0) — derivation stderr names this checkout and commit 2119da4; 9 matched families:pnpm check:pm-skill-ratchet— exit 0; verdict lines quoted above (57 green lines, all files).pnpm check:pm-skill-id-lint— exit 0; verdict: "check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g)."pnpm check:skill-frame-sync— exit 0; verdict: "check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files; binding sentence present in all 4; 4 count mention(s) agree; 42 markdown files scanned for undeclared copies."pnpm check:skill-frame-freshness— exit 0; verdict: "check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now)." (dispatch-named; not in the matched list)pnpm check:pm-governed-prose— exit 0; verdict: "check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces (docs/adr/** · .claude/** · skills/** · AGENTS.md · CLAUDE.md) and claim no others."pnpm check:pm-governed-merges— exit 0; verdict: "check-governed-merges --self-test: 206 assertions … live: the real generator declared 9 output(s) and certified this tree"pnpm check:agent-test-spelling— exit 0; verdict: "check-agent-test-spelling: 0 violations — 387 file(s) · 4168 bare launcher tokens · 1190 launcher-rooted run(s) · 9 separator(s) JUDGED · 5 vitest-backed script name(s) derived from 80 manifest(s)"pnpm check:doc-authoring— exit 0; verdict: "doc authoring guard: 535 refusal-message string(s) across 661 spec sources clean — no internal issue-id references."pnpm --filter @objectstack/lint run check:doc-formula-expressions— first run printed PREREQUISITE NOT MET (compiled deps absent — not a measurement); after building @objectstack/formula and @objectstack/lint, exit 0; verdicts: "check:doc-formula-expressions (spec TSDoc): 9 example(s) judged clean across 1052 packages/spec/src files" and "(field-level When predicates): 14 predicate(s) on a statically determinable field layer judged clean; 6 skipped as undeterminable."node scripts/pm/check-governed-queue-guard.mjs— CI-only in this environment: NOT MEASURED locally; runs on the PR.pnpm check:nul-bytes(any-edit discipline; outside the derived list) — exit 0; verdict: "check-nul-bytes: OK (scanned 7030 text file(s) … no raw ASCII control bytes)."No changeset: the diff is entirely
.claude/skills/pm-dispatch/**— publishes nothing; theskip-changesetlabel is applied to this PR as a step (whole-set label write with compare-style read-back, recorded in the card report).Acceptance replay (reasoning over the card's engine transcript)
The 2026-08-27 04:19Z case replayed under the new text: the summoned engine PM runs the four readings. Reading ① finds the predecessor's shift-close briefing posted 4 minutes earlier and it is the latest event ⇒ release marker, not a lock — the recency window no longer applies to it (只管未收班活动). Reading ③ finds fresh non-self claims on two lane cards, but they are the skills seat's review-chain strokes claiming no engine-lane dispatch — cross-seat under the new filter, not occupation. Result: the session seats immediately, repairs the lagging 🟢 title (the recorded half-state) on the way in, and fences the briefing's named tails as the linger set. No 40-50 minute wait, no question bounced to the maintainer. Counterfactual guard: a predecessor with NO briefing (dead mid-round) still presents un-closed activity inside the window ⇒ the conservative path verbatim — recuse; on an explicit summon, ask the maintainer to confirm termination, and the confirmation seats.
Generated by Claude Code