Repository navigation
Commit c7d6ca8
pm-dispatch: a published schema, skill or ADR PR owes a full review — item ④ design judgment in the contract-review record, brief and rule (#26514)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8EtbiFoQ2oh3NQHXsUyoiwP)_
Fixes #25765
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:契约复核在 ①②③ 之外加第 ④ 项「设计判断」:凡 PR 已提交树碰到已发布
schema(`packages/spec/src/**` 非测试)、`skills/**` 与
`.claude/skills/**`、`docs/adr/**` 三面,席位 ACCEPT 前欠的达档复核就是完整复核 ——
逐个面文件判改动对它所引的代码与验收场景是否成立(ADR 另判首个消费者与执行计划),只审面文件、被引代码读而不审,由起草者之外的 agent
在契约档跑,每个实质 head 一轮,只落发现的 commit 由同一复核者 delta 复核,无 must-fix 即止。记录仍是同一条
`## Contract review` 评论(多一个 ④ 节,⛔ 不另开第二条记录);`--template` 打出 ④
节与说明,`--brief-template` 打出对抗性的 ④ 指令与「只审面文件」规则。
- **为什么改**:项目负责人 2026-10-25 下令 spec、skills、ADR 的改动都要在契约档做完整复核,10:20Z
补充「只针对 PR 中的重要文件」(原话逐字在卡 #25765 正文与评论 6396446237,本文不改写)。卡上实测:一张 ADR PR
两次契约复核 PASS 之后,独立完整复核仍找出三处设计缺陷,全部被负责人采纳 —— 契约复核读的是 diff
对裁决,从不问设计对首个消费者成不成立。
- **风险与回滚**:只改 `.claude/skills/pm-dispatch/` 两个文件与
`scripts/pm/record-recognisers.mjs`;无新门禁、无发布面、无 changeset。行数 49→55(上限
60)、319→319(上限 319,同行改写);脚本自测 201→215 例。风险在成本:每张三面 PR 多一轮达档复核(卡上实测每轮
10–18 分钟);以及 ④ 只挂 schema 与 governed 两面、收窄面不挂,若负责人要把收窄面也纳入需另下令。回滚 =
revert 本 PR。
- **先看**:① `references/contract-review.md` 第 29–32 行(④
定义、只审面文件、cadence)与第 40 行(独立性);② `scripts/pm/record-recognisers.mjs`
`contractReviewBriefLines` 的两处 ④ 段;③ `SKILL.md` 第 190 行指针行的后半句。
- **两分钟验证**:`node scripts/pm/record-recognisers.mjs --brief-template |
grep -n ④` 看到「④ judges the hits files ONLY」与「④ Design judgment」两段;`node
scripts/pm/record-recognisers.mjs --brief-template --face narrowing |
grep -c ④` 打印 0;`pnpm check:pm-record-recognisers` 打印 215 cases pass。
- **需要你做的**:无,席位落地(Tier S:`.claude/**` + `scripts/pm/**`,契约复核 PASS 与席位
ACCEPT 后由席位 ready → 入队)。
## What changes
Items 1–6 of the card (「What to change」 1–4 in the body, 5–6 in comment
6396446237), as one PR of three files. The tier is spelled everywhere by
its constant name `CONTRACT_REVIEW_TIER`.
### Faces — measured, nothing added
The card asks whether `skills/**` and `docs/adr/**` already fall under
SKILL.md's governed-rules-text definition. They do: SKILL.md 〈路径分叉〉
「governed 面统一定义」 lists `docs/adr/**` + `.claude/**`(全量) + `skills/**` +
`docs/NORTH-STAR.md` + `.github/workflows/**`, and the rules face in
`record-recognisers.mjs` reads the governed register
(`governedPathsIn`). The published-schema face is `packages/spec/src/**`
minus test files (`SUSPECT_TIER_GLOBS`). Read on `origin/main` at
`00a4593a` with `node scripts/pm/record-recognisers.mjs --template
--paths PATH`:
| path | `Served-tier:` | `Scope:` hit |
|:--|:--|:--|
| `docs/adr/0141-x.md` | `CONTRACT_REVIEW_TIER` | yes (rules face) |
| `skills/objectstack-data/SKILL.md` | `CONTRACT_REVIEW_TIER` | yes
(rules face) |
| `.claude/skills/pm-dispatch/SKILL.md` | `CONTRACT_REVIEW_TIER` | yes
(rules face) |
| `packages/spec/src/data/x.zod.ts` | `CONTRACT_REVIEW_TIER` | yes
(schema face) |
| `packages/spec/src/data/x.test.ts` | `TIER_DEFAULT` | no |
So all three surfaces of the order are the schema face and the rules
face already; this PR adds no face and edits no definition. It adds the
**depth** (item ④), the **scope** of ④ (the `hits` files only, the cited
code read to judge them), the **cadence** and the **independence**
clause, on those two faces. The narrowing face (at tier, but by
construction free of schema and governed paths) and the declaration face
carry no ④.
### `.claude/skills/pm-dispatch/references/contract-review.md` — 49 → 55
lines (ceiling 60)
Record shape, before (one line):
```text
- 同形 = `## Contract review` 题头、所审 head sha 独占码段、①②③ 逐项、独立性对、PASS/FAIL。
```
After (two lines):
```text
- 同形 = `## Contract review` 题头、head sha 独占码段、①②③ 逐项(schema/governed 两面加 ④)、
独立性对、PASS/FAIL。
```
Added after the ② ③ definition line (four lines: ④'s definition and
scope, then cadence):
```text
- ④ 设计判断只欠 schema 与 governed 规则文本两面,spec、skills、ADR 皆已在内 ⛔ 不另立面:
逐 `hits` 文件判改动对所引代码与验收场景是否成立,ADR 另判首个消费者与执行计划;
被引代码读而不审;①–④ 合称完整复核,余面同形无 ④,⛔ 无第二条记录。
- 实质 head 各欠一轮完整复核;只落发现的 commit 由同一复核者 delta 复核;无 must-fix 即止。
```
Added after the 独立性对 line (one line — independence):
```text
- ④ 恒由起草者之外的 agent 达档跑,起草已达档不免;喂入同上,⛔ 不喂起草者的结论。
```
The existing scope rule (混合 diff 的达档复核范围 = `reviewFace` 的 `hits` 路径 + 其
hunk 所断言的文件(读被引代码)) is the surface-file rule the owner refined the order
with; ④ restates it as 「逐 `hits` 文件判 … 被引代码读而不审」 and the remainder stays
the lane seat's ACCEPT (unchanged line). The regeneration / prose-only
head-move exception is unchanged: those are not substantive heads. Every
line ≤ 120 bytes (longest new line 116).
### `.claude/skills/pm-dispatch/SKILL.md` — 319 → 319 lines (ceiling
319, headroom 0)
One line reworded, the ACCEPT pointer line in 〈路径分叉〉. Its first half —
the pointer format `指针 ACCEPT → PR #n comment ID` (the placeholder for
the comment id is written in angle brackets in the file; spelled out
here so the body sanitizer leaves it alone) — is unchanged. The second
half, after the `;`:
Before:
```text
技能面 hunk 复核随面定档:席未达档起隔离达档复核。
```
After:
```text
受管/spec 面欠完整复核 ①–④,席未达档起隔离达档复核。
```
111 → 118 bytes. The retired words 「随面定档」 (the tier follows the face)
remain stated in `contract-review.md` 「档随面定」; the SKILL.md line now
names what the governed and published-schema faces owe (the full review
①–④) and who runs it when the seat is below tier.
### `scripts/pm/record-recognisers.mjs` — 2054 → 2134 lines
- `REVIEW_FACES` gains `fullReview` (`true` on `schema` and `rules`,
`false` on `narrowing` and `declaration`);
`recordCarriesDesignJudgment(face, tierName)` reads it (a null face is
the bare template, their tier).
- `contractReviewRecordLines` takes an optional `faceId` and prints the
fourth section; `contractReviewTemplateLines` passes the face through
and gains a ④ note; `contractReviewBriefLines` gains the ④ instruction
and the surface-file scope rule.
`--template`, fenced record after (the bare template, `--face schema`,
`--face rules`, `--paths` over a mixed list):
```text
### ③ Boundary flags
### ④ Design judgment
Implemented-by: `claude/issue-NNNN-slug`
```
`--template`, note added after the `Scope` note:
```text
· ④ Design judgment on the schema and rules faces only — the published spec, skills and ADR surfaces the
full-review order names; the narrowing and declaration faces carry no ④. For each `hits`
file: does the change hold against the code it cites and the scenario it is accepted
against — an ADR against its first consumer and its execution plan? The cited code is
READ to judge it, never reviewed; a must-fix found here is a FAIL like any other.
```
`--brief-template`, scope rule added after the `ACCEPT at TIER_DEFAULT`
line:
```text
④ judges the `hits` files ONLY: lockfiles, generated output, changesets and prose elsewhere in the PR are
not reviewed; the cited code is READ to judge them, never reviewed.
```
`--brief-template`, judge item added after ③:
```text
④ Design judgment — for EVERY `hits` file: does the change hold against the code it cites and the
scenario it is accepted against — an ADR against its first consumer and its execution plan? Read the
cited code in the live tree and try to break the design; name each finding must-fix or should-fix.
A must-fix is a FAIL. ⛔ Judge the tree, never the PR body's account of it.
```
`--face narrowing` and `--face declaration` print none of the four
blocks (the ④ note in the template's calibration text names them as
faces that carry no ④).
Self-test: 14 pins added (8 in the template battery, 6 in the brief
battery), floors raised 19 → 27 and 10 → 16 so a pin that stops
registering reds; the battery roster count (12) is unchanged. The pins
cover: ④ right after ③ in the bare record and one record shape (four
`###` headings, never a second record); schema, rules and `--paths`
print it; narrowing and declaration do not (fenced record checked, not
the calibration text); the face table marks exactly `schema,rules`; an
unknown `faceId` is refused; a record carrying ④ still reads FOUND and
its tier STANDS (④ is prose to every reader); the note text; the brief's
④ instruction, its surface-file rule, per face; the brief stays free of
angle brackets, model identifiers and digit-shaped card numbers.
| File | Before | After | Ceiling |
|:--|--:|--:|:--|
| `.claude/skills/pm-dispatch/references/contract-review.md` | 49 | 55 |
60 (headroom 5) |
| `.claude/skills/pm-dispatch/SKILL.md` | 319 | 319 | 319 (headroom 0) |
| `scripts/pm/record-recognisers.mjs` | 2054 | 2134 | not ratcheted |
## Why
The owner's order: a change on a published schema, a skill or an ADR
owes a full review by an independent agent at the contract tier before
the seat's ACCEPT — the floor, not a replacement for who drafts (the
sibling card #25729 is untouched). Measured on the card: a contract
review reads the diff against its order and never asks whether the
design works for its first consumer; that question is what ④ makes
mandatory. Triage (comment 6431838043) fixed the carrier:
`contract-review.md` + the brief template, ⛔ no new gate — the full
review is a record the seat reads, like the contract review.
## Tests
- `pnpm check:pm-record-recognisers` (the script's own suite, run by
`lint.yml` line 1165): `✓ record-recognisers self-test: 215 cases pass`
— on `origin/main` at `00a4593a` it prints 201; +14 pins.
- `node scripts/pm/check-governed-queue-guard.mjs --self-test` (loads
this file through the real lazy import and drives every queue state with
the printed record): exit 0.
- `npx eslint scripts/pm/record-recognisers.mjs`: exit 0 (the repo-wide
`pnpm lint` is CI's run).
- No ablation: the diff changes printed text and a face flag, both
pinned by the self-test's positive and negative cases (narrowing /
declaration carry no ④); no behaviour outside the self-test's reach
changed.
## Acceptance notes
- **Carrier**: item ④ in the ONE `## Contract review` record (the seat's
recommendation on the card), not a second record — `contract-review.md`
says 「⛔ 无第二条记录」 and the self-test pins four `###` headings and one
record shape.
- **④ on the narrowing face: no.** The order names published spec,
skills and ADRs; the narrowing face exists only when the diff has no
schema and no governed path. Extending ④ there would widen the order; a
one-flag change (`fullReview: true` on `narrowing`) if the owner wants
it.
- **Independence** was already the `Implemented-by:`/`Reviewed-by:` pair
plus the feeding rule; the new line adds what the card asks: a change
drafted at the contract tier still owes the review, by an agent other
than its drafter, never fed the drafter's conclusions.
- **Cadence** is new text: one full review per substantive head; a
commit that only applies the findings gets a delta review of that commit
by the same reviewer; the cycle stops at the first round with no
must-fix. The existing regeneration / prose-only head-move exception
stays.
- **Citing the card in the skill — not done, by rule.** The dispatch
order allows the skill text to cite the card's evidence; `pnpm
check:pm-skill-id-lint` (pattern `#` + 3 digits) and
`.claude/agents/os-dev.md` (「教训写成自含规则,⛔ 不引卡号」) forbid card numbers in
`.claude/skills/pm-dispatch/**`. The rules are written self-contained;
provenance lives in this body and in the script's docblock, where
ruling-record citations are the file's own convention.
- **Not in scope, not changed**: who drafts ADR text (#25729);
re-reviewing merged PRs; `references/_index.md` does not exist for
pm-dispatch (no generator owns one).
- **Observation, not filed**: the bare `--template` / `--brief-template`
(no `--face`, no `--paths`) print ④, because the bare template is the
tier of a schema or governed-path face; a narrowing-only PR already has
to be printed with `--face narrowing` (the self-test refuses `--paths`
over such a list), so no new trap.
## Verification
Local gates on the final head `54224ebf` — derived with `node
scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (41 families), each exit code captured
before any pipe, reconciled with `--ran`: `✓ dispatch-gates --ran: 41
derived famil(ies) accounted for — 41 run, 0 NOT-MEASURED`. One family,
`check:doc-formula-expressions`, first exited 3 (PREREQUISITE NOT MET:
`@objectstack/formula` and `@objectstack/lint` dist absent in a fresh
worktree); after `pnpm exec turbo run build
--filter=@objectstack/formula --filter=@objectstack/lint` under
`os-verify-lock.sh` (VERDICT command-exit 0, held 99 s) it re-ran and
exited 0. `check:pm-dispatch-gates` ran detached (308.9 s).
<details><summary>Exit codes (41)</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
node scripts/pm/check-governed-queue-guard.mjs --self-test :: exit 0
node scripts/pm/check-harness-current.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:doc-authoring :: 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-expected-skips :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-governed-prose :: exit 0
pnpm check:pm-record-recognisers :: exit 0
pnpm check:pm-skill-id-lint :: exit 0
pnpm check:pm-skill-ratchet :: 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:skill-frame-sync :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:pm-dispatch-gates :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
```
</details>
- `pnpm check:pm-skill-ratchet`: `contract-review.md is 55 lines
(ceiling 60; headroom 5)`, `SKILL.md is 319 lines (ceiling 319; headroom
0)`, widest SKILL.md table row 341 (pin 341).
- `pnpm check:pm-skill-id-lint`: 104 file(s) clean.
- `pnpm check:pm-dispatch-gates`: `✓ dispatch-gates self-test (fast
tier): 1935 cases pass` (308.9 s).
- Control-character scan over the three files: clean. No model
identifier in the diff, the commit or this body.
Two-minute check:
```bash
node scripts/pm/record-recognisers.mjs --template | grep -n "④"
node scripts/pm/record-recognisers.mjs --brief-template | grep -n "④"
node scripts/pm/record-recognisers.mjs --brief-template --face narrowing | grep -c "④" # prints 0
pnpm check:pm-record-recognisers # 215 cases pass
```
CI on the PR: the required contexts run on the head after this PR opens;
this body is written before them (⛔ not waited on, per the order) and
the seat's ACCEPT reads the check-runs.
## Human Minimum
Each item is one rule from CONTRIBUTING.md › Human Minimum.
- [x] 1. No schema touched; `packages/spec` holds no runtime logic —
n/a.
- [x] 2. Every name I added uses the casing for its kind (`fullReview`,
`recordCarriesDesignJudgment`).
- [x] 3. No formatter ran; eslint on the edited file passes (repo-wide
`pnpm lint` is CI's).
- [x] 4. One issue, one branch off an up-to-date `main` (`00a4593a`),
one pull request — on the card thread's harness branch, the measured
platform exception to `claude/issue-N-slug` named in the seat's Claim.
- [x] 5. Nothing publishes: `.claude/**` and `scripts/pm/**` are the
fast lane, `skip-changeset` declared at `pr_create`; both declaration
lines are at the top of this body.
- [x] 6. `packages/spec` untouched.
- [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 and
the quoted rule lines are the four Chinese channels the skill allows).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbZCMuAzYCqL3RBJRdX)_
Co-authored-by: objectstack-fleet[bot] <332303061+objectstack-fleet[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>1 parent add806c commit c7d6ca8
3 files changed
Lines changed: 99 additions & 13 deletions
File tree
- .claude/skills/pm-dispatch
- references
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
190 | | - | |
| 190 | + | |
191 | 191 | | |
192 | 192 | | |
193 | 193 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
| 23 | + | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
29 | 34 | | |
30 | 35 | | |
31 | 36 | | |
32 | 37 | | |
33 | 38 | | |
34 | 39 | | |
35 | 40 | | |
| 41 | + | |
36 | 42 | | |
37 | 43 | | |
38 | 44 | | |
| |||
0 commit comments