Repository navigation
pm(tooling): the contract-review tier is fable — revert the opus constant and the retired-word list - #19684
Conversation
…tant and the retired-word list A temporary lack of authorization on ONE session is not a tier retirement. The 2026-09-21 change moved CONTRACT_REVIEW_TIER to the opus id and filled RETIRED_TIER_WORDS on an HTTP 429 quota refusal; the maintainer's ruling of 2026-09-22 reverses it. The constant reads the fable id again, no tier word is retired, and the derived ceiling, the guard and every rendering that reads the constant are kept as they are — they were never the defect. The retired-spelling guard's non-vacuity pin cannot hold on an empty list, so it moves off the live list onto a MUTATED copy: a list naming a word the ladder really prints must red. Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: ① Derived judgmentsRendered in-seat by the skills seat 1 session at the served tier the maintainer named on this date (the fable tier word at
② Semver levelNone — no release. ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
… double carrier, independence requirement (step ②) (objectstack-ai#19694) Part of objectstack-ai#19061 Clause-②: no Step ② of the ruled three-step order on the clause-② contract review (ruling 5770886272, letter B; scope narrowed by the maintainer's word of 2026-09-22, 5772289798: 「降档保险丝 不留」). Step ① landed as PR objectstack-ai#19660. Step ③ (the `needs:contract-review` label itself and every script that names it) is a later round and is NOT in this PR — objectstack-ai#19061 remains open. ## 维护者速读(草稿) **改了什么**:按裁决 B 的第 ② 步,把技能文本里守「条款②契约复核」的三层机器删掉两层半:`needs:contract-review` 标签的挂与清纪律、`--pair` 入队前检、双载体、以及「独立性对」作为硬性要求;`references/contract-review.md` 的〈载体纪律〉与〈降档保险丝〉两节整节删除(您的原话「降档保险丝 不留」),该文件 60 行缩到 24 行;SKILL.md、core-rules.md、platform-readings.md 各删或修剪对应句子(净减 44 行);`packages/spec` 错误码台账源文件顶部注释里那句话去掉标签名(「登记新码是条款②改动」的表述保留),生成的参考页同步重生成,并附一条 `@objectstack/spec` 的 patch changeset(这段注释随 `.d.ts` 与源码一起发布)。`platform-readings.md` 里「强制档不因临缺授权降档」这条原则按复核要求以独立一行恢复(不再提保险丝)。`scripts/pm/dispatch-gates.mjs` 自检里原来钉住〈降档保险丝〉两句话的两条断言,换成一条「这两句话不得回流」的守卫。留下的只有一条 `## Contract review` 记录作为 Tier S(`.claude/**`)的落地记录,队列守卫继续读它。 **为什么改**:您 9-18 说「不需要 AI 不能自审契约改动 这种」,9-22 批准了 B 方案(三层收成一层):标签、`--pair`、记录三层守的是同一件事,多出来的两层是纯成本;席内达档复核就是 Tier S 的落地方式。 **风险与代价(含回滚)**:规则文本删除加一处自检断言改写,不改任何门禁的判定逻辑;自检在本分支最终 head 上实测 1905/1905 通过。回滚 = revert 本 PR(纯文本加一条自检守卫,无迁移)。仍有六处 `.claude/` 散文与 AGENTS.md 一处仍点名旧机器,列在下文,归步骤 ③。 **席位意见**:技能席 1(`session_01Wnstp2kTth7sGXfr8fXypc`)已按 GitHub 与拉取的 head `c7ea483b8` 复核两轮。第 0 轮记录 FAIL 5773967201 只欠两件:`platform-readings.md` 被删的原则行须以不带保险丝名的独立规则补回(≤120 字节),以及 `@objectstack/spec` 的 patch changeset(docblock 会进发布的 `.d.ts`)。补丁轮 1 恰好只做了这两件——PR 路径上两轮之间的 diff = +6(一行 119 字节的补回行、五行 changeset),`origin/main` 的合并未触及 PR 路径,`dispatch-gates.mjs` 与第 0 轮逐字节相同。本席在独立 worktree 复跑 ratchet(SKILL.md 815/819 · contract-review.md 24/60 · platform-readings.md 468/469 · core-rules.md 151/151)、skill-id-lint、frame-sync、governed-prose、doc-authoring、nul-bytes、三个 changeset 门禁、token ratchet 均 exit 0;第 0 轮头上 1905 例 dispatch-gates 自测 exit 0(脚本两轮未变),本头由本席另起后台复跑。`skip-changeset` 已由本席摘掉,`Check Changeset` 随即跑绿,CI 35 项全绿。复核记录 PASS 5774400593(席内 fable),`--pair` 0,ACCEPT 5774446589 在卡上。登记簿读 Tier S,但裁决 B 写的是 Tier H——以您的裁决为准,PR 留 draft 等您的手;若您要本席按 Tier S 入队,一句话即可。步骤 ③(删标签、队列卫士的标签腿、六处散文残留与指针)另开一轮。 **你要做的**:合并这个 draft PR(受管面,按您定的方式落地)。 ## What this PR does - `.claude/skills/pm-dispatch/references/contract-review.md` (60 → 24 lines): the 〈载体纪律(挂与清)〉 and 〈降档保险丝(机读)〉 sections are deleted whole. 〈复核归属与资格(按面)〉 keeps who renders the record, its shape (`Served-tier:` = the constant name, the head sha, `Implemented-by:`/`Reviewed-by:` as record lines, PASS/FAIL) and the landing pre-check, which is now two items (① the at-tier record on file, ② every check green); the pure-regeneration exception line moves under ①. The title and the pointer line are reworded to what remains. No new sentence was added: the current-head binding of the record is stated by AGENTS.md Prime Directive objectstack-ai#14 and enforced by the queue guard, so the moved exception line stands on that. - `.claude/skills/pm-dispatch/SKILL.md` (819 → 815): in 〈入队与落地〉 the bullets that hang the label, run `--pair N` on PR open, park the PR behind the label and point at the retired sections go; the Tier S landing sentence keeps 「PASS ⇒ ready、auto-merge;FAIL ⇒ 补丁轮;⛔ 免复核不放行」. Elsewhere only in-line trims: the label example and the gate-label clause in the label-write rules, the self-review-ban half of the PM tooling exception (the authorization-quote condition stays), the machine-read note on the record lines, the triage 「不挂」 clause, and the dangling 「细则见」 pointer on the subagent-verdict rule. - `references/core-rules.md` (151 → 151): the two mirrored clauses trimmed (自审自合; 闸门标签同此). - `references/platform-readings.md` (469 → 468): the `--pair` proxy reading and the fuse-named rule go; the three readings whose subject was the fuse keep their platform fact (get_session semantics, the minute-wall example). The principle the fuse rule carried comes back as a standalone line without the fuse name (patch round 1, the review's ①(b)): 「强制档不因临缺授权降档:临缺非退役,退役唯维护者裁;不达档起隔离达档子代理或等档。」 (119 bytes, at :393 in the served-tier readings block). The `needs:contract-review` mergeable-state reading at :26 stays this round — it is a reading about a label that still exists until step ③. - `packages/spec/src/api/error-code-ledger.zod.ts` docblock :102: 「Registering a code widens this face and is therefore a Clause-② change, door or no door」 — the label name is gone, the Clause-② statement stays. `content/docs/references/api/error-code-ledger.mdx` regenerated by `gen:docs` (the diff is exactly that sentence). `.changeset/19061-error-code-ledger-docblock-label.md` declares a `patch` for `@objectstack/spec` (patch round 1, the review's ①(e)/②): the docblock ships in the `.d.ts` declaration chunks and the `.zod.ts` source is in the package's `files[]`, so the diff publishes bytes from a released package — `AGENTS.md`'s changeset clause is the stricter one over the comment fast lane. `Clause-②: no` stands (no accept / reject behaviour, no public-surface change). - `scripts/pm/dispatch-gates.mjs` (the coordinator's mid-run increment; the file is a self-test, not a gate's logic): its self-test read `references/contract-review.md` and pinned two sentences of the deleted fuse section in two `t(…)` cases — 「…and its fuse keeps the quota exemption pointed at DISPATCH, never at the review」 and 「…and carries the case it never had: a RETIRED tier is not an exhausted one, and it is a maintainer ruling, ⛔ never a seat's reading」. They are replaced by ONE retirement guard in the file's own pattern — 「the rulebook no longer carries the retired 降档保险丝 lines (maintainer 2026-09-22 「降档保险丝 不留」, objectstack-ai#19061 comment 5772289798)」 — which asserts the new truth and reds if the text creeps back; the `fuseRules` read and the `CONTRACT_REVIEW_TIER` case stay as they were. A plain removal of the two cases was made and measured first (1904/1904 on the merged head) but committing it was refused by the harness's action policy as a test removal; the guard form is what landed. ## Inventory — every grep hit on the base (`97f4f8c828`), its disposition and where it lands | base hit | what | disposition | after | |---|---|---|---| | SKILL.md:25 | 自审自合 on the PM tooling exception | TRIMMED (independence half goes; authorization-quote clause stays) | 25 | | SKILL.md:155 | gate-label hang/clear example (`needs:contract-review`) | DELETED (+ :154 gate-label clause trimmed) | — | | SKILL.md:239 | record lines `Implemented-by:`/`Reviewed-by:`, 自审机读可见 | TRIMMED (machine-read note goes; record lines stay) | 238 | | SKILL.md:342 | ⛔ 不挂 `needs:contract-review` (triage) | TRIMMED (label clause goes; 裁定写方向 stays) | 341 | | SKILL.md:536 | 条款②自审资格按车道 (at-tier in-seat / isolated subagent) | KEPT (who renders the record) | 535 | | SKILL.md:635 | skills-lane own PR at-tier 自审 | KEPT (who renders) | 634 | | SKILL.md:656 | label hung by the seat on PR/card | DELETED | — | | SKILL.md:657 | `--pair N` on PR open, C5 | DELETED | — | | SKILL.md:659 | pointer naming 载体纪律 / 降档保险丝 | TRIMMED (pointer reworded to what remains) | 655 | | SKILL.md:773 | director duty title names the label | DEFERRED to step ③ (twin lanes/director.md:27 is off this surface) | 769 | | contract-review.md:3 | pointer: 载体纪律、归属资格与降档保险丝 | REWORDED to what remains | 3 | | contract-review.md:5–:22 | 〈载体纪律(挂与清)〉 | DELETED whole; :21 (pure-regen exception) MOVED | 22 | | contract-review.md:30 | record shape lists 独立性对 | KEPT (shape) | 12 | | contract-review.md:31 | `Served-tier:` = constant name | KEPT (shape) | 13 | | contract-review.md:35 | 独立性件 + 保险丝 ⇒ isolated subagent | DELETED (independence requirement + fuse) | — | | contract-review.md:38 | 独立性对(机读) values | TRIMMED (「(机读)」 goes; record-line values stay) | 19 | | contract-review.md:39 | SELF-REVIEW verdict | DELETED | — | | contract-review.md:40 | 清标即落地 / 剥双载体 | DELETED | — | | contract-review.md:43 | ② 双载体已清 via `--pair N` | DELETED (pre-check renumbered 三条 → 两条, ③ → ②) | — | | contract-review.md:44 | 确定性行 / C5 | DELETED | — | | contract-review.md:48–:60 | 〈降档保险丝(机读)〉 | DELETED whole (maintainer 「不留」) | — | | core-rules.md:23 | 自审自合 mirror | TRIMMED (mirror of SKILL.md:25) | 23 | | core-rules.md:40 | 闸门标签同此 (mirror of SKILL.md:155) | TRIMMED | 40 | | platform-readings.md:26 | label is a merge gate (mergeable reading) | KEPT this round — a reading about the label object; goes at step ③ with the label | 26 | | platform-readings.md:106 | 双载体清标 as a rate-limit example | TRIMMED (example goes; reading stays) | 106 | | platform-readings.md:120 | `--pair` proxy reading | DELETED (its general fact stays at :117–:119) | — | | platform-readings.md:385 | ⛔ 不作保险丝输入 | TRIMMED (fuse clause goes; reading stays) | 384 | | platform-readings.md:386 | ⇒ 保险丝无输入 | TRIMMED (fuse clause goes; reading stays) | 385 | | platform-readings.md:394 | 强制档不得因不可用而降档 (fuse rule) | REPLACED — the fuse-named sentence goes; the principle returns as a standalone line without the fuse name (patch round 1) | 393 | | AGENTS.md:278 | PD#14 `--pair N` / `Served-tier:` | OUTSIDE (not edited; step ③, the maintainer's file) | 278 | | error-code-ledger.zod.ts:102 | docblock names the label | REWORDED (Clause-② statement stays) | 102 | Two non-keyword neighbours were trimmed with their hits because they are the same discipline: SKILL.md:154's 「闸门标签被剥不是红灯是放行」 and SKILL.md:76's dangling pointer 「细则见 references/contract-review.md」 (the details it pointed at were the fuse's subagent-adoption lines). ## Line budget (ratchet ceilings untouched; every file at or under) | file | before | after | ceiling | |---|---|---|---| | `.claude/skills/pm-dispatch/SKILL.md` | 819 | 815 | 819 | | `references/contract-review.md` | 60 | 24 | 60 | | `references/core-rules.md` | 151 | 151 | 151 | | `references/platform-readings.md` | 469 | 468 | 469 | | `packages/spec/src/api/error-code-ledger.zod.ts` | 1679 | 1678 | — | | `scripts/pm/dispatch-gates.mjs` | +4 / −4 (comment lines and the one guard) | | — | | `.changeset/19061-error-code-ledger-docblock-label.md` | — | new, 6 lines (`@objectstack/spec`: patch) | — | Diff against `origin/main` (`7e1b048`, merged in at patch round 1; head `c7ea483`): 8 files, +32 / −70. ## File-surface increments beyond the claim (declared) 1. `content/docs/references/api/error-code-ledger.mdx` — the generated reference page carried the sentence; regenerated by `pnpm --filter @objectstack/spec gen:docs`, `check:docs` and `check:generated` (15/15) green. 2. `scripts/pm/dispatch-gates.mjs` — the two fuse-pin cases of its self-test, replaced by one retirement guard (described above). PR objectstack-ai#19684 (objectstack-ai#19680) landed on `main` and edited the same file in a different region; it is merged in. ## Verification (final head `c7ea483`, patch round 1) - Derivation: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` (no paths; the changed set read from the merge-base) → 132 families (the changeset gates joined: `check-empty-changeset` `--base` and `--self-test`, `check:objectui-changeset`, `check:pm-changeset-deadline-census`, `release-rehearsal-clone --self-test`). Reconciliation with `--ran`: 132 derived, 129 run, 3 NOT-MEASURED, 0 UNRUN. Every exit captured before any pipe. - The 3 NOT-MEASURED, each a prerequisite refusal (exit 3), none touched by this diff: `pnpm --filter @objectstack/spec run check:skill-examples` (reads `packages/client-react/dist`, not built), `pnpm check:dual-build-cjs-loads` (needs every package's dist), `pnpm check:lean-entry-closure` (needs `packages/objectql/dist`). A whole-repo build is CI's run, not this docblock-only spec change's. - `pnpm check:pm-dispatch-gates` at `c7ea483`, run detached with the exit recorded: exit 0, 「✓ dispatch-gates self-test: 1905 cases pass」, 709.7 s; the retirement guard and the 「spells NO model id」 walk over the 28 rulebook files (which read the restored line) are among them. Without the script change the same gate read 「✗ dispatch-gates self-test: 2 of 1904 case(s) failed」, exit 1 — the expected direction for prose a self-test had pinned. - Changeset gates at this head, all exit 0: `check-changeset-no-major --base origin/main` 「✓ This diff introduces no `major` bump.」; `check-adr-0087-registration --base origin/main` 「this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)」; `check-empty-changeset --base origin/main` 「No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added)」 and 「No changeset from the merge base modified or deleted」; `check-empty-changeset --self-test` (159 assertions); `check:changeset-gate-self-tests`; `check:pm-changeset-deadline-census`; `check:objectui-changeset`. - Named gates, all exit 0 at this head: `check:pm-skill-ratchet` (SKILL.md 815 / 819, contract-review.md 24 / 60, platform-readings.md 468 / 469, core-rules.md 151 / 151; every line within the 120-byte cap), `check:pm-skill-id-lint` (30 files clean), `check:skill-frame-sync`, `check:pm-governed-prose`, `check:doc-authoring`, `check:nul-bytes` (plus a direct grep of the seven changed files for the wider control-byte class: no match), `check-skills-token-ratchet` (34 files within ceilings), `check-skill-frame-freshness`, `check:pm-clause2-carriers` (1140), `check-governed-queue-guard --self-test` (312), `check:pm-half-states` (5160), `check:pm-expected-skips`, `check-published-list-mirrors`, `check-self-test-wired`, `check-scripts-symbol-anchors`, `check-declaration-mirrors`. - `packages/spec`: `build` exit 0 (four times — base and after each of the three `origin/main` merges); `gen:docs`; `check:docs`; `check:generated` 「All 15 generated artifacts are up to date」 (at `c7ea483` too); `typecheck` exit 0 and `vitest run src/api/error-code-ledger.test.ts` 20/20 at `c7ea483` (declared narrowing: the diff in this package is a comment plus a changeset; the whole suite is CI's). - eslint narrowing: `pnpm exec eslint --no-inline-config --format json` over the two lintable changed files (`error-code-ledger.zod.ts`, `dispatch-gates.mjs`): exit 0, 2 judged, 0 errors, 0 warnings. Population: `eslint.config.mjs:971` (`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}`); invariance: `eslint.config.mjs:327–:329` — type-aware linting is never enabled, so no untouched file's verdict can move because of this diff. - `node scripts/pm/check-governed-merges.mjs --pr 19694`: GOVERNED — Tier S (4 of 6 paths hit the register, all under `.claude/**`; no Tier H path), 88 changed lines at that reading, under the human-merge threshold. ## Changeset — measured, not assumed The dispatch's assumption 「the docblock sentence changes no emitted dist byte」 is FALSIFIED on the dist half: the file-level docblock of `error-code-ledger.zod.ts` IS emitted into the declaration bundle. Base build vs post-edit build of `packages/spec/dist` (216 files each): 12 differ — the two `.d.ts`/`.d.mts` chunks that carry the docblock (`automation-api.zod-*`, renamed by content hash), the four entry `.d.ts` files that import them, four sourcemaps (line shift) and the two build-input stamps. Runtime `.js`/`.mjs`/`.cjs`: 0 differ. `needs:contract-review` in dist after: 0 hits (base: 2). `files[]` of `@objectstack/spec` also ships `src/**/*.zod.ts`, so the source comment ships too. So the diff publishes bytes from a released package and takes a `patch` changeset for `@objectstack/spec` (added in patch round 1 on the review's word; `AGENTS.md`'s clause is the stricter one over the comment fast lane). The `skip-changeset` label applied at round 0 is the wrong carrier and the seat removes it at the landing act; this dev did not touch it. ## Acceptance notes — riders off this surface (not filed; prose drift is outside the fileable classes) - Six `.claude/` prose sites still name the retired machinery and are off this step's claimed surface: `.claude/agents/os-dev.md:303` (the label + `--pair PR-NUMBER` reading), `references/dispatch-runbook.md:161` (独立性件), `references/lanes/director.md:27` (duty title names the label) and `:35` (pointer to 载体纪律), `references/lanes/ui.md:38` (独立性件与降档保险丝), `references/state-machine.md:10` (the label's card-side hang). Carrier: step ③ of objectstack-ai#19061 (the label), or a sub-issue of it. - `references/lanes/director.md:33` points at 「`../contract-review.md` 落地前检③」; after the renumbering here that item is ②. Same carrier. - `scripts/pm/dispatch-gates.mjs` docblock line 「Rule text: `references/contract-review.md` 「降档保险丝」」 (near the `CONTRACT_REVIEW_TIER` constant) names a section that no longer exists; outside the two cases this PR was allowed to touch — step ③'s territory. - `AGENTS.md:278` (Prime Directive objectstack-ai#14) still names `check-clause2-carriers.mjs --pair N` and the label — Tier H, the maintainer's own edit at step ③. - The `--template` pointers kept in `contract-review.md` :13 and :19 point at `check-clause2-carriers.mjs --template`; step ③ decides that file's fate. - The derivation did not list `check:pm-dispatch-gates` for a diff touching only `references/contract-review.md`, although that gate reads the file at runtime; it appeared once `dispatch-gates.mjs` itself was in the diff. --- _Generated by [Claude Code](https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc)_ --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #19680
Clause-②: no
The contract-review tier is fable. PR #19573 moved
CONTRACT_REVIEW_TIERto the opus id and filledRETIRED_TIER_WORDSbecause one agent's first request at the tier came back HTTP 429 on 2026-09-21. The maintainer's ruling of 2026-09-22 (issue comment 5771798588) is that this was a temporary lack of authorization on one session, not a retirement, and that it must not have changed the skills — verbatim and untranslated, in order:What lands —
scripts/pm/dispatch-gates.mjsonlyCONTRACT_REVIEW_TIERreads the fable id again, soTIER_CEILINGderives back to the fable word and the ladder readsfloor sonnet · default opus · ceiling fable.RETIRED_TIER_WORDS = Object.freeze([])— no tier word is retired. The guard itself stays.SKILL.md:516points every tier value at this file, so this docblock is that principle's home.Kept, deliberately — these were never the defect.
tierWordOf(), the derivedTIER_CEILING, the retired-spelling guard, and every rendering that reads the constant (the ladder, the exits, the clause-② note, the suspicion line) are PR #19573's structurally sound half: deriving the ceiling from the constant is exactly what stops the pair drifting, in either direction. The exits text is tier-agnostic and readsTIER_DEFAULT, so it follows the constant with no edit.Not touched:
.claude/**(step ② of #19061 owns the reference text),scripts/check-commit-card-trailers.mjs:848(a rejection vocabulary that must keep listing the id), any workflow.The pin that could not survive the revert, and what replaced it
the retired-spelling guard is not vacuous — it names at least one wordcannot hold on an empty list, and an empty list clears every rendering for free. Counting entries on the LIVE list is therefore not available as a control any more. The guard now runs through one function that both legs call, and the non-vacuity case is proved on a mutated copy: feed the guard a word the ladder demonstrably prints and it must red. Three cases now stand where one stood:The mutated-copy control fires on all four renderings, printed on every run — so the guard's green is measured on every run, not only in the one-off ablation below.
Evidence
The two
--tierrenders, verbatim, atc39a60e(node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier PATH, exit 0 both):pnpm check:pm-dispatch-gates—EXIT=0,✓ dispatch-gates self-test: 1906 cases pass., zero reds, run to completion on the final commitc39a60e. Run detached per this file's own header (the battery re-spawns the tool's CLI and took 675.0s on this box, over the container's foreground cap) with the exit code captured to a file, never through a pipe.Derived gate families —
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsat the FINAL head derived 29 commands (byte-identical to the derivation taken at the first commit). All 29 were run, each exit code captured before any pipe, and handed back through--ran:Four of them (
check-ci-filter-parity,check-closing-keyword-parityand its self-test,check-comment-mask-corpus) first exited 3PREREQUISITE NOT METin a worktree with nonode_modules. That is NOT MEASURED, never a pass and never a finding — they were re-run green afterpnpm install, and only the second reading is recorded above.Run by hand, outside the derivation —
node scripts/pm/check-clause2-carriers.mjs --self-test→ exit 0,1140 cases pass. It importsCONTRACT_REVIEW_TIER, so this change moves its verdict, and the path derivation does not name it (the watch-hint blind spot PR #19573 reported).Governed-surface predicate —
node scripts/pm/check-governed-merges.mjs --test scripts/pm/dispatch-gates.mjs→ exit 0,governed-surface predicate: 0 of 1 path(s) hit the register (6 surfaces, repo-agnostic),NOT governed — ordinary queue landing applies to a PR with exactly this file list. Head repo and size are NOT MEASURED by--test; re-read on the PR's own number before landing.Changed lines — 62 (+50 / −12, 1 file), under the 5000 human-merge threshold.
Lint, narrowed and declared.
eslint scripts/pm/dispatch-gates.mjs --no-inline-config→ 0 errors, 0 warnings. The narrowing is measured, not assumed: ① eslint's own population is**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}(eslint.config.mjs:971), so the population is the JS/TS tree; ② the file count is read from--format json(1); ③eslint.config.mjs:327states the config enables no type-aware linting for ANY file (noparserOptions.project, no typed rules), so this diff cannot move the verdict on a file it does not touch. The repo-widepnpm lintsweep is CI's run.Control bytes.
grep -naPfor the control class over the changed file prints nothing (exit 1); the control — the same-Pengine asked for printable ASCII on the same file — fires on 28,427 lines.pnpm check:nul-bytesexit 0.Changeset —
skip-changeset, measured. Every one of the 70 published packages in this workspace declares an explicitfiles[], and the distinct entries across all of them areCHANGELOG.md,README.md,api-surface,dist,json-schema,liveness,llms.txt,prompts,spec-changes.json,src/**/*.zod.ts. Not one reachesscripts/, soscripts/pm/dispatch-gates.mjsships in no tarball and this diff publishes nothing.Reverse verification — the live guard can still fail
Landed through
scripts/ablation-replace.mjs(anchor count checked, blob hash recorded before and after), the direction declared BEFORE the run: put the fable word back into the retired list, expect exactly three reds in the retired-guard block and no others.Three reds out of 1906, exactly the three predicted and no others — the guard, the emptiness reading and the invariant are three different halves of one promise, and each one binds. The restore is proved by blob equality against HEAD and an empty
git diff HEAD, not by an exit code.维护者速读(草稿)
改了什么 — 复核档常量改回 fable;退役词表清空;常量旁的注释块写下这条原则的正文(含 2026-09-22 三句裁决原话与评论号);那条「表里至少有一个词」的自检钉不可能在空表上成立,改成在变异副本上证明守卫仍会红。只动
scripts/pm/dispatch-gates.mjs一个文件。为什么改 — 2026-09-21 把常量从 fable 改成 opus 的依据,是某个 session 的 HTTP 429。那是「这一个 agent 此刻没拿到授权」,不是「这个档位退役了」。把前者写进常量,等于让一次临时授权缺失改写整条车道的治理值。维护者 2026-09-22 明确撤回。
风险与代价(含回滚) — 风险低:改动只在一个未受管文件里,29 个派生门禁族全绿、自检 1906 例全过,并做了消融证明守卫仍会红。代价是复核档重新高于默认档,席位若当时拿不到 fable,须走隔离子 agent 或在队列外等档,⛔ 不得自行降档。回滚就是把这两行改回去 —— 但那正是本卡禁止的动作,除非维护者另行裁决退役。
席位意见 — (留空,待席位定稿)
你要做的 — 确认这条原则的措辞就是你的意思:「档位退役只认你点名退役的裁决;429/额度/单 session 无授权都不算退役」。确认后本 PR 可照常走队列落地。
Acceptance notes
Reported, not fixed here — all outside this card's declared file surface (
scripts/pm/dispatch-gates.mjs):.claude/skills/checklist-test/SKILL.md:103states 「floor sonnet · default opus · ceiling fable」 as a live rule — the ladder prints exactly that again;scripts/pm/check-dispatch-gates.mjs:263calls the mandated surface "the fable-mandatory surface" — accurate again;.claude/skills/pm-dispatch/SKILL.md:538「额度耗尽豁免仅当契约复审档实测不可用才落默认判断档」 had no room left to exempt while the ceiling equalled the default, and has room again now..claude/skills/pm-dispatch/references/contract-review.mdand assert its 降档保险丝 lines (「额度耗尽豁免只及派发 ⛔ 不及复核」 and 「档位退役 ≠ 耗尽,恒维护者裁决 ⛔ 非席位读数」). Deleting that section per the maintainer's 「降档保险丝 不留」 turns both red. That is Retire the in-seat clause-② contract review — the rule, its gate label, and the 9k-line checker's gate role #19061's to carry — its diff must retire those two pins in the same PR. Content-wise the deleted line stays true of this change; it is only the pins that need moving.scripts/pm/check-clause2-carriers.mjsconsumesCONTRACT_REVIEW_TIERbut is still not derived bydispatch-gates --commandsfor a diff that changes it — the same watch-hint blind spot PR pm(tooling): move the contract-review tier off the retired ceiling, and give the fuse the case it never had #19573 reported, unchanged. Run by hand here; green.Generated by Claude Code