Skip to content

Commit bdb00ff

Browse files
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/contract-review.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
- 已发布面以包的 `exports` 映射为准:从入口类型图不可达、且 `exports` 不可寻址的 `.d.ts` 是
1919
出货字节,⛔ 不是已发布接受集,改它不触发 `Clause-②: yes`;legacy node10 深引用在契约之外。
2020
- 四面皆不碰 ⇒ CI 加席位自读 ⛔ 不起第二个 agent;谁跑 = 派发席,达档者席内审。
21+
- 派发席达档调用被配额拒(HTTP 429)⇒ ⛔ 不降档审;PR 落交接行、留等复核标记,交分诊席。
22+
- `Contract-review-handoff: triage — CONTRACT_REVIEW_TIER unavailable (<HTTP status / error>, <UTC time>) · head <sha>`
2123
- 复核记录 = 一条评论落 PR 或卡,席内与子代理同形;散文、dev 自评、`os-dev-report` 恒不算。
2224
- 同形 = `## Contract review` 题头、head sha 独占码段、①②③ 逐项(申报面外三面加 ④)、
2325
独立性对、PASS/FAIL。
@@ -32,8 +34,6 @@
3234
被引代码读而不审;①–④ 合称完整复核,余面同形无 ④,⛔ 无第二条记录。
3335
- 实质 head 各欠一轮完整复核;只落发现的 commit 由同一复核者 delta 复核;无 must-fix 即止。
3436
- 另记作者:PR `user.login` 非 `objectstack-fleet[bot]` ⇒ ③ 注点名该 login(脱中继写),落地前读。
35-
- 复核形状:只读 diff 与卡片,check 结论取 head 的 check-runs,⛔ 永不本地重跑派生门禁族。
36-
- 只喂卡片、既有裁决与 PR 本体,⛔ 不喂派发令与派发席自己的结论;简报写成对抗性。
3737
- 简报从 `node scripts/pm/record-recognisers.mjs --brief-template` 起稿:
3838
只读条款与输入集内建,⛔ 不删 ⛔ 行。
3939
- 隔离复核子代理暂存全写按所审 PR 命名的 `<scratchpad>/pr-<n>/`,⛔ 不读非本轮自写的暂存。
@@ -51,5 +51,5 @@
5151
- 只作等复核标记,⛔ 不是闸门;单载体 = PR,卡上副本在规则外、不是要求。
5252
- 挂:ACCEPT 时条款②任一肢命中而现 head 无同形 PASS 在案 ⇒ 派发席同笔挂于 PR。
5353
- 摘:现 head 同形 PASS 在案 ⇒ 写记录的席同笔摘;PR 合并或关闭 ⇒ 派发席摘;FAIL 不摘。
54-
- 读者 = 维护者过滤 `is:pr is:open label:needs:contract-review` 与席位巡查、交接。
54+
- 只带标记无交接行者仍归本席,分诊 ⛔ 不碰;交接后 head 再动 ⇒ 新交接行或本席自审。
5555
- ⛔ 无 check、workflow、队列守卫或巡查脚本读它;入队只认同形记录,标记有无不改判。

