Skip to content

pm-dispatch: name the review of record for default-tier lanes, make clearing cite it, and read it in check-clause2-carriers --pair (C6) - #17349

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-17302-review-of-record
Sep 10, 2026
Merged

pm-dispatch: name the review of record for default-tier lanes, make clearing cite it, and read it in check-clause2-carriers --pair (C6)#17349
os-zhuang merged 2 commits into
mainfrom
claude/issue-17302-review-of-record

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #17302

Summary

After the tier policy landed, the rule text said the lane seat's own default-tier review "plus the gates" is the review of record for every lane but spec and skills — and named neither where that review lives nor what it must contain. This PR names it, makes every clearing of needs:contract-review cite it, and teaches check-clause2-carriers --pair to read "a review of record exists on this head" as a row of its own (C6). Three governed rule files are rewritten in place at their ceilings (812 / 60 / 151, net zero); one scripts/pm gate gains one row, one battery and one read.

The finding's evidence (quoted from #17302)

PR merged by carrier review of record
objectstack#17181 (card #15951) 2026-09-10T00:03Z (before the notice) os-steve never hung none — PR body reasons yes in prose, no line-start declaration, no card-side verdict
objectui#8921 (card #8900) 03:11Z os-zhuang hung, cleared none
objectui#8922 (no card) 03:15Z os-zhuang hung, cleared none
objectui#8926 (card #8897) 03:15Z os-zhuang hung, cleared none
objectui#8931 (card #8268) 03:28Z os-zhuang hung, cleared none

Card #15951's thread, read for this PR: a Claim:, three dispatch-failure notes, the dev's os-dev-report, a PM correction, and 「Closing by hand — PR #17181 merged as 6058cb2」 — no comment on either carrier that names a reviewer. Ask 3 is ruled on the card (the five merges are accepted under the transition) and nothing here touches them.

The review of record — the shape, spelled once

ONE comment on the PR or on its card, on the merged/landing head, carrying:

  1. a level-2 heading whose line begins ## Contract review (H51's marker in check-half-states.mjs, measured over four live dialects — the same heading may sit on a later line of a director adoption record);
  2. the reviewed head sha as a code span (H51's contractReviewHeadMatch: a 7–40 hex span that is a prefix of the PR's current head);
  3. the checklist's ①②③ as line items — derived judgments / semver / boundary flags;
  4. the independence pair, Implemented-by: and Reviewed-by:;
  5. the verdict word, PASS or FAIL.

That is the tier verdict's shape minus the tier line. Measured on the board this PR was written against, every 2026-09-09 tier verdict already satisfies it (PR #17073 comments 5597841101 and 5600239551, PR #17116 comment 5600627944, PR #17090 comment 5598904803: heading, head span, **Reviewed-by:**). A bare os-dev-report never does, nor does the implementer's own comment (the implementer is not the reviewer). ⚠️ The skills seat's own ACCEPT comments on cards #17285 / #17288 / #17308 (5613072104, 5614148443, 5614255952) carry the head and a Reviewed-by: line under a bold first line and NO heading — under this text they are not a record either, and the seat's next clearing must write the heading. The C6 self-test pins that exact shape as a control.

