Skip to content

Commit 079deea

Browse files
pm(contract-review): name ④'s rules face as the governed register, scenario per surface (#26651)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi94Tjp2AWynvuMR9crB9WD2o)_ Part of #26527 (items 1 and 2; item 3, design judgment on the narrowing face, stays open for the maintainer's word and goes to the decision box after this PR) Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:契约复审模板里 ④ 设计判断那段说明,把规则面写成「受管登记表」而不是三个面;模板说明和复审简报都按文件类型写明验收场景(技能、schema、ADR 各一句)。 - 为什么改:PR #26514 的复审留下的两条 should-fix:规则面读起来像只有 spec、skills、ADR 三个面,简报只给 ADR 写了验收场景。 - 风险与回滚:只改 `scripts/pm/record-recognisers.mjs` 打印的提示文字和它的自测,不碰任何读取逻辑;回滚即 revert 这一个提交。 - 先看:`scripts/pm/record-recognisers.mjs` 的 `--template` ④ 说明段和 `contractReviewBriefLines` 的 ④ 段,再看两条新 pin。 - 两分钟验证:`node scripts/pm/record-recognisers.mjs --brief-template --face rules`,④ 段应出现 skill / schema / ADR 三句场景。 - 需要你做的:无,席位落地。 ## What changes - `--template` ④ note (`contractReviewTemplateLines`): the rules face now reads as "the published spec, and the governed register, which holds the skills and ADR surfaces the full-review order names among its rows" instead of "the published spec, skills and ADR surfaces". The governed register does not hold `packages/spec` (that is the schema face), so the card's suggested sentence was taken in the equivalent form the card allows, keeping the two faces apart. - Both the note and the reviewer brief (`contractReviewBriefLines`) now name the accepted scenario per surface: a skill file against the card's eval prompt and the spec it teaches on `main`; a schema file against the ruling or ADR it implements and its first consumer; an ADR against its first consumer and its execution plan. - The brief's docblock says the same. - Self-test: one new pin in the template battery (governed-register wording present, the three-surface wording absent, the skill and schema scenarios present) and one in the brief battery (the three per-surface scenarios). Battery floors 27 → 28 and 16 → 17, comment updated. No existing pin was edited; the pins on 'first consumer and its execution plan' and 'scenario it is accepted against' still hold unchanged. - `.claude/skills/pm-dispatch/references/contract-review.md` is NOT touched: its ④ line (L30) already names the schema and governed-rules-text faces with spec, skills and ADRs inside them, so it does not say "three surfaces". The diff touches no governed path. ## Tests - `node scripts/pm/record-recognisers.mjs --self-test`: 217 cases pass, exit 0, at `84e6125b`. - One-shot mutation of each new pin, trap-restored, restore checked byte-identical against the HEAD blob: - brief text back to "A skill file against the spec": exit 1, the new brief pin fails (1 of 217). - template text back to "the published spec, skills and ADR surfaces the": exit 1, the new template pin fails (1 of 217). - A first attempt at the brief leg anchored on text that occurs twice (source and pin), so the replace refused and nothing ran; it was re-anchored on the source line alone. ## Acceptance notes - Other rules-face rows (`AGENTS.md`, `.github/workflows/**`, `.claude/agents/**`, hooks) keep only the generic "the scenario it is accepted against"; the card names scenarios for skill, schema and ADR files only. ## Verification All at head `84e6125b` (after the last commit), in this worktree. <details><summary>dispatch-gates --commands: 31 derived, 31 run, 0 NOT-MEASURED, 0 UNRUN (DERIVED zero)</summary> ```text node scripts/check-closing-keyword-parity.mjs :: exit 0 node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 node scripts/check-comment-mask-corpus.mjs :: exit 0 node scripts/check-declaration-mirrors.mjs :: exit 0 node scripts/check-declaration-mirrors.mjs --self-test :: exit 0 node scripts/check-self-test-wired.mjs :: exit 0 node scripts/check-self-test-wired.mjs --self-test :: exit 0 node scripts/check-self-test-workflow-commands.mjs :: exit 0 node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0 node scripts/check-whole-set-label-write.mjs :: exit 0 node scripts/check-whole-set-label-write.mjs --self-test :: exit 0 node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0 node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0 pnpm check:agent-test-spelling :: exit 0 pnpm check:bash32-floor :: exit 0 pnpm check:ci-filter-parity :: exit 0 pnpm check:cli-command-ids :: exit 0 pnpm check:cross-package-test-inputs :: exit 0 pnpm check:driver-memory-census :: exit 0 pnpm check:entry-guard :: exit 0 pnpm check:gitlink-declared :: exit 0 pnpm check:nul-bytes :: exit 0 pnpm check:parse-guard :: exit 0 pnpm check:pm-record-recognisers :: exit 0 pnpm check:pnpm-filter-targets :: exit 0 pnpm check:pnpm-flag-spellings :: exit 0 pnpm check:ratchet-remedy-authority :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:scripts-symbol-anchors :: exit 0 pnpm check:watch-hint-literal :: exit 0 pnpm check:pm-skill-ratchet :: exit 0 pnpm check:pm-dispatch-gates :: exit 0 ``` `pnpm check:pm-skill-ratchet` is outside this diff's derivation (no `.claude/**` path) and was run because the card's acceptance names it. `pnpm check:pm-dispatch-gates`: fast tier, 1935 cases pass, 479.4 s. </details> CI families (lint, type check, tests) are the PR's check-runs; not re-run locally. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (not touched) - [x] 2. Every name I added uses the casing for its kind. (no new names) - [ ] 3. No formatter ran; `pnpm lint` passes. (no formatter ran; `pnpm lint` is CI's run, see check-runs) - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (publishes nothing: `scripts/pm/**` only, `skip-changeset`) - [x] 6. `packages/spec` touched: its generated artifacts are regenerated and committed. (not touched) - [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 维护者速读 block is the repo's required Chinese summary) - [ ] The *Before Submitting* list in CONTRIBUTING.md is done. (the local subset per the dispatch contract; the full suite is CI's) --- _Generated by [Claude Code](https://claude.ai/code/session_01ADHkmSpW72m8tVh1b1rPxQ)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent cabe2ed commit 079deea

