Repository navigation
Commit bdb00ff
docs(pm-dispatch): an exhausted contract-review tier hands the review to triage, and triage tracks stale records (#26771)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi97BMJshj8DqLK19hrY2xed1)_
Fixes #26667
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:契约复审归属加一条交接臂——派发席的 `CONTRACT_REVIEW_TIER` 调用被配额拒时,不降档审,而是在 PR
落一行固定格式的 `Contract-review-handoff:` 交接,交分诊席跑;分诊每轮认交接行、自己的记录与现 head
比对,过期的重审或记一条。
- 为什么改:你 10-27 的原话「开发自己有 fable 时都可以自己审核,只有开发的 fable
用完了才需要分诊,但此时分诊要能看出来、区别出来」与「改进 skills」(分诊漏看 #26598 head 已动)。
- 风险与回滚:只改 pm-dispatch 两份参考文本与一个只读识别器;无门禁读交接行,队列守卫不变;回滚 = revert 本 PR。
-
先看:`references/contract-review.md`(交接臂与行形状)、`references/triage-duties.md`(分诊轮步骤)、`scripts/pm/record-recognisers.mjs`(`contractReviewHandoff`
与自检)。
- 两分钟验证:`pnpm check:pm-record-recognisers`——预期 `✓ … 232 cases
pass`,含交接行电池 13 例;`pnpm check:pm-skill-ratchet` 预期两文件行数不变(55 / 103)。
- 需要你做的:批准本 PR(Tier S draft,等契约复审与 GOVERNED_APPROVERS)。
## What changes
Maintainer rule, verbatim (2026-10-27):
「关于契约复审,开发自己有fable时都可以自己审核,只有开发的fable用完了,才需要分诊,但是此时分诊要能看出来,区别出来。」 and,
after the triage seat reported a stale FAIL on a moved head: 「改进
skills」.
- `.claude/skills/pm-dispatch/references/contract-review.md` 〈复核归属与资格〉 —
two lines after 「谁跑 = 派发席」: the hand-off arm (a quota-refused
`CONTRACT_REVIEW_TIER` call never reviews at a lower tier; the seat
posts the hand-off line, keeps `needs:contract-review`, and triage runs
the review) and the line's exact shape, `Contract-review-handoff: triage
— CONTRACT_REVIEW_TIER unavailable (STATUS-OR-ERROR, UTC-TIME) · head
SHA` (the placeholders are spelled in words here; the file carries the
card's exact angle-bracket shape). In 〈等复核标记〉 the readers line becomes
the relation rule: a label without the line is still the seat's own
review and triage does not touch it; a head that moves after the
hand-off needs a fresh line or the seat's own review.
- `.claude/skills/pm-dispatch/references/triage-duties.md` — six
round-step lines: list open PRs whose newest hand-off line names the
current head and run the review in an isolated `CONTRACT_REVIEW_TIER`
subagent with the same-shape record signed by the triage seat; on
triage's own quota refusal write once on the PR, keep the label, never
review lower; each round compare every triage-rendered record's
`Head-sha` with the current head (regen-only / prose-only moves
excepted, judged on the committed tree); a stale FAIL is re-reviewed in
the same round (every earlier must-fix, the delta commits as changes in
their own right); a stale PASS gets one note that it no longer governs
and a delta review is owed; the round record lists each triage-reviewed
PR as `record head → current head → state`.
- `scripts/pm/record-recognisers.mjs` — ONE recogniser:
`readContractReviewHandoff(text)` reads the line (target `triage`, the
tier by its constant NAME through the same token reader `Served-tier:`
uses, reason and UTC time in the parentheses, a 7–40 hex head; anything
short of that is started-and-unreadable), and
`contractReviewHandoff(rows, headSha)` answers whose review it is off
the newest such line: `absent` (the seat's own), `triage` (names the
current head, prefix-matched like a record's span), `stale` (head
moved), `unreadable`. Newest governs through `latestMarkedComment`, the
locator's own resolution. ⛔ No gate reads it; `locateReviewOfRecord` and
the queue guard are untouched, and a pin drives the guard's reading past
a hand-off line beside a record. A new self-test battery of 13 cases;
`SELF_TEST_BATTERY_FLOOR` 12 → 13.
Line budget (the ratchet, lines only down; 120-byte cap, no re-wraps):
| file | before | after | paid by |
|---|---|---|---|
| `references/contract-review.md` | 55 | 55 | +2 hand-off lines, −2 (the
read-only shape and the input set, lines 35–36 — both live verbatim in
`--brief-template`'s built-in text, which the surviving line 37 points
at and the self-test pins); the readers line (54) rewritten into the
label↔line relation (its filter query lives in `dispatch-runbook.md`
〈跨账户复审〉) |
| `references/triage-duties.md` | 103 | 103 | +6 round-step lines, −6:
emergency-triage authorization (→ `dispatch-runbook.md` 〈紧急卡直接分诊〉), the
report-only-instrument close rule (→ `instrument-discipline.md` lines 8
and 12), lane seats do not triage (→ `lanes/skills.md` L22,
`lanes/ui.md` L8, `cross-repo-coordination.md` L32), the three-exit
first-touch + `Batch-of:` line (→ `filing-gate.md` L25–28), the
three-state grading pointer (→ `filing-gate.md` L46–48), the 域分批 / 积压告警
pointer (→ `dispatch-runbook.md` L23/L27). The (c)-metadata lines and
the routing line stay (seat review, head `5956f4ba`). |
The hand-off line in `contract-review.md` is 122 bytes as ONE code span
— the card's exact shape, kept verbatim; the ratchet's `unbreakable`
class (a single code span has no legal wrap) passes it, measured with
`wrapLine`. Every other new line is ≤ 120 bytes.
No forced test edits. No changeset: `.claude/**` and `scripts/pm/**`
publish nothing (`skip-changeset`).
## Tests
- `node scripts/pm/record-recognisers.mjs --self-test` — `✓
record-recognisers self-test: 232 cases pass` (219 before + 13 new),
exit 0. The battery pins rows and strings only: a first cut also read
the rulebook file to pin that it spells the line, and
`check:pm-dispatch-gates` refused it (a structural read of a governed
file from a script is a census that gate classifies), so that pin was
dropped rather than the census widened.
## Acceptance notes
- `landing-operations.md` line 13 still says a review subagent that
cannot start leaves the PR draft outside the queue with the maintainer's
own review as the only bypass; the hand-off arm narrows the quota case
out of that. Not edited (outside the card's file surface); noted for the
seat.
- Item 6 of the card asks the recogniser to expose a record's `Head-sha`
and `Reviewed-by` session. `locateReviewOfRecord(...).sha` already
answers the head on the current head and `absent` on a moved one; the
`Reviewed-by` VALUE is readable through the exported `REVIEWED_BY_LINE`
regex's capture group — no second reader was added, per the claim's
ONE-recogniser surface.
## Verification
Head `c59cd9c6`. `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths) derived 40 families; every one
was run on this head with its exit code captured before any pipe, and
`--ran` reconciled: `✓ dispatch-gates --ran: 40 derived famil(ies)
accounted for — 40 run, 0 NOT-MEASURED`, exit 0. All 40 exit 0.
<details><summary>The acceptance families, verdict lines</summary>
- `pnpm check:pm-record-recognisers` — `✓ record-recognisers self-test:
232 cases pass`
- `pnpm check:pm-skill-ratchet` — `✓ check-skill-line-ratchet: declared
cross-file moves: 3, total ceilings down 487 lines.` (contract-review.md
55/60, triage-duties.md 103/103, both unchanged in count)
- `pnpm check:pm-skill-id-lint` — `✓ check-skill-id-lint: 104 file(s)
clean`
- `pnpm check:pm-governed-prose` — `✓ check-governed-prose: 2
instruction surface(s) name all 13 registered governed surfaces`
- `pnpm check:pm-dispatch-gates` — `✓ dispatch-gates self-test (fast
tier): 1935 cases pass; 7 slow section(s) deferred.`
- `pnpm check:skill-frame-sync` — `✓ check-skill-frame-sync: the one
declared copy of the decision frame is internally coherent`
- `pnpm check:nul-bytes` — `✓ check-nul-bytes --self-test: 75
assertions`
</details>
Not run locally: `pnpm lint` (repo-wide, CI's run), `test:repo` (CI's
`Test Core`). No package is touched, so no ① closure and no ② package
tests. Dogfood battery: n/a — no `!` in the title and no narrowing arm.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(n/a — no schema)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` is CI's run (repo-wide), not
re-run locally.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset — nothing
publishes; both declaration lines are filled in above.
- [x] 6. `packages/spec` touched: no.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the maintainer's
rulings quoted verbatim).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADHkmSUY4HbWjTHDfPyAYU)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent b2b3124 commit bdb00ff
3 files changed
Lines changed: 134 additions & 10 deletions
File tree
- .claude/skills/pm-dispatch/references
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
| 22 | + | |
21 | 23 | | |
22 | 24 | | |
23 | 25 | | |
| |||
32 | 34 | | |
33 | 35 | | |
34 | 36 | | |
35 | | - | |
36 | | - | |
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
54 | | - | |
| 54 | + | |
55 | 55 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
12 | 18 | | |
13 | 19 | | |
14 | 20 | | |
| |||
21 | 27 | | |
22 | 28 | | |
23 | 29 | | |
24 | | - | |
25 | 30 | | |
26 | 31 | | |
27 | 32 | | |
| |||
33 | 38 | | |
34 | 39 | | |
35 | 40 | | |
36 | | - | |
37 | 41 | | |
38 | 42 | | |
39 | 43 | | |
| |||
48 | 52 | | |
49 | 53 | | |
50 | 54 | | |
51 | | - | |
52 | 55 | | |
53 | 56 | | |
54 | | - | |
55 | 57 | | |
56 | 58 | | |
57 | 59 | | |
| |||
73 | 75 | | |
74 | 76 | | |
75 | 77 | | |
76 | | - | |
77 | | - | |
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
| |||
0 commit comments