Commit 66e34d1
Fixes #18060
A review of record is a GitHub **comment**, and `AGENTS.md` is
unqualified about that surface:
> …the pre-push hook refuses a model identifier in that pair; **no model
identifier lands in a PR title or body, a comment, a changeset, a doc or
a code comment.**
The `Served-tier:` top line the in-seat contract-review protocol
mandates carried the **value** of `CONTRACT_REVIEW_TIER`, which is a
literal model identifier. So a record could clear a carrier **only** by
putting that identifier into the very artifact the rule names.
This PR makes the identifier-free spelling the **only** one: the line's
token is the constant's **NAME**. Nothing evidential is traded away —
the line was never the reading. The protocol already says 「⛔
自述档位与传参皆非读数」, and the authoritative control is the seat's own transcript
grep against the constant's value, which produces **no repository
artifact at all**.
Direction was ruled by triage (comment 5656662371, quoted verbatim,
untranslated):
> ### ⭐ Direction ruled — **the convention yields to `AGENTS.md`.** ⛔
Not a decision card.
> ⛔ Triage rules the direction; the exact replacement wording is the
implementer's.
## Premise readings
All four checked against `origin/main` at `57343f761`, in the worktree,
on 2026-09-14.
| # | premise | verdict | evidence |
|:--|:--|:--|:--|
| P1 | both readers compare the token to the constant's **VALUE**, which
is a literal model identifier ⇒ a record can pass today only by carrying
that identifier in a comment | **holds** | `servedTierStands()` read
`served.value === CONTRACT_REVIEW_TIER`;
`check-governed-queue-guard.mjs` imports that very predicate through
`loadRecordRecognisers`, so both gates answered from one comparison
(00:10Z) |
| P2 | `contract-review.md` :29 and :56 require that value | **holds** |
:29 read 「值取转录 harness `model` 盖章」 and :56 「裁决 `Served-tier:` ≠
`CONTRACT_REVIEW_TIER` ⇒ exit 4」 (00:08Z) |
| P3 | `git grep -n Served-tier origin/main -- .claude AGENTS.md skills`
finds ONLY those two lines | **holds** | exactly 2 hits, both
`contract-review.md`; the whole-tree grep adds only the two script
files, 26 + 14 hits (00:09Z) |
| P4 | the card's 「no gate reads it *yet*」 is **already false** on
`origin/main` | **FALSIFIED — the window the card names has closed** |
`273a66501` (2026-09-13T13:12Z, #17990) and `60b99552a`
(2026-09-13T15:00Z, #18036) are both ancestors of `origin/main` — `git
merge-base --is-ancestor` exit **0** for each, the self-proving
direction that needs no control leg (00:41Z) |
P4 is why the **readers change too** rather than only the prose. The
card was filed while this was a habit; it is a gate now, and 「a gate is
much harder to walk back than a habit」.
## What changed
1. **`references/contract-review.md` :29 and :56** — equal-line edits,
file still 60 lines, both lines inside the 120-byte CJK prose budget
(110 B and 120 B).
- :29 — 「值写常量名 `CONTRACT_REVIEW_TIER`,可前置 N/N;无此行不成裁决。」
- :56 — 「`Served-tier:` ≠ 常量名 ⇒ exit 4,点名 PR、评论、读数;型号串按 `AGENTS.md` 拒。」
- The evidence stays exactly where :53 already puts it — the seat's
transcript grep, which lands no artifact.
2. **`check-clause2-carriers.mjs`** — new `CONTRACT_REVIEW_TIER_NAME` is
the one accepted token; new `isModelIdentifierToken()` refuses the
constant's value **and** the id shape (the word claude, a hyphen, a
model word — a shape, never a list, so a model nobody has named yet
binds). C7's remedy quotes the **new** rule lines and names
`AGENTS.md`'s rule, and ⛔ never quotes an identifier token back — a
refusal that echoed it would land the identifier in one more artifact.
3. **`check-governed-queue-guard.mjs`** — its references-tier record
reader takes the predicate through the same lazy recogniser import
(fail-closed on a rename: a missing export is `available: false`),
carries the flag on the record so the renderer never re-decides it, and
its merge-queue refusal prints no identifier either. Remedy 3 now spells
the token as the NAME.
⛔ **Neither gate is weakened.** The line is still required, a missing
line is still a refusal, the comparison is still EXACT — no family
match, no prefix floor — and the accepted token is still exactly one.
The only behavioural delta is *which* single token, plus one **new**
refusal class.
## Tests
Self-test batteries, on `57132927c`. ⛔ No case deleted — 15 cases were
re-spelled and the rest are additions:
| battery | before | after | delta |
|:--|--:|--:|--:|
| `check-clause2-carriers --self-test` | 598 | **605** | +7 |
| `check-governed-queue-guard --self-test` | 229 | **233** | +4 |
The 598 baseline was measured by running `origin/main`'s own copy of the
file in this tree. The 229 is derived (4 added `assert(` calls, 0
removed), because that file's copy cannot be run against a modified
sibling — its fixtures are the thing this PR changes.
New cases include: the constant's VALUE is refused; the refusal never
quotes it back; the refusal names `AGENTS.md`'s rule; a never-shipped id
binds too (a SHAPE, not a list); the two refusals differ exactly on
quoting; and the row is not widened.
**Gate sweep** — `node scripts/pm/dispatch-gates.mjs --commands` derived
43 families from the three changed paths; all 43 run, recorded with
`--ran`:
```
✓ dispatch-gates --ran: 43 derived famil(ies) accounted for — 43 run,
0 NOT-MEASURED (a DERIVED zero — all 43 recorded an exit code and none of them is 3).
```
`pnpm --filter @objectstack/lint run check:doc-formula-expressions`
first returned **exit 3 — PREREQUISITE NOT MET** (unbuilt
`@objectstack/formula` / `@objectstack/lint`, ⛔ not a finding); after
`turbo run build` for those two packages under the shared verify lock it
returned exit 0.
**Lint** — the full repository run, not a narrowed union: `pnpm exec
eslint . --no-inline-config --format json` over **6741 files**, **0
errors, 0 warnings**, exit 0.
## Legacy count — a reading for the seat, ⛔ not a work item here
`origin/board-archive` at `bd7bbf53b` (snapshot 2026-09-13T20:23Z), over
the archived comment bodies:
| probe | count |
|:--|--:|
| `Served-tier` | **0** |
| lit control — `Reviewed-by` | 88 |
| lit control — `CONTRACT_REVIEW_TIER` | 303 |
⚠️ **Read this zero narrowly.** Two corrections to how it was specified:
- The prescribed path `-- archive/` does not exist on that branch — a
grep there returns 0 for the wrong reason, with the lit control ALSO
reading 0. The archive lives under `board/` (11,593 files). The table
above is the `board/` reading, with the instrument lit.
- The archive's highest card is **#17600**, while the `Served-tier:`
rule landed with **#17990** on 2026-09-13. ⇒ the archive's window
**predates the convention entirely**. The zero says the archive does not
reach the window, ⛔ **NOT** that no live comment carries the line. The
card's 「how many comments across the fleet carry the line」 stays
unmeasured, and this PR migrates nothing.
## Landing note
This PR's own governed surface is
`.claude/skills/pm-dispatch/references/contract-review.md` —
`check-governed-merges.mjs --test` confirms it hits the register, so the
PR is **draft-only and human-merged**, or lands on the references tier's
review of record.
⚠️ **The review of record on THIS PR must use the NEW form** —
`Served-tier:` naming `CONTRACT_REVIEW_TIER`, ⛔ not its value — because
the merge-group leg runs **this PR's own guard**. A record written in
the old spelling is refused by the code this PR ships.
## Acceptance notes
**Open question surfaced by triage, ⛔ NOT decided here, and ⛔
`AGENTS.md` untouched.** Triage named a class the rule as written does
not distinguish: **a preserved verbatim maintainer ruling that happens
to contain an identifier** vs. **an identifier a seat emits as its own
artifact**. This PR only removes the second kind. A blanket cleanup that
rewrote preserved maintainer quotes would be worse than the problem
(座位制度原话照抄不译). Whoever reconciles `AGENTS.md` should decide that
explicitly.
Out of scope, noted and not filed:
- **238** occurrences of the constant's literal value already sit in
archived comment bodies under `board/`. That is a pre-existing fact
about historical compliance with the `AGENTS.md` rule, not about
`Served-tier:` (which reads 0 there) — and no migration rides on this
PR.
- The C7 battery feeds the refusal an id **nobody has shipped** to prove
it binds a SHAPE. Spelling it lands no identifier because it identifies
no model — the same device `check-commit-card-trailers.mjs`'s own
battery uses. A comment beside the case now says so.
- `scripts/pm/dispatch-gates.mjs` is untouched: it is a `Restart-touch:`
trigger file of hold #14290, and the fix did not need it. The constant
keeps its single value site there.
No changeset: the diff publishes nothing from any released package —
`.claude/**` and `scripts/pm/**` are on the fast track, and no path in
the diff appears in any package's `files[]`.
`Clause-②: no`
## 维护者速读(草稿)
**改了什么。** 契约复核记录里那行
`Served-tier:`,原先要求写模型档位常量的**值**(一串型号标识);现在改成写常量的**名字**。两个门禁(`check-clause2-carriers.mjs`
C7、`check-governed-queue-guard.mjs` 的 references
档记录读取器)同步改判,并新增一条拒绝:凡写成型号串的一律拒,且拒绝文案**不回显**那个串。
**为什么改。** `AGENTS.md` 明写「no model identifier lands in … **a comment**
…」,而复核记录就是一条 GitHub 评论。优先序 `AGENTS.md` >
座位惯例,分诊已定向。关键是这笔交易**零成本**:协议本身就说自述档位不是读数,真凭据是座位的转录 grep,那个动作不落任何仓库产物 ——
所以删掉型号串不损失任何证据价值。
**风险与代价(含回滚)。** 风险低但有一个真实的过渡成本:**本 PR 自己的复核记录必须用新拼写**,因为 merge-group
那条腿跑的就是本 PR 带的守卫;用旧拼写写的记录会被拒。存量迁移不在本 PR(板存档读数
0,但那份存档的窗口早于本约定,所以「舰队里有多少条评论带旧拼写」仍未测)。回滚 = revert 两个
commit;门禁与规则文本同笔回到旧拼写,无数据迁移、无发布面。两侧门禁都**只收紧不放宽**:该行仍必填,缺行仍是拒绝。
**席位意见。**
**你要做的。** 确认「预留原文维护者裁决里出现的型号串」这一边界情形该怎么定 —— 本 PR 只清理座位自己产出的那一类,⛔ 没有动
`AGENTS.md`,也⛔ 没有改写任何被原样保留的裁决引文。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7010085 commit 66e34d1
3 files changed
Lines changed: 219 additions & 70 deletions
File tree
- .claude/skills/pm-dispatch/references
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
| 29 | + | |
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| |||
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
56 | | - | |
| 56 | + | |
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| |||
0 commit comments