Skip to content

Commit 29c7157

Browse files
pm-dispatch: a declared narrowing is the fourth contract-review face, at CONTRACT_REVIEW_TIER (#23750)
Fixes #23692 Clause-②: no Implements ruling record 6203713500 (letter B, maintainer 「B (推荐)」): ruling C (6053791774) stands as a face of its own under ruling B′ (6159870311). A card declared `Clause-②: no` that narrows a published accept set owes one isolated review at `CONTRACT_REVIEW_TIER`, exactly. ## What changed **`scripts/pm/record-recognisers.mjs`** - `REVIEW_FACES` gains `narrowing`, mapped to `CONTRACT_REVIEW_TIER_NAME`. The decision order is `schema`, `rules`, `narrowing`, `declaration`. `REVIEW_TIER_NAMES` is unchanged: still two names. - `reviewFace(paths, repo, { declaredOn })` takes one optional, named third input: the texts the `Clause-②:` line is written on. Those are each committed changeset's text, plus the PR body where the caller holds it. The texts are read with `readClause2Line`, imported from `clause2-line.mjs`; there is no second parser. - `servedTierStands` is unchanged. Because the face now answers `CONTRACT_REVIEW_TIER_NAME` on a narrowing PR, it accepts a `CONTRACT_REVIEW_TIER` record there and refuses `TIER_DEFAULT` (ruling item 1). - In `--template` and `--brief-template`, `--face narrowing` is listed from the face table. The narrowing brief sets the scope floor at every path of the diff outside the prose faces, with no lane remainder. `--paths` over a list the face cannot be read from is refused: exit 1, with the usage, the reason, and a pointer to `--face`. - Self-test: a new battery `letter B: a declared NARROWING is a face of its own …` (17 cases, floor 17; the roster floor goes from 11 to 12). The closed-set case now expects four faces. **`.claude/skills/pm-dispatch/references/contract-review.md`**: net +1 line (47 → 48; the ceiling stays at 60). - 契约三面 → 契约四面, adding 收窄申报. 三面皆不碰 → 四面皆不碰. - The tier bullet (档随面定) now covers the narrowing face: the spelling at line start in the PR body and the changeset, `CONTRACT_REVIEW_TIER`, and a scope floor of the whole diff outside the prose faces. - The reading line (面读 …) now reads the file list and the declaration line, and still never a seat's own statement. In-place edit, 113 B. `execution-duties.md` is not touched (ruling item 2). Neither are the frozen `dispatch-gates.mjs`, `dispatch-gates.data.mjs` and `dispatch-gates.self-test.mjs`, or `check-governed-queue-guard.mjs`. ## Design answer: how the face learns the declaration (dispatch §2 (b)) - **Path faces decide first and never read `declaredOn`.** Results for `schema` and `rules` are byte-identical to the two-argument call. A case pins this by `JSON.stringify` equality with a narrowing line, with a malformed line, and with no line. - **The paths decide nothing and a declaration is handed.** A `(narrowing)` arm on any carrier gives the narrowing face. Otherwise, a `malformed` line on any carrier gives UNREADABLE: `readClause2Line` collapses near-miss arms such as `(narrowed)` into `malformed` precisely so they cannot read as no narrowing. Otherwise the result is the declaration face. - **The paths decide nothing and no declaration is handed.** If the file list carries a changeset (a `.changeset` markdown file other than the README), the result is UNREADABLE: `face: null`, `tierName: null`, plus a `reason`. `servedTierStands` stands nothing on that, so the reading is never the cheaper tier. This is the queue guard's own rule for a pair with no path list, applied one level down. If the list carries no changeset, the declaration face stands, byte-identical to before. A narrowing of a published accept set moves a published package and therefore ships a changeset, so a committed tree with no changeset carries no narrowing declaration. - **Why the no-declaration fallback is not always UNREADABLE (measured).** The queue guard calls `reviewFace(pair.paths, pair.repo)` with no declaration. Its self-test pins `recordVerdict` standing at `TIER_DEFAULT` on a declaration-only pair whose paths include no changeset (case `the-SAME-reading-STANDS-a-TIER_DEFAULT-record-on-a-declaration-only-pair`). The required `Governed Surface Queue Guard` check runs that self-test first. An unconditional UNREADABLE would turn that case red, and fixing it would need an edit to `check-governed-queue-guard.mjs`, which is outside this card's file surface. The changeset rule keeps that pin true and still refuses the narrowing-only shape that met PR #23683. So the PM reading in §2 (e) holds: no guard edit is owed. The guard self-test passes 322 cases before and after. Three choices for the reviewer to judge: 1. A `yes (narrowing)` line maps to the narrowing face. The arm decides, the way one hit decides a mixed diff. The ruling's text names only the `no (narrowing)` spelling. 2. The narrowing face's scope floor is the whole diff outside the prose faces. The ruling does not address scope, and the letter-C `hits` floor would be empty on this face. 3. The PR body counts as a carrier when a caller hands it, because the ruling writes the spelling on both. The doc's reading line names the file list and the declaration line. ## Verification at head `4c15f575b` - Defect reproduced at `3b46855fb`: `--brief-template --paths` over a list shaped like PR #23683's (one changeset, `packages/metadata-protocol/src/protocol.ts`, one runtime test) printed "You review at `TIER_DEFAULT`". At this head the same command exits 1 and names `--face narrowing`. - `pnpm check:pm-record-recognisers` (`--self-test`): 184 cases passed at the base and 201 pass at the head, exit 0. - Ablation: `scripts/ablation-replace.mjs` deleted the `narrowing` row of `REVIEW_FACES` (anchor ×1 → ×0, blob `e0e82d93bad8` → `23a1eb6ce055`). The self-test then exited 1 with 8 of 201 cases red: - the faces are the closed set of four, in decision order, and they map to exactly two NAMES - ⭐ a narrowing-only path list with `Clause-②: no (narrowing)` on its changeset ⇒ the narrowing face, CONTRACT_REVIEW_TIER - ⭐ …and a CONTRACT_REVIEW_TIER record STANDS on that pull request — the ruling's first half - ⭐ the line read off the PR BODY counts as well — the ruling writes the one spelling on both carriers - ⭐ `yes (narrowing)` is the narrowing face — a diff that widens and narrows owes the narrowing review, as a mixed diff owes its hit - ⭐ `--face narrowing`'s record carries CONTRACT_REVIEW_TIER and the `Scope:` line, round-trips through the locator and STANDS on the narrowing face - ⭐ `--template --face narrowing` prints exactly that template and exits 0 - ⭐ the narrowing brief reviews at CONTRACT_REVIEW_TIER, hands `--face narrowing`, and floors the WHOLE diff — no lane remainder The restore leg proved blob == HEAD (`e0e82d93bad8`) with an empty `git diff HEAD`. The restored run passes 201 cases, exit 0. - `node scripts/pm/check-governed-queue-guard.mjs --self-test`: 322 cases pass, exit 0, before and after. - `check-skill-line-ratchet` for `contract-review.md`: "is 47 lines (ceiling 60; headroom 13)" before, "is 48 lines (ceiling 60; headroom 12)" after, exit 0. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `4c15f575b` derives 37 commands, the same list as the dispatch. All 37 were run locally, each exit code captured before any pipe. `--ran` reports: "37 derived famil(ies) accounted for — 37 run, 0 NOT-MEASURED (a DERIVED zero — all 37 recorded an exit code and none of them is 3)". - `check:doc-formula-expressions` first exited 3 (PREREQUISITE NOT MET: `@objectstack/formula` not built). After a rebuild of `@objectstack/formula` and `@objectstack/lint` under the verify lock (command-exit 0), it reran with exit 0. - `pnpm check:pm-dispatch-gates` exited 0: fast tier, 1884 cases, 6 slow sections deferred to CI. - No control bytes in either file (scan exit 1 = no match). No changeset: neither `scripts/pm/**` nor `.claude/**` publishes a package, so this PR carries `skip-changeset`. ## Acceptance notes (observations; not edited, because the files are frozen or outside this card's surface) - `scripts/pm/dispatch-gates.mjs:12013` (comment: "the contract-review reference's three faces") now names three of four faces. The file is frozen. - `scripts/pm/dispatch-gates.data.mjs:1062` ("the three contract faces … a published schema, governed rules text, the `Clause-②: yes` declaration") likewise. Frozen. - `scripts/pm/dispatch-gates.self-test.mjs:9961` ("the three contract faces") likewise. Frozen. - `.claude/skills/pm-dispatch/references/lanes/ui.md:38` still says 契约三面. It is outside this card's file surface (carrier: none). - `dispatch-gates.mjs:11794–11797` (the `--tier` docblock) and `:11866–11873` remain true. The `--tier` note already says what ruling C says. - Records written at `TIER_DEFAULT` on narrowing-only PRs before this lands, such as PR #23683's, are not re-opened (ruling item 3). ## 维护者速读(草稿) **改了什么**:复核记录识别器新增第四个复核面「收窄」。PR 正文与 changeset 行首写 `Clause-②: no (narrowing)` 的卡,其隔离复核档位定为 `CONTRACT_REVIEW_TIER`,且只认这一档。规则文 contract-review.md 同步把「三面」改为「四面」,净增一行。 **为什么改**:裁决 B 认定「收窄已发布接受集的卡欠一次契约复审档复核」是独立的一面。旧识别器没有这一面,收窄类 PR 只能按默认档复核,规则文与工具给出两个答案;本周已有四张收窄类 PR 遇到。 **风险与代价(含回滚)**:风险小,只动 PM 工具与一份内部规则文,不发布任何包。若调用方没有交来申报内容,而 PR 带 changeset,识别器答「不可读」,不再默认给便宜档;命令行 `--paths` 此时会拒绝并提示改用 `--face`。队列守卫的线上行为不变(它只看 governed PR,永远按路径定面)。回滚:revert 本 PR 即可,无数据迁移。 **席位意见**: **你要做的**:无需动手。席位拿到达档契约复核 PASS 后按 Tier S 落地;如对上文三处设计取舍有不同意见,在卡上回一句即可。 --- _Generated by [Claude Code](https://claude.ai/code/session_01VqGhCg9wdsBXPz54uAEWg7)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent c6c84d3 commit 29c7157

2 files changed

Lines changed: 185 additions & 34 deletions

File tree

‎.claude/skills/pm-dispatch/references/contract-review.md‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@
55
## 复核归属与资格(按面)
66

77
- 欠不欠按面判 ⛔ 不按车道:diff 或认领申报碰下列任一面即欠达档复核,交付后当轮完成。
8-
- 复核面 = 契约三面:`Clause-②: yes` 申报、已发布 schema(`packages/spec/src/**` 非测试)、governed
9-
规则文本(统一定义见 SKILL.md);`.changeset`/CHANGELOG、`content/docs/**`、`apps/docs/**` 散文面
10-
⛔ 不在其列:派发席按本席档在 ACCEPT 逐句对照 diff 核,ACCEPT 点名所核句。
11-
- 档随面定:已发布 schema 或 governed 规则文本 ⇒ `CONTRACT_REVIEW_TIER`,混合 diff 一处命中即此档;
12-
仅凭 `Clause-②: yes` 申报而欠 ⇒ `TIER_DEFAULT`,由车道席位作复核记录;档恰等不作下限。
8+
- 复核面 = 契约四面:`Clause-②: yes` 申报、收窄申报、已发布 schema(`packages/spec/src/**` 非测
9+
试)、governed 规则文本(统一定义见 SKILL.md);`.changeset`/CHANGELOG、`content/docs/**`、`apps/docs/**` 散
10+
文面 ⛔ 不在其列:派发席按本席档在 ACCEPT 逐句对照 diff 核,ACCEPT 点名所核句。
11+
- 档随面定:已发布 schema 或 governed 规则文本 ⇒ `CONTRACT_REVIEW_TIER`,混合 diff 一处命中即此
12+
档;PR 正文与 changeset 行首 `Clause-②: no (narrowing)` 申报的收窄亦此档,范围为散文面外全
13+
diff;仅凭 `Clause-②: yes` 申报而欠 ⇒ `TIER_DEFAULT`,由车道席位作复核记录;档恰等不作下限。
1314
- 混合 diff 的达档复核范围 = `reviewFace` 的 `hits` 路径 + 其 hunk 所断言的文件(读被引代码);
1415
规则与执行它的门禁同属一改者一并读;记录 `Scope:` 行列两者,可放宽、⛔ 不窄于 `hits`。
1516
- 余部归车道席位 `TIER_DEFAULT` 的 ACCEPT,同单独开 PR 并点名;Tier H 人批与队列守卫仍整张 PR。
16-
- 面读 PR 已提交树的文件清单,同队列守卫判 governed 路径;⛔ 不读席位自述。
17+
- 面读 PR 已提交树的文件清单(同队列守卫判 governed 路径)与申报行;⛔ 不读席位自述。
1718
- 已发布面以包的 `exports` 映射为准:从入口类型图不可达、且 `exports` 不可寻址的 `.d.ts` 是
1819
出货字节,⛔ 不是已发布接受集,改它不触发 `Clause-②: yes`;legacy node10 深引用在契约之外。
19-
- 三面皆不碰 ⇒ CI 加席位自读 ⛔ 不起第二个 agent;谁跑 = 派发席,达档者席内审。
20+
- 四面皆不碰 ⇒ CI 加席位自读 ⛔ 不起第二个 agent;谁跑 = 派发席,达档者席内审。
2021
- 复核记录 = 一条评论落 PR 或卡,席内与子代理同形;散文、dev 自评、`os-dev-report` 恒不算。
2122
- 同形 = `## Contract review` 题头、所审 head sha 独占码段、①②③ 逐项、独立性对、PASS/FAIL。
2223
- 同形含首行 `Served-tier:`:值写该面所定档的常量名 `CONTRACT_REVIEW_TIER` 或 `TIER_DEFAULT`;

0 commit comments

Comments
 (0)