‎.claude/skills/pm-dispatch/references/triage-duties.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
- 查重缓存住席内:新会话全量拉,其后 `since` 增量;⛔ 不建分支、不共享、不依赖备份。
1010
- 每日一 fire 跑两仓全量对账并归集日频职责;选层按 fire 时刻 ⛔ 不用计数器,简报写明层。
1111
- 每条枚举比对返回数与 `totalCount`,不等 ⇒ 报 `sweep INCOMPLETE` 点名缺口,⛔ 永不报干净。
12+
- 复审交接:每轮列最新交接行点名现 head 的 open PR,隔离子代理达档跑同形记录、署本席。
13+
- 本席也被配额拒 ⇒ PR 写一次、留标,⛔ 永不降档审;交接行读法 `record-recognisers.mjs`。
14+
- 每轮比对自审记录 `Head-sha` 与现 head,越过即过期,纯重生成/只动散文面例外(判已提交树)。
15+
- 自审 FAIL 过期 ⇒ 同轮达档重审新 head,逐条复查 must-fix,delta commit 当改动审。
16+
- 自审 PASS 过期 ⇒ PR 记一条 PASS 不再管辖、欠 delta 复审,归本席或新交接行。
17+
- 轮次记录逐张自审 PR 写 `记录 head → 现 head → 状态`(现行/过期已重审/过期已记)。
1218
- Backlog sweep 是常设职责,每 fire 扫任一析取命中的卡。
1319
- 析取 ①:全裸(无 `pm:*`、无 `needs-user-decision`、无 `domain:*`)。
1420
- 析取 ②:有 `pm:queue` 无 `domain:*`(仅多车道仓);析取 ③:有 `domain:*` 无 pm-state。
@@ -21,7 +27,6 @@
2127
- 排序:队列每轮清空时最新优先;清不空的那一刻起改最老优先。
2228
- 裸卡数 >15 ⇒ 下一 fire 整轮改域分批集中模式,每批 ≤5 张同族卡,照样跑到清空。
2329
- 紧急卡直接分诊:维护者点名或 p0 嫌疑 ⇒ 立即起默认判断档分诊子代理,不等定时轮。
24-
- 紧急分诊的授权面 = 分诊本身,⛔ 不写码不认领;产出落卡,与定时轮分诊同格式同效力。
2530
- main-red 锚卡由席位先立先派,首触事后补作记录级:可改域,⛔ 不改动作,在飞照补。
2631
- 分类动作每张三选一,外加一个修复通道;裁定写方向、给六态之一。
2732
- 方向在 ADR/本卡裁决/不可重裁规则:贴 `check-prior-rulings.mjs` 的 `Prior rulings read:` 行,或不点。
@@ -33,7 +38,6 @@
3338
- 碰迁移形状或删已发布能力也进决策箱;说明书脱节不进:有裁定出处才是 (b),否则改文档。
3439
- 决策卡速读居四棱块之上、同题,用业务语言先讲事情与选项,末句只问一字:A/B/C 或是否。
3540
- 首行写立卡门类别 ①–④;四类定义、⛔ 清单、四答、理由判据与配额见 `filing-gate.md`。
36-
- 首触先按业务理由三出口分流,再定级路由;批量卡审计评论带 `Batch-of:` 行。
3741
- `finding` = 三类内待定级:(a) 可复现缺陷(复现或失败探针);(b) 违背已声明契约(引契约文);
3842
- (c) AI 写元数据会被运行时拒收或静默丢弃的陷阱;其余进 PR `## Acceptance notes`,⛔ 不立卡。
3943
- (a) 的分界是不完整 vs 错误,不是文档 vs 代码:示例照抄即失败是 (a),漏列成员不是。
@@ -48,10 +52,8 @@
4852
- 判据:违背已声明契约 ⇒ `Bug`,扩大接受集/公开面越出已声明 ⇒ `Feature`,其余 ⇒ `Task`。
4953
- 定级按北极星「优先级」1–3 条;首行 `Path: <定义项> | <清单项 id/缺项> | P<路步> | none`。
5054
- 定义项取北极星「做出来的是什么」或仪器卡的车队决定;无则关;`none` 非分诊缺口。
51-
- 清单项三态定级(会 fail / 步骤够不到 / 无项断言)与定级判据见 `references/filing-gate.md`。
5255
- 「无则关」= 首触即关 not_planned,带理由与入队三门,⛔ 不定 p3、不 hold;下行同此。
5356
- 所入车道队列 P0/P1 开着(= 未派发,在飞不算)时,三门皆无的 p2/p3 `tooling` 卡同样关。
54-
- 只报告仪器(放宽 tell、半状态巡查)的修复卡首触即关 not_planned,引裁决 208;在途只有删除。
5557
- 路由是分诊的技术判断,⛔ 永不升级哪个仓的问题;父单是协调节点,永不派发。
5658
- 父单已有子结构的:父单队列标签即可,分诊逐个展开路由、补 `Blocked-by:` 排序。
5759
- 跨层功能点(≥2 层)立父单带 `area:*`+`pm:epic`+清单项级,逐层子单按 `Blocked-by:` 排。
@@ -73,8 +75,6 @@
7375
- 在飞活性复查:认领逾一天无自有产出(commit/PR/评论)⇒ 敲门一次,首行键 `Owned-24h re-check:`。
7476
- 敲门后 `RECLAIM_AFTER_HOURS = 12` 小时无答复 ⇒ `label-write.mjs --release` 一笔中继释放,贴不动。
7577
- 释放:`Release:` 评论(出处三件)+ `Resume-from:` 行 + `pm:dispatched`→`pm:queue` + 清 assignee。
76-
- 车道席可附证据/前提重验,⛔ 不定级,改标限席改标;每张卡欠一次分诊首触,⛔ 不自分诊。
77-
- 域分批与 sweep 打包晋级五条照用;每批约定与积压告警见 `references/dispatch-runbook.md`。
7878
- `pm:retriage` 每 fire 先答所求、同笔摘标;须维护者答的入箱;既裁砍项与席改标退回执行席。
7979
- retriage 的异议判据认内容(有评论写明所求)⛔ 不认形状(有一条异议评论)。
8080
- 人工地板(恒呈维护者):功能新增;ADR;协议/公开契约变化;破坏性或难回滚动作。

0 commit comments

Comments
 (0)