What the gate READS is items 1, 2 and the Reviewed-by: key line of item 4 — never the verdict word (the file's standing boundary: reading a verdict to clear a gate is 自查放行; H51 is verdict-agnostic by construction; and the live corpus already spells the word two ways — a fenced VERDICT: PASS on the 2026-09-01 board, **Verdict: PASS WITH FINDINGS** on the 2026-09-09 board — so a regex for it would be a third spelling), and never the ①②③ items (prose the seat reads). Exit 0 therefore means "a review of record exists on this head and names a reviewer", never "the review passed"; precondition ① of the landing check stays human.

Before / after, per changed line, beside the ask it implements

Every edited line is ≤ 119 bytes (the ratchet's 120-byte cap); the three files stay at 812 / 60 / 151.

Ask 1 — name the review of record (contract-review.md, twinned in SKILL.md and core-rules)

references/contract-review.md — one line was freed inside the file so the shape could take two lines:

  • :21 before 「- 重挂前先查裁决:闸门标签缺失 ⇒ 先 grep 卡评论找复审结论。」 → after 「- 重挂前先查裁决:闸门标签缺失 ⇒ 先 grep 卡评论找复审结论;get_reviews 读空 ≠ 未复审。」 (118 B; absorbs old :23)
  • old :23 「- get_reviews 读空 ≠ 未复审。」 → retired (folded into :21 above); :24–:29 renumber by −1, so the dispatch's ":28" is now :27.
  • old :29 「- 契约判断清单逐项落卡或 PR 评论,⛔ 不是散文自述。」 → new :28 「- 复核记录 = 一条评论落 PR 或卡,达档与默认档同形;散文、dev 自评、os-dev-report 恒不算。」 (118 B) + new :29 「- 同形 = ## Contract review 题头、所审 head sha 码段、①②③ 逐项、独立性对、PASS/FAIL 判词。」 (119 B)
  • :41 before 「- 落地前检三条:① 席内条款②复核 PASS 在案,即卡上的复核裁决评论(spec 席达档)。」 → after 「- 落地前检三条:① 席内条款②复核 PASS 在案,即 PR 或卡上同形的复核记录(spec 席达档)。」 (115 B)

SKILL.md :641 before 「- 交付后复核由派发席席内完成:spec 席达档自审或派该档子任务,余席默认档自审加门禁。」 → after 「- 交付后复核由派发席席内完成:spec 席达档,余席默认档加门禁;记录 = 同形评论落 PR 或卡。」 (119 B)

references/core-rules.md :123 before 「- 受管面 PR 留 draft 并向两个授权批准账户请审;无席内复核 PASS 的契约卡 ⛔ 禁止入队。」 → after 「- 受管面 PR 留 draft 并向两个授权批准账户请审;契约卡无同形复核记录 PASS ⛔ 禁止入队。」 (117 B)

Ask 2 — clearing the carrier cites it

  • contract-review.md :38 before 「- 放行 = 清标即落地:PASS ⇒ 同席同笔剥双载体,清标同笔留 provenance 评论引该 PASS。」 → after 「- 清标即落地:PASS ⇒ 同席同笔剥双载体;凡清标同笔留 provenance 评论,引记录 id 与所判 head。」 (119 B) — every clear, PASS or FAIL (:20 already clears both on FAIL), names the record comment and the head it judged.
  • contract-review.md :22 before 「- PASS + 无标 + head 未动 = 已清标不是被剥;head 后移或无结论才重挂。」 → after 「…;head 后移或无结论才重挂;清标缺引记录即半态。」 (117 B) — clearing without the citation is the half-state the sweep reads (H51/H53 are A timestamp a seat writes into GitHub must come from the clock read by the same act that writes it — make an estimated stamp unspellable (helper token and/or a report-only patrol row), after two recorded ~1h mis-estimates #17314's file and are not touched here).
  • contract-review.md :43 before 「- 该命令 0 = 双肢一致且无放宽 tell,4 = 任一不成立,3 = 环境答不了;⛔ 3 不作干净。」 → after 「- 0 = 双肢一致、无放宽 tell、head 上有记录;4 = 任一不成立;3 = 环境答不了,⛔ 不作干净。」 (115 B) — the --pair contract now includes the record.
  • SKILL.md :642 before 「- PASS ⇒ 同席剥标、ready、auto-merge;FAIL ⇒ 补丁轮;⛔ 免复核不放行。」 → after 「- PASS ⇒ 同席剥标并引记录、ready、auto-merge;FAIL ⇒ 补丁轮;⛔ 免复核不放行。」 (101 B)

Ask 3 — check-clause2-carriers --pair reads it (row C6)

scripts/pm/check-clause2-carriers.mjs:

  • needsRecordRead(pair) — owed by the COMPLETED state only (gateBindingState unchanged: declared yes, cleared on both carriers, head unmoved). A pending pair owes nothing yet; never-hung / half-bound / moved-after-clear stay C3's rows, so no fact is owned twice.
  • reviewOfRecord(pair) — reads the PR's thread and the card's; the newest ## Contract review comment naming the current head (H51's CONTRACT_REVIEW_HEADING_MARKER + contractReviewHeadMatch + latestMarkedComment, all imported from check-half-states.mjs — no cycle: that file imports nothing from this one, verified by grep) that also carries a Reviewed-by: line (C4's own AUTHORSHIP_KEY_LINES regex, key presence only — the value is C4's row). States: not-owed / unreadable / absent / unsigned / found.
  • c6NoReviewOfRecord(pair) — the FINDING row (absent, or unsigned naming the comment and the missing line); c6RecordNote(pair) — the C6-RECORD note on found, naming the comment id, thread and head span so the provenance comment can cite it.
  • gather — a fourth pass buys the PR's own thread for completed pairs only, in BOTH modes, through the existing readCardComments (a PR is an issue at /issues/N/comments), cached per PR; the offline document carries it in the same comments bag keyed by the PR number — no new field, no seventh reader method.
  • pairUnjudged — an unread PR thread (or a head sha too short to match) on a completed pair is UNJUDGED (exit 2), never a missing record.
  • renderPair — the success sentence is keyed by note CODE, not note count (the C6 note had otherwise borrowed the C2-SIBLING wording — caught by the on-time fixture run and fixed in the second commit); a found record is named in the sentence.
  • Header docblock: a C6 section, the pair-json shape, the request budget (a completed pair adds one read), the exit-2 and exit-4 rows. Self-test: one new battery (40 cases: the cost bound, the measured record on either carrier, H51's dialects, the two controls — os-dev-report and the seat's headingless ACCEPT — the unsigned state, newest-governs, the check:react-declaration-parity 是唯一没接进任何 workflow 的源码审计门禁,且无 MANIFEST 时静默 skip 退出 0 —— 它现在永远不可能红 #4690 half, reporting order, and the offline bag), roster +1, floor 15 → 16; the [finding] check-clause2-carriers.mjs --pair answers C3 / exit 4 on a LEGITIMATELY cleared clause-② pair — the completed state (declaration outlives the label) is indistinguishable from the fail-open it hunts #14155 completed fixture now carries its head (9af92aa3) and its record on the PR thread, so 「the pair is CLEAN overall」 still holds and the landing check's ② still answers 0 after a legitimate clear.

Hard-4, not advisory — the decision and why. The seat's reading was "advisory until the rule text has been live for a window"; the file's own exit table decides otherwise: --pair is a predicate about THIS pair at its own landing moment, an adverse fact about it exits 4, and 「⛔ never 0-with-a-message: silence is what this whole file exists against」. C6 re-blocks no legal workflow — under the text the record precedes the clear, so a pair that followed it reads clean — and a pair cleared before the text landed owes exactly one comment (the review its seat already performed, written down) before --pair answers 0. The sweep stays report-only (rows print, exit 0), so the board-wide transition costs nothing. A window would also be the staged option the decision frame rules out by default (「过渡也从紧 —— 创业阶段不渐进」).

Offline runs on the final tree (07887a781), the same document three ways (--pair 13864 --pair-json …):

维护者速读(草稿)

改了什么 —— 三个协议文件各改几行、净零行:契约复核细则把「默认档车道的复核记录」定成一条评论(落 PR 或卡,形状 = 达档裁决同形去掉档位行),并要求每次清标的 provenance 评论引这条记录的 id 与所判 head;主文件与核心条款各改孪生行。check-clause2-carriers --pair 新增 C6 行:对已清标的条款②对子,读 PR 与卡两条线程找这条记录,没有就退出 4;找到就打印一条 NOTE 告诉席位该引哪条评论。

为什么改 —— 总监席的漏网扫描在一个窗口里量到五个 Clause-②: yes 合并没有任何复核记录,只有 dev 自己的报告。规则文本(本席上午落地的档位政策)说「车道席默认档自审加门禁就是复核记录」,却没说它住哪、长什么样,于是「清了标」和「没有审」在任何扫描里看起来一样,总监席的事后审计也没有可核对的对象。

风险与代价(含回滚) —— 代价:每次清标多写一条评论(把已经做过的复核写下来)、--pair 对已清标对子多一次读;过渡期内已清标未落地的对子跑 --pair 会红一次,补一条记录即绿;本席自己现行的 ACCEPT 评论没带 ## Contract review 题头,自下一次清标起须写成同形。风险:识别形状复用 H51 已量过的四种方言,不读裁决词、不读 ①②③;若某席写出第五种方言,C6 会报「无记录」(红而不是绿,fail-closed)。回滚:一次 revert,无发布物、无迁移。

席位意见 ——

你要做的 —— 人工合并本 draft PR(受管面 .claude/** + scripts/pm/**,席位不翻 ready、不入队、不批准)。

Acceptance notes

Gates (every command dispatch-gates.mjs --commands derived from the merge-base changeset, run on the final commit 07887a781, exits captured before any pipe; the ledger is in the os-dev-report):

  • pnpm check:pm-skill-ratchet exit 0 — 「✓ check-skill-line-ratchet: declared cross-file moves: 1, total ceilings down 9 lines.」 (812 / 60 / 151 at ceiling, headroom 0, none raised)
  • pnpm check:skill-frame-sync exit 0 — 「✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files」; SKILL.md :734–:752 md5 22f2339f0acb64cdb50c7adc9db681c3 unchanged
  • pnpm check:skill-compatibility exit 0 — 「✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 80 workspace packages」
  • pnpm check:pm-skill-id-lint exit 0 — 「✓ check-skill-id-lint: 27 file(s) clean (pattern /#[0-9]{3,}/g).」 (no issue number, model name or date in any skill file)
  • pnpm check:pm-governed-prose exit 0 — 「✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces … and claim no others.」
  • pnpm check:nul-bytes exit 0 — 「check-nul-bytes: OK (scanned 8170 text file(s) -- 8170 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).」
  • pnpm check:pm-clause2-carriers (the self-test) exit 0 — 「✓ check-clause2-carriers self-test: 359 cases pass (… the review of record on the completed state with the two shapes that are not one …)」
  • pnpm check:pm-dispatch-gates exit 0 (710 s, detached + tail --pid) — 「✓ dispatch-gates self-test: 1674 cases pass.」
  • pnpm check:pm-governed-merges exit 0 — 「✓ check-governed-merges --self-test: 274 assertions …」 and the report-only sweep
  • node scripts/pm/check-governed-merges.mjs --test … exit 3 — 「⛔ GOVERNED — a human merge is the review record for this PR (governance: human merge IS the review record for governed surfaces — retire the per-PR ADR approval check, add a report-only post-merge audit #9495 regime). … .claude/** ×3」; scripts/pm/check-clause2-carriers.mjs reported as not on the register
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions first exit 3 PREREQUISITE NOT MET (@objectstack/formula / @objectstack/lint not built — not a measurement); built both under the verify lock (「VERDICT command-exit 0 · held the lock 165s」) and re-ran: exit 0 — 「✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 440 files / 1374 TS blocks judged clean」
  • the remaining 32 derived commands (reference-carrier-shape, changeset-no-major --base origin/main + self-test, ci-filter-parity, closing-keyword-parity, comment-mask-corpus, declaration-mirrors, scripts-symbol-anchors, self-test-wired, self-test-workflow-commands, whole-set-label-write, bare-root-worklist, governed-queue-guard, report-test-timings, agent-test-spelling, bash32-floor, changeset-gate-self-tests, cli-command-ids, cross-package-test-inputs, doc-authoring, driver-memory-census, entry-guard, parse-guard, pnpm-filter-targets, ratchet-remedy-authority, refd-timer-probe, watch-hint-literal) all exit 0
  • dispatch-gates.mjs --ran reconciliation: Run reconciliation — 43 derived, 43 run, 0 NOT-MEASURED, 0 UNRUN. (recorded as command :: exit code, exits captured before any pipe; 43 derived, 43 run)
  • Rule ⑤ (the gate script's own suite): its --self-test above; the one importer (scripts/check-changeset-no-major.mjs, readClause2Line) ran its self-test and its --base origin/main run green; lint.yml's step runs the self-test only, unchanged.
  • No package source touched ⇒ no build closure, no package tests owed.

Ceilings arithmetic: contract-review.md 60 → 60 (−1 old :23 folded into :21, −1 old :29 replaced, +2 new :28/:29); SKILL.md 812 → 812 (two lines rewritten in place); core-rules.md 151 → 151 (one line in place). Widest edited line 119 B against the 120-byte cap.

Assumptions A–D, measured

Declared deviations from the dispatch text: (1) C6 reads no verdict WORD — H51's heading + head-sha shape and the Reviewed-by: key line instead (evidence above; the rule text still names the word for the seat); (2) hard-4 rather than advisory (reasoning above); (3) the live --pair run is on this PR, whose card declares Clause-②: no — it exercises the read path and C5, not C6 (no completed-state pair was enumerated on the live board; a sweep costs the anonymous budget); the C6 reading is exercised by the three offline documents and 40 self-test cases; (4) contract-review.md :23–:29 renumber by −1.

Out of scope, filed: #17346 — C4's VERDICT: discriminator and session-only Reviewed-by: grammar match none of the four 2026-09-09 verdicts (readVerdictAuthorship(body) === null on each), so the independence carrier has no live population; C6 deliberately reads the key line only and is not blocked by it. Noted, not filed: the skills seat's own ACCEPT comments lack the heading — the rule text now names it, 承接者 = the skills seat at its next clearing.

Changeset: none owed — nothing in any package's files[] moves (.claude/** and scripts/pm/** are the fast lane); skip-changeset applied by the dev per the standing contract. Governed: draft only, never ready / enqueued / auto-merged / approved by a seat.

Generated by Claude Code


Generated by Claude Code

…--pair reads it (C6)

contract-review.md names the default-tier lanes' review of record — one comment
on the PR or its card in the tier verdict's shape minus the tier line — and
makes every clear cite it; SKILL.md and core-rules carry the twin lines.
check-clause2-carriers --pair gains C6: on a completed pair it reads the PR's
thread and the card's for that record (H51's heading + head-sha shape, plus a
Reviewed-by: line) and answers 4 when none exists.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ
…e count

The C6-RECORD note made renderPair print the C2-SIBLING wording; each note
kind now speaks for itself, and a found record is named in the sentence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ
@os-justin os-justin added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 10, 2026 — with Claude
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

维护者速读

改了什么 —— 给默认档车道的「复核记录」定一个具名的载体和形状:一条评论落在 PR 或卡上,带 ## Contract review 题头、所审 head sha、①②③ 三项逐条、Implemented-by: / Reviewed-by: 独立性对、PASS/FAIL 判词——和契约复审档裁决同形,只少一行档位。清 needs:contract-review 标签时必须引这条记录的 id 与所判 head,缺引即半态。check-clause2-carriers --pair 加一行 C6:条款②已声明、双载体已清、head 未动的对子,读 PR 与卡两条线程找这条记录,找不到 exit 4,线程读不到 exit 2(未判,不算干净)。SKILL.md 两行、契约复核细则七行、核心条款一行,全部净零。

为什么改 —— 09-10 档位改制后,默认档车道的复核不再经契约复审档,通知里说「席位自审加门禁就是复核记录」,却没说记录长什么样、放在哪。总监席一扫:一个窗口里五次条款②合并没有任何复核记录,清标和从未复核在板上分不开,事后审计也无从审起。这是本席通知的漏洞,这张卡补上。

风险与代价(含回滚) —— 不进发布包。C6 是硬 4(该文件的退出码表里,对子上任何一个不利事实都是 4,不允许 0 带口信),只在「已完成」态才触发,不会重新挡住任何合法流程;巡查行仍只报告。代价:清标多写一行引用;各席 ACCEPT 评论要带题头——本席自己之前的 ACCEPT 就没带题头(dev 在板上量了:不算记录),从本 PR 的验收评论起改用同形。回滚 = revert 一个提交。

席位意见 —— 本席按契约复审档自审通过:三个技能文件的行与两条 ask 逐句对得上;C6 的识别形状复用 H51 的题头 + head 码段加 Reviewed-by: 键行,不读判词(该文件禁止自查放行,且判词在板上有两种拼法),本席认可 dev 的选择;三份离线夹具(有记录 0 / 无记录 4 / 线程缺 2)与 40 条自测在案,本席重跑自测 359 例通过,棘轮 812/60/151 全在天花板净零,决策框架块未动。dev 顺手立了 #17346(C4 对 09-09 的裁决评论静默)——裸卡,等分诊。

你要做的 —— 人工合并本 draft PR(受管面 .claude/**)。合入后各席位的条款②复核评论要带 ## Contract review 题头。


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation needs-user-decision size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants