Repository navigation
pm-dispatch: an unruled decision behind Blocked-by refuses the dispatch; measure-then-decide sends a reading-only dev - #23414
Conversation
…dispatch; measure-then-decide sends a reading-only dev SKILL.md state-model row: a line-head Blocked-by: naming an open needs-user-decision card without Ruled: is not dispatched and hangs pm:blocked until that card is ruled. dispatch-runbook 派发词构造细则: a measure-first card dispatches a 只读读数 dev (no closure, no tests, no PR; the reading goes on the decision card or into a new card) or waits for the ruling; the building dev follows the ruling. os-dev.md: the reading-only mode's one line under 何时停手不写码 and `reading` in the status enum. Ceilings held at 319/241/402: the runbook pays with its frame-copy pointer folded into the neighbouring line, os-dev with a line its sibling restates. Claude-Session: https://claude.ai/code/session_013zGKMG1cRedVzWtqGGGZW9 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Reviewed in seat by the dispatching skills seat (served tier read from ① Derived judgments
② Semver levelNone owed: ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #23379
Clause-②: no
Text-only charter change on three protocol faces, under the maintainer's 「同意」 (2026-10-14) on the director seat's proposal, quoted from the card verbatim: 「先裁再派。派发门加一条:卡上有未裁的
Blocked-by: 决策或派发令写「先测量再决定」的,只派「只读读数」的 dev(不建闭包、不跑测试),或根本不派、等裁。」 and 「立卡你决定,不需要我同意」. Second leg of the serial relay behind #23378 (its draft PR #23405 at head63496bd46ceditsos-dev.md,dispatch-runbook.mdandAGENTS.md); this PR is based onorigin/main5656c5638a, not on that branch, and touches none of its hunks (proof below). Draft; the landing tier is whatevernode scripts/pm/check-governed-merges.mjs --pr Nreads for.claude/**.What changes (text only), before / after
1. Dispatch gate —
.claude/skills/pm-dispatch/SKILL.md, 状态模型 table, thepm:blockedrow (L108), amended in placeBefore (159 bytes):
After (295 bytes; the file's widest-row pin is 342, so the row stays inside it, and a table row is structurally exempt from the 120-byte line cap):
The gate sentence: a line-head
Blocked-by:in the card's body or thread that names an openneeds-user-decisioncard carrying noRuled:means the card is not dispatched and hangspm:blockeduntil that card carriesRuled:. It sits wherepm:blocked+Blocked-by:semantics already live (the selection skip, the unlock scan), beside theneeds-user-decisionrow (L106, 永不派发) and the red line at L29.Ruled:is the markerreferences/lanes/director.mdL12 writes on the decision card's first body line when a ruling is recorded.2. Reading-only dev —
.claude/skills/pm-dispatch/references/dispatch-runbook.md, 〈派发词构造细则〉Before (L235, 112 bytes):
After (L235 amended in place, 118 bytes; L236 new, 108 bytes):
A measure-first card (a dispatch whose first act is "measure, then decide") dispatches a 只读读数 (reading-only) dev — no worktree closure, no build, no test run, no PR; it reads the tree and the record and posts the reading on the decision card or files one — or is not dispatched at all and waits for the ruling; the building dev follows the ruling. The old clause 「⛔ 测量存在之前不写修复」 is subsumed: a dev that opens no PR writes no fix.
Paid by one deletion in the same file. Deleted L206 (87 bytes):
It restates
.claude/agents/os-dev.mdL355 「框架唯一副本在 pm-dispatch SKILL.md 〈升级与决策〉;派发词逐字粘贴,dev ⛔ 不留副本。」, and the one fact it carried for the PM side (where the block lives) is folded into its neighbour L205, amended in place: 「四轴框架整段现取现粘、逐单随派发词下发。」 becomes 「四轴框架块(SKILL.md 升级与决策 节)整段现取现粘、逐单随派发词下发。」 (95 bytes).3. The mode's one line —
.claude/agents/os-dev.md, 〈何时停手不写码〉 and the report templateNew L354 (116 bytes), the sibling of the
needs_decisionline:Report template enum (L364), amended in place:
becomes
Paid by one deletion in the same file. Deleted old L354 (84 bytes):
It restates its two siblings: L355 「…派发词逐字粘贴,dev ⛔ 不留副本。」 (the frame arrives via the dispatch order, from the single copy) and L356 「…派发词没带,停下向 PM 索取…」, and
dispatch-runbook.mdL205 「…逐单随派发词下发」 carries the PM side.Line budget (ceilings on
main: 319 / 241 / 402 — every added line paid in the same file, no ceiling moved).claude/skills/pm-dispatch/SKILL.md.claude/skills/pm-dispatch/references/dispatch-runbook.md.claude/agents/os-dev.mdEvery added or amended non-table line is at or under 120 bytes (measured with
Buffer.byteLength): 116, 65, 95, 118, 108; the SKILL.md row is a table row (exempt classtableinscripts/pm/check-skill-line-ratchet.mjs) at 295 B against the file's row pin of 342 B. No re-wrap funds any line. No issue number enters a skill file; no model identifier enters any file. The maintainer's term 「只读读数」 is the one spelling in all three files; "reading-only" is its English face here.Dry run — the new sentences applied to #23267's claim as it stood at comment 6156257682 (2026-10-13T22:09Z, posted 22:10:28Z)
What the record shows for the card at that instant:
pm:queue(it had carriedpm:blockedfrom 14:09:08Z and moved topm:queueat 20:58:57Z, the unlock scan firing after itsBlocked-by:target reached the closed state at 20:02:28Z). The claim's own act moved it topm:dispatchedat 22:10:22Z.Blocked-by:isBlocked-by: #23224(body line 10 today, line 18 before theRuled:first line and its blank were prepended on 2026-10-14). ADR-0138 E1: the grants channel — the guest entry resolves D4's organization, then theguestanchor's bindings (D3, D4) #23224 is adomain:engineenhancement card that never carriedneeds-user-decision(its label timeline has zero such events) and was already in the closed state at 22:09Z.Blocked-by: #23266」, not as a line-head marker;references/state-machine.mdL24 makes the line-head form canonical and a sentence mention prose. And [Decision] 访客(guest)的行可见范围:沿用对象的内部共享模型(A,现状,公开对象全部行可见)、按 externalSharingModel 判(B)、还是只经分享链接(C) #23266 was no longer an unruled decision at 22:09Z: its ruling (letter C, 「同意」) is comment 6148183570 at 14:44:15Z andneeds-user-decisionleft it at 14:44:56Z — 7.4 hours before the claim, which itself says "under ruling C on [Decision] 访客(guest)的行可见范围:沿用对象的内部共享模型(A,现状,公开对象全部行可见)、按 externalSharingModel 判(B)、还是只经分享链接(C) #23266". At 22:09Z [Decision] 访客(guest)的行可见范围:沿用对象的内部共享模型(A,现状,公开对象全部行可见)、按 externalSharingModel 判(B)、还是只经分享链接(C) #23266 was apm:dispatchedexecution card of that ruling (its body carries noRuled:first line; the ruling lives in its comment, which predates the first-line convention).So the
Blocked-by:leg does not refuse the building dispatch as of 22:09Z: neither card the body names was an openneeds-user-decisioncard withoutRuled:. It would have refused only a dispatch read before 14:44Z.The measure-then-decide leg does refuse it. The claim's File surface reads "the dev measures which door is the record's and declares it in the report and PR body … whichever the measurement shows ADR-0138 D3 item 5 names as the administrator's path", so the dispatch's first act was a measurement whose result selected the route, and the card's own body lists the dev's options as (A) a new door, (B) a new spec key, (C) amend the record — a decision shape. Under runbook L235–236 as amended, that dispatch sends a 只读读数 dev (or nobody, waiting for the ruling): the reading goes on the decision card, no closure is built, no suite runs, no PR opens. What happened instead is the card's own measurement: a building dev built the plugin-security closure under the verify lock and ran two pin suites, returned
status: needs_decisionat 22:28:30Z (comment 6156571003), the card enteredneeds-user-decisionat 22:31:36Z, the ruling landed at 02:06:29Z (comment 6159279205,Ruled:first line written on the body), and the building dev was dispatched at 02:11Z.Acceptance item read against the record: one of the two legs refuses (measure-then-decide), the other does not fire at 22:09Z; the card's premise that the
Blocked-by:leg would have caught this claim is corrected here rather than forced.Boundary proof against PR #23405 (head
63496bd46c, merge-basee4ec6ec1a5)git fetch origin pull/23405/head:refs/pm/pr-23405→63496bd46c.git merge-tree --write-tree 54dbbd5636 refs/pm/pr-23405→ exit 0, tree73d591a0e40c08d7f06917e8a0a710ec6c5fe871, no conflict (a read; nothing merged, nomainmerged, no rebase).git diff -U0 e4ec6ec1a5 refs/pm/pr-23405):os-dev.mdL138, L140–141, L143, L147–148, L158–163 (deleted), inserts after L245 and L278, L370;dispatch-runbook.mdL220;AGENTS.mdL584–585. This PR's edits:os-dev.mdL354 and L364;dispatch-runbook.mdL205–206 and L235–236;SKILL.mdL108.AGENTS.mduntouched.e4ec6ec1a5and5656c5638a(git diff --staton them is empty), so every line number above holds on the currentmain.Gates (local subset = the families
node scripts/pm/dispatch-gates.mjs --commandsderives for this diff at5656c5638a; exit codes captured before any pipe; the farm —pnpm lint,pnpm test, full builds — is CI's, read off this PR's check-runs)22 families derived, 22 run, all exit 0,
--ranreconciliation: 「✓ dispatch-gates --ran: 22 derived famil(ies) accounted for — 22 run, 0 NOT-MEASURED (a DERIVED zero — all 22 recorded an exit code and none of them is 3)」.pnpm check:pm-skill-ratchetexit 0 — verdict lines: 「SKILL.md is 319 lines (ceiling 319; headroom 0)」, 「SKILL.md: widest table row is 342 bytes (pin 342; headroom 0)」, 「dispatch-runbook.md is 241 lines (ceiling 241; headroom 0)」, 「os-dev.md is 402 lines (ceiling 402; headroom 0)」.pnpm check:pm-skill-id-lintexit 0 ·pnpm check:pm-governed-proseexit 0 ·pnpm check:skill-frame-syncexit 0 ·pnpm check:agent-model-declaredexit 0 ·pnpm check:nul-bytesexit 0.check-closing-keyword-parity+ its self-test,check-comment-mask-corpus,check-governed-queue-guard --self-test,check-harness-current --self-test,check:agent-test-spelling,check:commit-card-trailers,check:cross-package-test-inputs,check:doc-authoring,check:driver-memory-census,check:gitlink-declared,check:pm-expected-skips,check:pm-governed-merges,check:refd-timer-probe,check:watch-hint-literal,@objectstack/lint check:doc-formula-expressions) all exit 0.check:doc-formula-expressionsfirst answered exit 3 PREREQUISITE NOT MET (@objectstack/formulaand@objectstack/lintunbuilt — nothing measured), so a targeted two-package build ran under the verify lock (os-verify-lock.sh -c 'pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2'→ 「VERDICT command-exit 0 · held the lock 112s · waited 0s」), after which the gate re-ran to exit 0 (「9 @example(s) judged clean … 15 predicate(s) … judged clean」). The build leftgit statusclean — no generated artifact moved.Acceptance notes (observations, not cards)
Ruled:first line; the first-line convention (lanes/director.mdL12) post-dates it. The gate readsRuled:on the decision card; a reader applying it to pre-convention cards should also accept a## Rulingcomment as the record. Not filed: no live card is affected (that card is in the closed state), and the convention is already the rule.references/execution-duties.mdL10 (候选 = …无needs-user-decision…) is unchanged — its filter still reads only the card's own labels; theBlocked-by:-against-the-decision-box reading lives in the SKILL.md row, which every seat reads.维护者速读(草稿)
pm:blocked行多一条:卡上行首Blocked-by:指向一张还没Ruled:的 open 决策卡,就不派发,挂pm:blocked等裁。派发细则里「测量先行卡」改为只派「只读读数」dev(不建闭包、不跑测试、不开 PR,读数贴决策卡或立卡)或干脆不派等裁,建设 dev 待裁后再派。os-dev 契约加一行只读读数模式(报status: "reading"),报告枚举多一个reading。新增的行以删掉两句重复表述支付。guestanchor — ADR-0138 D3 item 5 has no enforcer (PUT /meta/position/guestanswers 403 NOT_OVERRIDABLE; guest suggestions are refused) #23267 那种卡决策没裁就派了建设 dev,结果是一次 271 秒的闭包构建、两套 pin 测试和一份八千字的 needs_decision 报告,零代码。门加在派发前:读数由便宜的只读 dev 产出,建设在裁决之后。Blocked-by:与决策卡上的Ruled:标记,散文里提到决策卡不触发,这是既有正典标记规则的延续。回滚 = revert 这一个 commit,无数据与状态迁移。check-governed-merges --pr读(.claude/**按登记是 Tier S,席位按契约复核落地);若它读为 Tier H,需要你的一次批准。PR 保持 draft。Generated by Claude Code