1 file changed

Lines changed: 22 additions & 12 deletions

File tree

‎scripts/pm/record-recognisers.mjs‎

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,11 +1102,14 @@ export function contractReviewTemplateLines(values = {}) {
11021102
` below \`hits\`. The rest of a mixed diff is the lane seat's ACCEPT at ${TIER_DEFAULT_NAME},`,
11031103
' named there; Tier H approval and the queue guard stay whole-PR. On the narrowing',
11041104
' face the floor is every path of the diff outside the prose faces: no path is a remainder.',
1105-
' · ④ Design judgment on the schema and rules faces only — the published spec, skills and ADR surfaces the',
1106-
' full-review order names; the narrowing and declaration faces carry no ④. For each `hits`',
1107-
' file: does the change hold against the code it cites and the scenario it is accepted',
1108-
' against — an ADR against its first consumer and its execution plan? The cited code is',
1109-
' READ to judge it, never reviewed; a must-fix found here is a FAIL like any other.',
1105+
' · ④ Design judgment on the schema and rules faces only — the published spec, and the governed register,',
1106+
' which holds the skills and ADR surfaces the full-review order names among its rows;',
1107+
' the narrowing and declaration faces carry no ④. For each `hits` file: does the change',
1108+
' hold against the code it cites and the scenario it is accepted against? A skill file',
1109+
" against the card's eval prompt and the spec it teaches on `main`; a schema file",
1110+
' against the ruling or ADR it implements and its first consumer; an ADR against its',
1111+
' first consumer and its execution plan. The cited code is READ to judge it, never reviewed;',
1112+
' a must-fix found here is a FAIL like any other.',
11101113
' · Implemented-by the identity that produced the diff, read off the implementation claim:',
11111114
" a mode:subagent dev's BRANCH, a mode:remote dev's session id.",
11121115
' · Reviewed-by the session that RENDERS or ADOPTS the verdict — a session only. Prose',
@@ -1203,8 +1206,10 @@ export const BRIEF_TEMPLATE_PLACEHOLDERS = Object.freeze({
12031206
*
12041207
* ⭐ Item ④, on the faces whose record carries it (`recordCarriesDesignJudgment`): the brief
12051208
* hands the reviewer the design judgment as an ADVERSARIAL instruction — hold the change against
1206-
* the code it cites and the scenario it is accepted against, an ADR against its first consumer
1207-
* and its execution plan, and try to break it — and the surface-file scope rule the owner refined
1209+
* the code it cites and the scenario it is accepted against, named per surface (a skill file
1210+
* against the card's eval prompt and the spec it teaches, a schema file against the ruling or ADR
1211+
* it implements and its first consumer, an ADR against its first consumer and its execution
1212+
* plan), and try to break it — and the surface-file scope rule the owner refined
12081213
* the order with: ④ judges the `hits` files only; lockfiles, generated output, changesets and
12091214
* prose elsewhere in the PR are not reviewed, and the cited code is READ to judge them, never
12101215
* reviewed. The narrowing brief carries neither: its floor is the whole diff outside the prose
@@ -1267,8 +1272,10 @@ export function contractReviewBriefLines(values = {}) {
12671272
' ③ Boundary flags — every dev flag and every `open_questions` entry answered, or escalated.',
12681273
...(designJudgment ? [
12691274
' ④ Design judgment — for EVERY `hits` file: does the change hold against the code it cites and the',
1270-
' scenario it is accepted against — an ADR against its first consumer and its execution plan? Read the',
1271-
' cited code in the live tree and try to break the design; name each finding must-fix or should-fix.',
1275+
" scenario it is accepted against? A skill file against the card's eval prompt and the spec it teaches",
1276+
' on `main`; a schema file against the ruling or ADR it implements and its first consumer; an ADR',
1277+
' against its first consumer and its execution plan. Read the cited code in the live tree, and',
1278+
' try to break the design; name each finding must-fix or should-fix.',
12721279
" A must-fix is a FAIL. ⛔ Judge the tree, never the PR body's account of it.",
12731280
] : []),
12741281
"Sign `Implemented-by:` with the dev's branch and `Reviewed-by:` with your session id — the value is the FIRST",
@@ -1415,10 +1422,10 @@ const SELF_TEST_BATTERIES = Object.freeze({
14151422
'#18141: the head sha sits in a span of ITS OWN — the key-in-span spelling names no head': 7,
14161423
'the `Served-tier:` reading — the tier that SERVED the record (#17915 · #18060 · #18174)': 27,
14171424
'⭐ the 2026-09-20 ruling: a pure-regeneration head move KEEPS the record, decided on the COMMITTED trees': 22,
1418-
'#18042: the copyable record TEMPLATE — the one machine-read artefact with nothing to copy': 27,
1425+
'#18042: the copyable record TEMPLATE — the one machine-read artefact with nothing to copy': 28,
14191426
'#18701: ONE thread set -- what the template STATES is what the queue guard READS': 17,
14201427
'the `Local-runs:` reading — `none` or ONE named probe, judged when present; an absent line is not re-judged': 12,
1421-
'the reviewer BRIEF template — the read-only shape a hand-written brief dropped, built in and printed': 16,
1428+
'the reviewer BRIEF template — the read-only shape a hand-written brief dropped, built in and printed': 17,
14221429
'⭐ B′ with C: the review tier FOLLOWS THE FACE that made it owed — exact, never a floor': 23,
14231430
'⭐ B′ with C: a PROSE-ONLY head move keeps the record, decided on the COMMITTED trees like a regeneration': 6,
14241431
'⭐ letter C: the at-tier review of a mixed diff is SCOPED — `hits` plus what they claim, on the `Scope:` line': 18,
@@ -1432,7 +1439,8 @@ const SELF_TEST_BATTERIES = Object.freeze({
14321439
// Raised 11 → 12 with the letter-B narrowing-face battery (ruling record 6203713500): one battery, one row.
14331440
// Item ④ (the owner's full-review order, #25765) added no battery: the template battery's floor rose 19 → 27
14341441
// and the brief battery's 10 → 16, one pin per new printed line or face reading, so a pin that stops
1435-
// registering reds here instead of leaving the ④ lines unmeasured.
1442+
// registering reds here instead of leaving the ④ lines unmeasured. The per-surface scenario lines
1443+
// and the governed-register wording added one pin to each: 27 → 28 and 16 → 17.
14361444
const SELF_TEST_BATTERY_FLOOR = 12;
14371445

14381446
// The key an assertion is filed under when no battery is open. It is not a
@@ -1836,6 +1844,7 @@ export async function selfTest() {
18361844
t('⭐ the face table marks exactly the two path-read faces as full-review faces, every face answering — never a second list', REVIEW_FACES.filter((f) => f.fullReview === true).map((f) => f.id).join() === 'schema,rules' && REVIEW_FACES.every((f) => typeof f.fullReview === 'boolean') && recordCarriesDesignJudgment(null, CONTRACT_REVIEW_TIER_NAME) && !recordCarriesDesignJudgment(null, TIER_DEFAULT_NAME));
18371845
t('⛔ an unknown `faceId` is REFUSED, never printed as the bare record', (() => { try { contractReviewRecordLines({ faceId: 'lane' }); return false; } catch { return true; } })());
18381846
t('⭐ item ④ is prose to every reader — a record carrying it still reads FOUND on its head and its tier STANDS', (() => { const r = tplOn(contractReviewRecordLines({ headSha: TPL_REAL_HEAD, reviewedBy: 'session_01PINFOUR' }).join('\n'), TPL_REAL_HEAD); return r.state === 'found' && r.sha === TPL_REAL_HEAD && servedTierStands(r.served); })());
1847+
t('the notes name the rules face as the governed register, never as three surfaces, and give each surface its scenario: a skill its eval prompt and the spec it teaches, a schema its ruling or ADR and first consumer', ['the published spec, and the governed register,', 'which holds the skills and ADR surfaces the full-review order names', "against the card's eval prompt and the spec it teaches", 'against the ruling or ADR it implements and its first consumer'].every((frag) => says(TPL_TEXT, frag)) && !says(TPL_TEXT, 'the published spec, skills and ADR surfaces'));
18391848
t('the notes explain ④: which faces, judged against the cited code and the accepted scenario, an ADR against its first consumer, the cited code READ never reviewed', ['on the schema and rules faces only', 'against the code it cites', 'scenario it is accepted', 'first consumer and its execution plan', 'READ to judge it, never reviewed', 'narrowing and declaration faces carry no ④'].every((frag) => says(TPL_TEXT, frag)));
18401849
// The branch placeholder is ASSEMBLED in the module body so the derivation
18411850
// does not read it as a dead path population. This pin spells the printed
@@ -1875,6 +1884,7 @@ export async function selfTest() {
18751884
t('…and the usage names it', usageLines().join('\n').includes('--brief-template'));
18761885
// -- item ④ in the brief: the adversarial design-judgment instruction and the surface-file scope rule --
18771886
t('⭐ the brief carries the ④ instruction, adversarial: hold against the code it cites and the accepted scenario, an ADR against its first consumer and execution plan, try to break it, a must-fix is a FAIL', ['④ Design judgment', 'against the code it cites', 'scenario it is accepted against', 'first consumer and its execution plan', 'try to break the design', 'must-fix or should-fix', 'A must-fix is a FAIL', 'Judge the tree'].every((frag) => says(BRIEF_BODY, frag)));
1887+
t('…and names the accepted scenario per surface, not for an ADR alone: a skill file against the card\'s eval prompt and the spec it teaches, a schema file against its ruling or ADR and its first consumer', ["A skill file against the card's eval prompt and the spec it teaches", 'a schema file against the ruling or ADR it implements and its first consumer', 'an ADR', 'against its first consumer and its execution plan'].every((frag) => says(BRIEF_BODY, frag)));
18781888
t('…and the surface-file scope rule: ④ judges the `hits` files ONLY; lockfiles, generated output, changesets and prose elsewhere are not reviewed; the cited code is READ, never reviewed', ['④ judges the `hits` files ONLY', 'lockfiles, generated output, changesets and prose', 'not reviewed', 'READ to judge them, never reviewed'].every((frag) => says(BRIEF_BODY, frag)));
18791889
t('…on the schema and rules faces and over a mixed path list alike', ['schema', 'rules'].every((id) => contractReviewBriefLines({ face: id }).join('\n').includes('④ Design judgment')) && contractReviewBriefLines({ paths: MIXED }).join('\n').includes('④ judges the `hits` files ONLY'));
18801890
t('⛔ the narrowing brief carries no ④ and no surface-file rule — its floor is the whole diff outside the prose faces', (() => { const b = contractReviewBriefLines({ face: 'narrowing' }).join('\n'); return !b.includes('④') && !b.includes('files ONLY'); })());

0 commit comments

Comments
 (0)