Skip to content

pm-dispatch: a declared narrowing is the fourth contract-review face, at CONTRACT_REVIEW_TIER - #23750

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-23692-narrowing-review-face
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-23692-narrowing-review-face

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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 fix(metadata-protocol)!: the promote doors ask ADR-0029 D9.9's question the save door asks (#23630) #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 fix(metadata-protocol)!: the promote doors ask ADR-0029 D9.9's question the save door asks (#23630) #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 fix(metadata-protocol)!: the promote doors ask ADR-0029 D9.9's question the save door asks (#23630) #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

…eview face

Ruling record 6203713500 (letter B): ruling C stands as a face of its own
under B-prime. `REVIEW_FACES` gains `narrowing`, mapped to
`CONTRACT_REVIEW_TIER` exactly, keyed on the `Clause-②: no (narrowing)` line
read by `readClause2Line`. `reviewFace` takes the declaration's carrier texts
as an optional third input; a path list that decides no face and carries a
changeset nobody read is UNREADABLE, never the cheaper tier, and
`--template/--brief-template --paths` refuse it in favour of `--face`.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VqGhCg9wdsBXPz54uAEWg7
… its fourth face

Ruling record 6203713500 (letter B): the three contract faces become four.
A `Clause-②: no (narrowing)` line at line start in the PR body and the
changeset puts the review at `CONTRACT_REVIEW_TIER`, its floor the whole diff
outside the prose faces; the face reads the committed file list and the
declaration line. Net +1 line (47 to 48, ceiling 60 unchanged).

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VqGhCg9wdsBXPz54uAEWg7
Without the `narrowing` row the template and brief calls refuse; each case
now turns that refusal into its own red so an ablation reads the case names.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VqGhCg9wdsBXPz54uAEWg7
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Oct 15, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 15, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

Seat domain:skills#2 · session_01VqGhCg9wdsBXPz54uAEWg7 (os-elon-musk) · read 2026-10-16T00:02Z · head 4c15f575b1ea6c1495f443aff575c58bb6e193c4 · verdict: ACCEPT (Tier S — lands on the seat only after an at-tier contract review PASS on this head)

Implemented-by: claude/issue-23692-narrowing-review-face
Reviewed-by: session_01VqGhCg9wdsBXPz54uAEWg7

  1. Shape.
    • Draft PR against main. The body's first line is Fixes #23692, followed by Clause-②: no at line start, copied from claim 6204308625.
    • The only closing keyword is that one. PR fix(metadata-protocol)!: the promote doors ask ADR-0029 D9.9's question the save door asks (#23630) #23683 is named several times, never beside a closing verb.
    • The file list is scripts/pm/record-recognisers.mjs (+177/−27) and .claude/skills/pm-dispatch/references/contract-review.md (+8/−7), the claimed surface.
    • The diff touches neither the frozen dispatch-gates*.mjs, execution-duties.md nor check-governed-queue-guard.mjs. Merge base 3b46855fb.
  2. The ruling (6203713500, letter B) against the diff, read by the seat.
    • Item 1:
      • REVIEW_FACES gains narrowing → CONTRACT_REVIEW_TIER_NAME. REVIEW_TIER_NAMES keeps two names.
      • reviewFace(paths, repo, { declaredOn }) reads the carriers with readClause2Line, imported from clause2-line.mjs. It writes no second parser; its { kind: 'declared', arm } shape was read by the seat.
      • servedTierStands is unchanged and now gets CONTRACT_REVIEW_TIER_NAME on that face.
    • Item 2: contract-review.md 三面 becomes 四面, net +1 line (47 → 48, ceiling 60 unmoved). execution-duties.md L107 and the --tier note are untouched.
    • Item 3: no record is re-read or re-opened.
    • Path-decided faces never read declaredOn. The early returns for schema and rules precede the declaration branch.
    • Callers. The only two-argument runtime caller is the guard's recordVerdict (check-governed-queue-guard.mjs L820), whose leg sees governed pairs only, so it is always path-decided. reviewScopeHits reads hits only, which UNREADABLE still carries. git grep at the head finds no other call.
  3. Gates — check-runs pinned to head 4c15f575b.
    • 41 check-runs: 28 success, 11 skipped and 2 in_progress (Test Core (1/6), Type Check · consumer gates).
    • success includes Lint & Repo Gates, Governed Surface Queue Guard, Lint · PM dispatch-gates, Lint · scoped · tooling self-tests and every other Lint lane, plus Type Check · workspace, · source gates and · debt ledger.
    • The dev's local union at 4c15f575b covers 37 derived families: all 37 ran at exit 0, and --ran reports 0 not measured.
    • The self-test went 184 → 201 cases. The ablation deleted the narrowing row: 8 named cases went red, and the blob was restored to e0e82d93bad8.
    • The pre-landing check re-reads every check-run on the head of the day.
  4. Design choices the ruling does not settle. The seat accepts all three and puts them to the at-tier reviewer.
    • (a) yes (narrowing) maps to the narrowing face. This is the stricter reading, the way one hit decides a mixed diff. The doc line names only the no (narrowing) spelling, so a reader could take a yes (narrowing) PR for 「仅凭 Clause-②: yes 申报」; the reviewer judges whether that gap needs a word.
    • (b) The narrowing face's scope floor is every path outside the prose faces. The letter-C hits floor would be empty on this face.
    • (c) No declaration handed and a changeset in the tree ⇒ UNREADABLE (face: null). With no changeset, the declaration face stands byte-identically. That keeps the guard's pinned declaration-only case green without a guard edit; it is measured, guard self-test 322/322 before and after.
    • Consequence: --template / --brief-template --paths over a non-governed list with a changeset now refuses and asks for --face.
  5. Out of scope (dropped — not cards; the carrier is this seat's shift brief, stale-text class):
    • The frozen dispatch-gates.mjs:12013, dispatch-gates.data.mjs:1062 and dispatch-gates.self-test.mjs:9961 comments still say "three contract faces". They are frozen (ruling 208), and ruling item 2 grants no exception.
    • references/lanes/ui.md L38 still says 契约三面.
  6. Landing.
    • At-tier contract review: OWED. The face is rules (contract-review.md), at CONTRACT_REVIEW_TIER. The scope widens to scripts/pm/record-recognisers.mjs, the code the rule line describes.
    • needs:contract-review is added in this act. The isolated at-tier reviewer is launched on this head.
    • After that come the pre-landing checks (all check-runs green, check-governed-merges --pr 23750 Tier S), then pr_ready + automerge_enable.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4c15f575b1ea6c1495f443aff575c58bb6e193c4
Local-runs: none
Scope: .claude/skills/pm-dispatch/references/contract-review.md + scripts/pm/record-recognisers.mjs, scripts/pm/clause2-line.mjs (readClause2Line and readArmToken, read only), scripts/pm/check-governed-queue-guard.mjs (recordVerdict and its Tier S record leg, read only)

① Derived judgments

Every accept-set and public-surface change the diff implies, judged in the cited code at this head:

  1. REVIEW_FACES gains narrowing mapped to CONTRACT_REVIEW_TIER_NAME, decision order schema, rules, narrowing, declaration; REVIEW_TIER_NAMES stays two names (record-recognisers.mjs L153–161). Right — ruling 6203713500 item 1 as written. The path faces still decide first (L225–226), so a declaration can never move a path-decided tier, and servedTierStands (L675–682) is untouched: with the face answering CONTRACT_REVIEW_TIER_NAME on a narrowing PR it accepts that name and refuses TIER_DEFAULT, exact in both directions. Pinned by the new battery (17 cases at floor 17; roster floor 11 → 12), and the ablation named 8 red cases by name rather than crashing the run.
  2. reviewFace(paths, repo, { declaredOn }) — one optional, named third input, read with the imported readClause2Line (L107, L237); no second parser. Right. The arm reader (clause2-line.mjs L280–293, L445–449) already folds (narrowed), (Narrowing), the unfilled menu and the no (widening) contradiction into malformed, so the face's order — any declared line with the narrowing arm ⇒ narrowing; else any malformed ⇒ UNREADABLE; else declaration (L238–243) — fails closed in the one direction that matters: a declared narrowing cannot read as none. A near-miss or null reading (no key line; a line that quotes the spelling) falls to the declaration face — correct: the face is asked only of a review already owed, and whether a declaration line is missing is the clause-② gates' fact, not this function's.
  3. Two-argument callers whose paths decide nothing (L227–236): a .changeset/*.md other than the README in the list ⇒ UNREADABLE (face: null, tierName: null, a reason naming the carrier); no changeset ⇒ declaration, byte-identical to before (pinned by JSON equality). Right, and the premise holds by the repo's own rule: a narrowing of a published accept set publishes from a released package, which takes a changeset and never skip-changeset (AGENTS.md Post-Task Checklist 3), so a committed tree with no changeset carries no narrowing declaration. The return type widens by a null-face shape; servedTierStands stands nothing on it (REVIEW_TIER_NAMES.includes(null) is false), so no caller can turn UNREADABLE into the cheaper tier.
  4. The one live caller, check-governed-queue-guard.mjs recordVerdict (L820, L866): unchanged, correctly. Its record leg iterates governed entries with tier === TIER_S on merge_group only (L1627–1631), and every governed list is the rules (or schema) face at L225–226 before declaredOn is consulted, so the null-face branch is unreachable on the live leg and the guard's own pin on a declaration-only, changeset-free pair still stands. Governed Surface Queue Guard is success on this head. Right; no guard edit is owed.
  5. CLI: --template / --brief-template --paths LIST where the paths decide no face and a changeset is present is now refused — usage, the reason naming the carrier, a pointer to --face narrowing or --face declaration, exit 1 (L1108–1114, L2029–2038) — where before it printed the declaration brief, the exact shape that met PR fix(metadata-protocol)!: the promote doors ask ADR-0029 D9.9's question the save door asks (#23630) #23683. Right — the paths cannot read the line, and printing the cheaper tier there is the defect this card was filed on. Accepted cost: an ordinary Clause-②: yes lane PR that ships a changeset must now pass --face declaration after reading its line; the refusal text carries that remedy.
  6. --face narrowing: the record template prints CONTRACT_REVIEW_TIER and a Scope: line; the brief prints that tier, --template --face narrowing, and a whole-diff floor with no lane remainder (L1176–1180). Right in direction (see ③ Q2). reviewScopeHits is unchanged; hits is empty on this face by construction and its docblock now says so.
  7. contract-review.md (governed rules text; 47 → 48 lines, ceiling 60, every line ≤ 120 B, max measured 120): 契约三面 → 契约四面 with 收窄申报 named (L8); the tier bullet gains 「PR 正文与 changeset 行首 Clause-②: no (narrowing) 申报的收窄亦此档,范围为散文面外全 diff」 (L12–13); the reading line now reads the file list 与申报行 (L17); 三面皆不碰 → 四面皆不碰 (L20). Right: the rules text, the frozen --tier note, execution-duties.md L107 (unchanged, as ruled) and the tool now give one answer for a narrowing-only PR. L17 stays consistent with 「⛔ 不读席位自述」: the Clause-②: line is the formal declaration the ruling fixes as a carrier, not a seat's prose about the face. The mid-run CJK wraps on L8–10 (非测/试, 散/文面) are wrapLine's canonical form — a break between two wide characters is removed at render — and the base already wrapped L18–19 the same way; not a defect.
  8. Not touched, correctly: dispatch-gates.mjs, dispatch-gates.data.mjs, dispatch-gates.self-test.mjs (frozen; the --tier note's operative text at L12016 ff. and the docblock at L11798–11800 already say what ruling C says), execution-duties.md L107, check-governed-queue-guard.mjs. No new label, check family or workflow. Nothing in the diff re-reads existing records, so PR fix(metadata-protocol)!: the promote doors ask ADR-0029 D9.9's question the save door asks (#23630) #23683's TIER_DEFAULT record is not re-opened (ruling item 3).

Check-runs on this head, read 2026-10-16T00:16Z: 47 runs, all completed. The five required contexts — Lint & Repo Gates, TypeScript Type Check, Test Core (aggregate and all six shards), Governed Surface Queue Guard — are success; Build Core is skipped by the path filter (no package touched). No run concluded failure or cancelled. Lint & Repo Gates carries check:pm-record-recognisers, so the new battery and the --paths refusal pin ran in CI.

② Semver level

skip-changeset is correct: the diff touches scripts/pm/** (root scripts of the private monorepo, "private": true, in no package's publish set) and .claude/**; nothing published moves. Check Changeset is success. The PR body's Clause-②: no (line 2, no arm) is right: clause ② reads published contract surfaces only (execution-duties.md L106 「条款②只指已发布契约面」), and the CLI's new --paths refusal narrows an internal tool's accept set, not a published one. No ADR-0087 disposition is owed.

③ Boundary flags

Dev deviations (os-dev-report 6204819375):

  • D1, extra machine fields in the report: a report-shape matter with no code consequence — noted.
  • D2, the no-declaration fallback conditional on a changeset path rather than unconditionally UNREADABLE: judged right in ① items 3–4; it is a rule about the committed carrier, not a workaround for the guard's pin — the pin merely confirms it.
  • D3, origin/main not merged after opening (base moved 3b46855 → a869ea6: metadata-protocol and driver-sql, zero overlap with the touched files): right — AGENTS.md §10 merges main after opening only on a reported conflict, a main-caused red or a queue removal; mergeable_state is blocked (draft), not dirty, and the queue rebuilds on current main.

Open questions, which are also the seat's three accepted design choices (ACCEPT 6204870805):

  • Q1, yes (narrowing) maps to the narrowing face: A, right, with one escalation. The arm decides, the way one hit decides a mixed diff; under B a PR that widens AND narrows would get a cheaper review than one that only narrows, and AGENTS.md admits yes (narrowing) as a legal declaration ((narrowing) is BREAKING whatever the value). Escalated to the maintainer, as the seat already invited on the card: the ruling's letter names only Clause-②: no (narrowing), and so do rules-text L12, the narrowing face's words (L156) and the template help (L1053–1054), while the code keys on the arm — a reader of L12–13 alone can land yes (narrowing) on TIER_DEFAULT, where L8's generic 收窄申报 lands it at CONTRACT_REVIEW_TIER. If the maintainer confirms A, a follow-up one-line touch spelling the arm rather than the no value in L12 and words closes that residual two-reading risk; if B, the code keys on the no value too. Not blocking: the implemented direction is the strict one and no declared narrowing escapes it.
  • Q2, the narrowing face's scope floor is the whole diff outside the prose faces: A, right. The narrowing is declared of the PR, not of a path, so letter C's hits floor is empty here, and B would let a CONTRACT_REVIEW_TIER review cover nothing mandatory. The ruling is silent on scope; the dev flagged it, the rules text states it (L12–13) within the ruled budget, and the maintainer can object on the card.
  • Q3, widen a later card so the guard hands declaredOn or flips the fallback to unconditional UNREADABLE: A (no), right. The guard's live leg is governed-only and path-decided (① item 4); a non-governed leg would be a new gate — 「新增门禁默认否」 — and the ruling adds no check family.

Out-of-scope findings (carrier: none; acceptance notes):

Reviewer's own flags:

  • record-recognisers.mjs L113–114: the B′ docblock still reads "the three contract faces of references/contract-review.md" — the line-wrapped spelling the dev's sweep missed; it reads as history ("three … the FOURTH") but misdescribes the reference as it now stands. Fix on next touch; not blocking.
  • contract-review.md L24 names only --face declaration as the extra template flag; the --paths refusal text carries the --face narrowing remedy and the line sits at 119 B. Acceptable.
  • Per L46 the needs:contract-review label comes off with this PASS; that is the adopting seat's write, not this record's (one write only).

Implemented-by: claude/issue-23692-narrowing-review-face
Reviewed-by: session_01VqGhCg9wdsBXPz54uAEWg7

VERDICT: PASS


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 size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants