Repository navigation
feat(pm): teach the merge-queue guard the governed surface references tier - #18036
Conversation
…es tier The maintainer tiered the governed surface on 2026-09-13 and the charter text landed with PR #18018, but the queue guard still demanded an authorized approval for every governed path — so the tier was declared and not enforced. The merge-group leg now classifies each governed pull request's governed paths and, when they ALL lie under `.claude/skills/pm-dispatch/references/`, accepts the skills seat's review of record on the CURRENT head in place of the approval: a `## Contract review` comment on the PR thread carrying a `Reviewed-by:` line and a `Served-tier:` reading that stands. Every other governed path is the rules layer and keeps today's predicate byte-for-byte — 16 predicate bodies are md5-identical to their pre-change selves. Recognition is IMPORTED, never re-implemented: the heading marker, the head-sha span test, the newest-of resolution, the `Reviewed-by:` key line and the `Served-tier:` reader all come from the two files that own them. Reaching them needed the module-eval cycle solved rather than worked around, so the import is lazy and this file's dispatch no longer carries a top-level await; the battery pins that precondition against this file's own source. The leg is MONOTONE by construction — consulted only for an entry no authorized approval satisfied — so it can lift a refusal and can never create one. Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewHead: ① derived judgments — the merge-group leg gains the references tier the charter declared in PR #18018; the rules layer keeps today's predicate:
Seat measurements on the head tree ( ② semver: ③ boundary flags: Implemented-by: Verdict: PASS — the tier the charter declared is now enforced on the merge-group leg, monotone, with imported recognition and a fail-closed load. Generated by Claude Code |
…identifier — AGENTS.md's comment rule (objectstack-ai#18060) (objectstack-ai#18087) Fixes objectstack-ai#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, objectstack-ai#17990) and `60b99552a` (2026-09-13T15:00Z, objectstack-ai#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 **objectstack-ai#17600**, while the `Served-tier:` rule landed with **objectstack-ai#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 objectstack-ai#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>
…line
Rework of the two one-line defects the at-tier contract review returned FAIL
on. Both are text; no behaviour, constant, register row, tier function or
self-test expected value moves.
- .claude/skills/pm-dispatch/references/landing-operations.md:27 named the
SURFACE ("Tier S(.claude/** 全树)者") and dropped the PR-level ALL
quantifier that its own predecessor ("受管路径全在 … 者"), this repair's
prescribed wording and the sibling contract-review.md:46 all carry. As
written, :27 and :28 partitioned governed SURFACES rather than pull
requests, so a mixed diff (.claude/** plus AGENTS.md, Tier H by the
register's ALL-not-ANY rule) matched both lines with no tiebreak on the
page. Restored to "受管路径全在 `.claude/**` 者 Tier S:…" — 93 B → 105 B
against the 120 B cap, line count 69/69 unchanged.
- scripts/pm/check-governed-queue-guard.mjs:4442-4443, the --self-test
SUCCESS line printed on every run, still stated the superseded #18020
references-tier population ("a governed diff whose governed paths all lie
under the one ruled prefix") while the battery at :3949-3950 asserts
the-old-references-boundary-is-GONE. Landed 2026-09-13 in #18036 and
untouched since, so the file's docblock repair left the one instance seats
actually read. The population is re-keyed to Tier S — the register's
.claude/** row, asked through governedTierFor — with the #18020 naming
kept as history, matching the docblock's own form. 296 cases unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>
…-tier boundary (objectstack-ai#19379) Part of objectstack-ai#19146 Clause-②: no Two carriers of the governed-tier rule still stated the 2026-09-13 boundary (Tier S / "fact layer" = only `.claude/skills/pm-dispatch/references/**`) that objectstack-ai#19133 (2026-09-18) superseded. objectstack-ai#19133's ruling, verbatim and untranslated: maintainer 「同意改规则。」 on the skills seat's proposal, plus the amendment that folded `.claude/settings.json` and `.claude/hooks/**` in too: 「我觉得这些我也没必要确认」. Tier S is now the whole `.claude/**` tree. Current, correct source of truth (unchanged by this PR): - `scripts/pm/check-governed-merges.mjs` register row `{ id: 'claude-tree', prefix: '.claude/', glob: '.claude/**', tier: GOVERNED_TIER_S, … }`, pinned by self-test case `skills-agents-and-the-fact-layer-are-Tier-S`. - `.claude/skills/pm-dispatch/SKILL.md:625-626`: 「受管面两层:Tier H(规则层)= `AGENTS.md`+`CLAUDE.md`+`docs/adr/**`+`docs/NORTH-STAR.md`+发布 `skills/**`。」「Tier S = `.claude/**` 全树;Tier H 四件套等人批;Tier S 经席内达档复核 PASS 在案后 ready → 入队。」 ## What was stale **`.claude/skills/pm-dispatch/references/landing-operations.md:27-28`** Before: ``` - 受管路径全在本技能 `references/` 者事实层:席内达档复核过落地前检三条即转正式入队。 - 其余为规则层:四件套留 draft 等人批,⛔ 不翻正式不入队;获授权批准后认领席落地。 ``` After: ``` - Tier S(`.claude/**` 全树)者:席内达档复核过落地前检三条即转正式入队。 - Tier H(其余受管面)者:四件套留 draft 等人批,⛔ 不翻正式不入队;获授权批准后认领席落地。 ``` Line count and byte ceiling unchanged (69/69, both lines within the 120-byte cap — `check:pm-skill-ratchet` verified). **`scripts/pm/check-governed-queue-guard.mjs`, the "THIRD leg" header (~268-294)** It reproduced the 2026-09-13 boundary and concluded: "Every other governed path is the rules layer and keeps the predicate above byte-for-byte" — false since objectstack-ai#19133, and self-contradicting the same file's own later "the landing TIER" section, which already records that `REFERENCES_TIER_PREFIX` "is gone". Fix: the quoted 2026-09-13 ruling is kept, untranslated, as the ruling that STARTED this leg (history is load-bearing — a reader who finds that text must see why it no longer governs). A new paragraph marks it SUPERSEDED by objectstack-ai#19133 (cited with date, reusing this same file's own existing verbatim quote of the amendment for consistency) and points at `governedTierFor` / the register / `node scripts/pm/check-governed-merges.mjs --test <paths>` instead of a prefix to remember. The concluding sentence now reads "Every governed path outside Tier S is Tier H, the rules layer, and keeps the predicate above byte-for-byte." No behavior changed: `GOVERNED_SURFACES`, `governedTierFor`, `landingTierOf`, every tier constant and every self-test assertion's expected value are untouched — only the two stale prose passages. ## Verification before editing - Read objectstack-ai#19133 on GitHub directly (the tracking card/comments): maintainer ruling 「同意改规则。」 plus the amendment 「我觉得这些我也没必要确认」, landed by PR objectstack-ai#19144 (merged `1047fe101`), matches this PR's premise exactly. - Read the register row, its self-test case, and `SKILL.md:625-626` — all current and correct, confirmed unedited. - Read both stale passages in full context before editing. ## Tier verdict on this PR's final file list ``` node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/references/landing-operations.md scripts/pm/check-governed-queue-guard.mjs ``` → `GOVERNED — Tier S`, exit 3. `landing-operations.md` hits the `.claude/**` register row; `check-governed-queue-guard.mjs` is not itself a registered surface (1 of 2 paths governed). Per Prime Directive objectstack-ai#14, this PR lands on the owning seat's Tier S contract-tier review of record — no seat approves it, and no maintainer click is waited for. ## Gates run (exit code captured before any pipe) - `node scripts/pm/check-governed-queue-guard.mjs --self-test` — exit 0 (296 cases pass) - `node scripts/pm/check-governed-merges.mjs --self-test` — exit 0 (435 assertions) - `pnpm check:pm-skill-ratchet` — exit 0 (landing-operations.md 69/69, headroom 0, unchanged) - `pnpm check:pm-skill-id-lint` — exit 0 (27 files clean) - `pnpm check:pm-governed-prose` — exit 0 (names all 6 registered surfaces) - `pnpm check:nul-bytes` — exit 0 - `npx eslint scripts/pm/check-governed-queue-guard.mjs` — exit 0 - `node --check scripts/pm/check-governed-queue-guard.mjs` — exit 0 ## Changeset `skip-changeset` — no `packages/*` touched; neither `.claude/skills/**` nor `scripts/pm/**` ships in any package's `files[]` (same as precedent PRs objectstack-ai#19144 and objectstack-ai#19021). ## On the card This PR is filed as **Part of objectstack-ai#19146**, not a new card: objectstack-ai#19146 ("skills: re-key the three 事实层 = references/ spellings the Tier S ruling leaves false") was already open, filed by the seat that landed PR objectstack-ai#19144, and its item 2 is exactly `landing-operations.md:27-28`. Creating a new duplicate card would have contradicted this repo's own duplicate-avoidance practice, so none was created. objectstack-ai#19146's other items — `.claude/agents/os-dev.md:286-287`, `check-half-states.mjs` H48 and `check-half-states.mjs` H43 — are **not** touched by this PR and remain open on that card; neither is `SKILL.md:608`'s own `事实层` wording (added to objectstack-ai#19146 by its own addendum comment). H43 is the newest of them: it had lived only in card comment 5750573385 and is enumerated on the card body as item 4 by this rework. It is left here deliberately — H43 is missing LOGIC in a non-governed instrument (a LAZY `governedTierFor` load, because that row travels to sibling repos, plus one `pnpm check:pm-half-states` self-test case), which is the same change class as H48 and rides with it in ONE half-states PR rather than under a docs-only Tier S record. The at-tier review of record (`5751616940`) ruled this PR NOT incomplete for leaving it there. This PR additionally fixes `scripts/pm/check-governed-queue-guard.mjs`'s self-contradiction, which is not named in objectstack-ai#19146 at all. ## Note on the dispatching brief The brief that generated this PR stated "this repair has no card yet." That is not accurate: objectstack-ai#19146 already existed (filed 2026-09-18, still open) covering part of this exact repair. Everything else in the brief — the ruling text, the register row, the self-test name, `SKILL.md:625-626`, and both stale passages — verified exactly as stated on direct reading. ## Rework after the at-tier contract review (record `5751616940` — FAIL) Head `fa628d0b36` → `71216fcff6`, one commit on the same branch (⛔ no rebase, no amend, no force-push — the review record is anchored to this branch's history). Both defects are TEXT: ⛔ no tier constant, no `GOVERNED_SURFACES` row, no `governedTierFor`, no `landingTierOf` and no self-test expected value moved. Self-test case counts are unchanged at 296 / 435. **1. `landing-operations.md:27` — the PR-level ALL quantifier is restored.** The line shipped as 「- Tier S(`.claude/**` 全树)者:…」, which names the SURFACE. Its own predecessor (「受管路径全在本技能 `references/` 者事实层」), this repair's prescribed wording on the card, and the sibling `contract-review.md:46` (「受管路径全在 Tier S 面(`.claude/**`)者」) all carry the quantifier. Without it, lines 27 and 28 partition governed SURFACES rather than pull requests — so a mixed diff (a `.claude/**` path plus `AGENTS.md`, Tier H by the register's ALL-not-ANY rule) matched both lines with no tiebreak on the page. ```diff -- Tier S(`.claude/**` 全树)者:席内达档复核过落地前检三条即转正式入队。 +- 受管路径全在 `.claude/**` 者 Tier S:席内达档复核过落地前检三条即转正式入队。 ``` Re-measured here, not taken on trust: 93 B → 105 B against the 120 B cap, file 69/69 lines with headroom 0 (`check:pm-skill-ratchet` exit 0 names the file at 69/ceiling 69). **2. `check-governed-queue-guard.mjs:4442-4443` — the `--self-test` SUCCESS line is re-keyed.** The docblock repair in the first commit left the one instance seats actually read: the SUCCESS line printed on EVERY run (it is in the review's own capture) still stated the superseded objectstack-ai#18020 population, while the battery at `:3949-3950` asserts `⛔ the-old-references-boundary-is-GONE`. Landed 2026-09-13 in objectstack-ai#18036 and untouched by objectstack-ai#19144 — present at merge-base and at the reviewed head, reproduced here before the edit. ```diff - 'the boundary a label reader cannot cross — and the objectstack-ai#18020 references TIER: a governed diff whose governed ' + - 'paths all lie under the one ruled prefix lands on the skills seat\'s review of record instead of an ' + + 'the boundary a label reader cannot cross — and the objectstack-ai#18020 references TIER, re-keyed to Tier S by objectstack-ai#19133: a ' + + 'governed diff whose governed paths are ALL Tier S — the register\'s `.claude/**` row, asked through ' + + '`governedTierFor`, never a prefix repeated here — lands on the skills seat\'s review of record instead of an ' + ``` History stays (the `objectstack-ai#18020` naming), exactly as the docblock keeps its quoted ruling; only the POPULATION is re-keyed. Proof it is gone from the PRINTED output, not merely from the source: `--self-test` at the new head prints `the one ruled prefix` 0 times and the re-keyed sentence once. ### Still stating the superseded boundary — reported, ⛔ deliberately not pulled in The review lists these as live and OUT of this PR's scope, and this rework leaves them exactly as it found them: `.claude/agents/os-dev.md:286-287` (this card's item 1) and `SKILL.md:608`'s 「⛔ 无事实层例外」 (a card addendum, vocabulary only — the rule itself stays true). Naming-only uses of "the references tier" as this leg's NAME (queue-guard `:268`, `:358`, `:442`, `:452`, `:646`, `:736`, code comments `:1420` / `:1546` / `:2261` / `:2470` / `:2560`, and `check-clause2-carriers.mjs:8635`) are an optional tidy and were left alone: widening the diff of a docs-only record to sweep names is not what the FAIL asked for. ### Tier verdict on the FINAL file list ``` node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/references/landing-operations.md scripts/pm/check-governed-queue-guard.mjs ``` → `⛔ GOVERNED — Tier S(席内达档复核落地)`, exit 3; 1 of 2 paths on the register (`.claude/**` ×1 — `landing-operations.md`; `scripts/pm/check-governed-queue-guard.mjs` is not a registered surface). File list unchanged from the reviewed head, so the tier is unchanged. Per Prime Directive objectstack-ai#14 this lands on the at-tier review of record — ⛔ no seat approves it and no maintainer click is owed. ### Gates at the new head (exit code captured BEFORE any pipe) The brief's minimum, plus every family `node scripts/pm/dispatch-gates.mjs --commands` derives for this change set — 38 commands, 37 at exit 0: - `check-governed-queue-guard.mjs --self-test` — exit 0, 296 cases (unchanged) - `check-governed-merges.mjs --self-test` — exit 0, 435 assertions (unchanged) - `check:pm-skill-ratchet` exit 0 (69/69, headroom 0) · `check:pm-skill-id-lint` exit 0 (27 clean) · `check:pm-governed-prose` exit 0 (6/6 surfaces, 28 self-test cases) · `check:skill-frame-sync` exit 0 · `check:nul-bytes` exit 0 (9053 files, no raw control bytes) - `node --check` exit 0 · `npx eslint scripts/pm/check-governed-queue-guard.mjs` exit 0 (1 file linted, 0 errors, 0 warnings, read from `--format json`) - `check:pm-dispatch-gates`, `check:pm-governed-merges`, `check:ratchet-remedy-authority`, `check:doc-authoring`, `check:cross-package-test-inputs`, `check-declaration-mirrors`, `check-scripts-symbol-anchors`, `check-self-test-wired`, `check-self-test-workflow-commands`, `check-comment-mask-corpus` and the rest of the derived list — all exit 0 - ⊘ NOT MEASURED — `pnpm --filter @objectstack/lint run check:doc-formula-expressions` exit 3, PREREQUISITE NOT MET (`@objectstack/formula` and `@objectstack/lint` unbuilt in this worktree). Exit 3 is this repo's NOT-MEASURED code, ⛔ not a finding; the family's population is docs formula expressions, disjoint from this diff's two paths, and CI runs it against a built tree.⚠️ `dispatch-gates.mjs` prints a STALE TREE warning: this branch is ≥55 commits behind `origin/main` and 15 files the derivation reads changed across that range. The gate list above is therefore derived from this branch's tree, which is what the review record is anchored to; ⛔ it was not refreshed by a rebase. CI on the merge group derives from the merged tree. ###⚠️ `check-clause2-carriers.mjs --pair 19379` reads 2 (UNJUDGED) — measured, and it is the BRANCH NAME Reproduced at the new head: `PM_SWEEP_REPO=objectstack-ai/objectstack node scripts/pm/check-clause2-carriers.mjs --pair 19379` → exit 2, 「the card's NEWEST claim comment (5754245926) matches the claim marker but its `Branch:` directive parses to ZERO branches」. The stored line 2 really is `` Branch: `claude/pm-superseded-references-tier` `` on a line of its own, so the printed remedy — "name the branch on a `Branch:` line of its OWN" — is already satisfied and cannot clear it. The cause is not the regex named in the dispatching brief. `BRANCH_TOKEN` (`check-clause2-carriers.mjs:3278`) reads the `Implemented-by:` VALUE of a review record; it never sees a claim's `Branch:` directive. That directive is read by the sibling `check-half-states.mjs:5407` `claimedBranches`, through `CLAIM_BRANCH_SHAPE` (`:5358`): ``` /claude\/issue-\d+-[A-Za-z0-9][A-Za-z0-9._-]*/g ``` which REQUIRES a literal `issue-` plus digits segment. Measured on the real stored comment body and two controls: | input | `claimedBranches()` | | --- | --- | | the live comment `5754245926`, as stored | `[]` | | the same comment, branch swapped to `claude/issue-19146-superseded-references-tier` | `["claude/issue-19146-superseded-references-tier"]` | | `Branch: `claude/issue-abc-slug`` (no digits) | `[]` | Only the branch NAME differs across those rows, so the marker, the backticks, the line position and the directive shape are all fine. `claimGovernance` on that one-comment thread returns `governing: null` with `malformed: { id: 5754245926 }` — which `cardDeclaration` turns into `claim-branch-unparsed`, i.e. exit 2. The narrow shape is DELIBERATE where it was written (`CLAIM_BRANCH_SHAPE`'s own docblock: a `Branch:` line naming some other shape "is deliberately left unmatched, which puts the card out of this row's scope entirely" — under-reporting beats manufacturing findings out of typos). The consequence in THIS reader is not out-of-scope, though: Prime Directive objectstack-ai#14 makes `--pair` at 0 part of the Tier S landing predicate, so a Tier S PR on a branch without an `issue-`digits segment cannot satisfy it by any act of the claiming seat short of renaming the branch — which would strand this review record. ⛔ Not repaired here: `check-clause2-carriers.mjs` is ⛔ out of this PR's scope and the branch is ⛔ not renamed. Reported for the seat. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 --- _Generated by [Claude Code](https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2)_ --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #18020
Part of #17950. The maintainer tiered the governed surface on 2026-09-13 (「我点头」) and the charter text landed with PR #18018, but
check-governed-queue-guard.mjsstill demanded an authorized approval for every governed path — the tier was declared, not enforced. The merge-group leg now learns it.What changed
A governed pull request whose governed paths all lie under
.claude/skills/pm-dispatch/references/is satisfied by the skills seat's review of record on the current head — a## Contract reviewcomment on the PR thread carrying aReviewed-by:line and aServed-tier:reading that stands — in place of the approval. One rules-layer path in the same diff and today's predicate is the only way through.Monotone by construction. The tier leg is consulted only for an entry no authorized approval satisfied, so it can lift a refusal and can never create one. Nothing that passes the queue today newly refuses.
Recognition is imported, never re-implemented — the heading marker, head-sha span test and newest-of resolution (
check-half-states.mjs), theReviewed-by:/Served-tier:readers (check-clause2-carriers.mjs). Zero new parsers;check-clause2-carriers.mjscarries one export-only change (const→export const REVIEWED_BY_LINE, value expression md5-identical).Acceptance greps (both directions)
REFERENCES_TIER_PREFIXin the guardreadServedTier/servedTierStandsimportedGOVERNED_APPROVERS(lit control — still hits)Two measurements the route turned on
check-clause2-carriers.mjs, which imports H31's file. So the import is lazy, which is legal only because this file's dispatch no longer carries a top-levelawait. That precondition is pinned against this file's own source; ablation D (restoringawait main()) reds exactly that one case, and ablation C (in the self-test dispatch) reproduces the exit-13 deadlock.GET /repos/{o}/{r}/issues/{n}/commentsanswersX-Accepted-GitHub-Permissions: issues=read; pull_requests=read, and GitHub documents the semicolon as separating alternative permission sets. The workflow's existingpull-requests: readis sufficient; ⛔ no workflow change.Reverse verification (mutate → prove on disk → run → restore)
entrySatisfiedaccepts any record stateawaitin the self-test dispatchawaitin the live dispatchEach leg proved its mutation on disk before running, and its restore by blob hash against
HEAD.Acceptance notes
AGENTS.mdPD feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14's sentence 「the queue guard refuses an unpinned governed diff」 stays true: a references-only PR is pinned by its record. No prose changed;check:pm-governed-proseis not in the derived set for this diff and is green when run anyway.makeLabelReader's docblock says the issues-labels route "needsissues: read, which this workflow does not grant". The live API answersissues=read; pull_requests=readfor it too, so the stated reason is stale — the choice to read the pull object is still right (it reuses a call the leg already makes). Successor: whoever next edits that reader, in this same file.Authored by the
domain:skillsseat, sessionsession_01DAcomhvR9kKizeYgg89Vo8.Gates (all at
f088df57)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 37 families; all 37 run, every one with its exit code captured before any pipe, all 0.--ranreconciles: 37 derived, 37 run, 0 NOT-MEASURED (a DERIVED zero), 0 UNRUN. Includescheck:pm-dispatch-gates(1682 self-test cases),check:pm-clause2-carriers,check:nul-bytes,check:refd-timer-probe,check:closing-target-claim,check:whole-set-label-write, and the guard's own--self-test(229 cases).check:pm-governed-proseis not in the derived set for this diff — no prose surface changed — and was run anyway: green, 2 instruction surfaces name all 5 registered governed surfaces and claim no others.eslint . --no-inline-configran the whole population rather than a narrowing: 6722 files, 0 errors, 0 warnings. NoparserOptions.projectand no typed rules are configured, so no untouched file's verdict can move with this diff.Generated by Claude Code