Skip to content

pm(tooling): the contract-review tier is fable — revert the opus constant and the retired-word list - #19684

Merged
huangyiirene merged 1 commit into
mainfrom
claude/issue-19680-contract-review-tier-is-fable
Sep 22, 2026
Merged

huangyiirene merged 1 commit into
mainfrom
claude/issue-19680-contract-review-tier-is-fable

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #19680

Clause-②: no

The contract-review tier is fable. PR #19573 moved CONTRACT_REVIEW_TIER to the opus id and filled RETIRED_TIER_WORDS because 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:

「复核档应该就是 fable 啊」 · 「某个agent临时没有fable给的特殊授权,不应该改变skills」 · 「fable 撤回卡 你来创建」

What lands — scripts/pm/dispatch-gates.mjs only

  1. The constant. CONTRACT_REVIEW_TIER reads the fable id again, so TIER_CEILING derives back to the fable word and the ladder reads floor sonnet · default opus · ceiling fable.
  2. The retired list is empty. RETIRED_TIER_WORDS = Object.freeze([]) — no tier word is retired. The guard itself stays.
  3. The docblock beside the constants carries the principle, as one rule with the ruling quoted by date and comment id: a tier word is RETIRED only by the maintainer's explicit ruling that NAMES a retirement; a 429, an exhausted quota or a missing authorization on one session is never a retirement; and a seat this tier is not served to renders the review through an isolated at-tier subagent or waits outside the queue — never by editing the constant. What one session is authorized for is a property of that session; the constant is a property of the lane's governance. SKILL.md:516 points every tier value at this file, so this docblock is that principle's home.
  4. The non-vacuity pin moved off the live list onto a MUTATED copy (see below).

Kept, deliberately — these were never the defect. tierWordOf(), the derived TIER_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 reads TIER_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 word cannot 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:

✓ ⛔ no RETIRED tier word survives in any live RULE — … (dirty: none)
✓ no tier word is RETIRED today — the list is empty and frozen, which is what
  "a temporary lack of authorization is not a retirement" looks like in data (holds: none)
✓ …and the guard is NOT vacuous, proved on a MUTATED copy: a list naming the ladder's own
  ceiling word (fable) REDS, so the green above is the empty list and not a broken search
  (mutated hits: 0/fable 1/fable 2/fable 3/fable)
✓ …and the invariant that copy stands for: no tier word still in the ladder may ever enter the live list

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 --tier renders, verbatim, at c39a60e (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier PATH, exit 0 both):

Model tier — MANDATORY: claude-fable-5-1 (derived from the file surface, not recalled).
Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s), derived here, not recalled.
  The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable).

pnpm check:pm-dispatch-gates — EXIT=0, ✓ dispatch-gates self-test: 1906 cases pass., zero reds, run to completion on the final commit c39a60e. 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 --commands at 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:

✓ dispatch-gates --ran: 29 derived famil(ies) accounted for — 29 run,
  0 NOT-MEASURED (a DERIVED zero — all 29 recorded an exit code and none of them is 3).

Four of them (check-ci-filter-parity, check-closing-keyword-parity and its self-test, check-comment-mask-corpus) first exited 3 PREREQUISITE NOT MET in a worktree with no node_modules. That is NOT MEASURED, never a pass and never a finding — they were re-run green after pnpm 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 imports CONTRACT_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:327 states the config enables no type-aware linting for ANY file (no parserOptions.project, no typed rules), so this diff cannot move the verdict on a file it does not touch. The repo-wide pnpm lint sweep is CI's run.

Control bytes. grep -naP for the control class over the changed file prints nothing (exit 1); the control — the same -P engine asked for printable ASCII on the same file — fires on 28,427 lines. pnpm check:nul-bytes exit 0.

Changeset — skip-changeset, measured. Every one of the 70 published packages in this workspace declares an explicit files[], and the distinct entries across all of them are CHANGELOG.md, README.md, api-surface, dist, json-schema, liveness, llms.txt, prompts, spec-changes.json, src/**/*.zod.ts. Not one reaches scripts/, so scripts/pm/dispatch-gates.mjs ships 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.

ablation-replace: ok mutation landed: anchor 1 -> 0, blob f04cb35cdb94 -> 2d7ada7ab81a
  ✗ ⛔ no RETIRED tier word survives in any live RULE — … (dirty: 0/fable 1/fable 2/fable 3/fable)
  ✗ no tier word is RETIRED today — the list is empty and frozen … (holds: fable)
  ✗ …and the invariant that copy stands for: no tier word still in the ladder may ever enter the live list
✗ dispatch-gates self-test: 3 of 1906 case(s) failed.
ablation-replace: ok restored: blob == HEAD (f04cb35cdb94) and `git diff HEAD` is empty

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):


Generated by Claude Code

…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>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c39a60ef49212a3ecc212043b35c27c8f23a5a50

① Derived judgments

Rendered in-seat by the skills seat 1 session at the served tier the maintainer named on this date (the fable tier word at get_session → external_metadata.last_served_model; ruling record 5771798588). Card #19680 is the maintainer's direct-dispatch reversal of #19544 / PR #19573; the contract is the three sentences on the card body, quoted verbatim there and in the diff.

  • (a) The two values move back, and only the two values — right. git diff 97f4f8c c39a60e is one file, +50 / −12: CONTRACT_REVIEW_TIER set to the fable id (:12282, was the opus id), RETIRED_TIER_WORDS = Object.freeze([]) (:12542, was ['fable']). TIER_CEILING = tierWordOf(CONTRACT_REVIEW_TIER) (:12523) is untouched and derives the ceiling back to the fable word; tierWordOf, the exits docblock (「a tier EXHAUSTED means the default tier, never lower; a RETIRED tier is a maintainer ruling」), the ladder line and every rendering that reads the constant are kept — deriving the ceiling from the constant is what stops the pair drifting in either direction, and PR pm(tooling): move the contract-review tier off the retired ceiling, and give the fuse the case it never had #19573's structurally sound half is exactly that. A grep for the opus id over the file at this head finds 0 occurrences: it was spelled only at the constant, so no rendering carries a literal.
  • (b) The renders — measured by this seat on a clean worktree at this head. --tier .claude/skills/pm-dispatch/SKILL.md prints 「Model tier — MANDATORY:」 followed by the fable id and 「(derived from the file surface, not recalled)」; --tier scripts/pm/dispatch-gates.mjs prints 「no path-derived mandate … (floor sonnet · default opus · ceiling fable)」. Both are what the card's acceptance asks for.
  • (c) The retired-spelling guard on an empty list — right, and non-vacuous. The guard now runs through one function both legs call, retiredWordHits (:25513–:25514), so the live reading and its control run the same search. Four cases replace three (:25515–:25520): the live list clears every rendering (「dirty: none」); 「no tier word is RETIRED today — the list is empty and frozen」; the control on a MUTATED copy — a list naming the ladder's own ceiling word must red, printed on every run as the hits it finds; and the invariant the copy stands for — no word still in the ladder (TIER_FLOOR, TIER_DEFAULT, TIER_CEILING) may enter the live list. The old pin 「names at least one word」 (which could not hold on []) is the only case removed. The dev's ablation — the fable word put back into the list on a copy through scripts/ablation-replace.mjs, anchor 1 → 0, blob f04cb35cdb94 → 2d7ada7ab81a on disk — reds exactly the three predicted cases of 1906 and restores byte-identical to HEAD: the guard is proved from the mutated copy, not from the empty list.
  • (d) The docblock principle — right, and it quotes rather than restates. :12258–:12281 states the third case as one rule with the three sentences of 2026-09-22 verbatim, untranslated, and the ruling record by comment id: a tier word is RETIRED only by the maintainer's explicit ruling that names a retirement; a 429, an exhausted quota or a missing authorization on one session is ⛔ never a retirement; a seat this tier is not served to renders the review through an isolated at-tier subagent or waits outside the queue — ⛔ never by editing this line. 「What one session is authorized for is a property of that session; this constant is a property of the lane's governance」 is the sentence the reversal needed. The RETIRED_TIER_WORDS docblock (:12525–:12541) states EMPTY as the correct steady state, ⛔ not a disabled guard. SKILL.md:516 (「档位值单源在 dispatch-gates 常量」) makes this file the principle's home; ⛔ no .claude/** line is touched, as the card requires (the reference's 降档保险丝 section is Retire the in-seat clause-② contract review — the rule, its gate label, and the 9k-line checker's gate role #19061 step ②'s deletion).
  • (e) Pin disposition across PR pm(tooling): move the contract-review tier off the retired ceiling, and give the fuse the case it never had #19573's six hunks — right. Values inverted (1); the exits docblock kept (2), tier-agnostic; tierWordOf + derived ceiling kept, the list emptied with its docblock rewritten (3); the ladder line reading the derivation kept and printing 「ceiling fable」 again (4); the exits rendering reading TIER_DEFAULT kept (5); every self-test case kept except the one that cannot hold on an empty list (6). No pin in scripts/pm or .claude asserted the opus VALUE or 「the contract-review tier sits at the default-tier constant」 — measured by grep, and this seat's own grep agrees.
  • (f) Two pins this PR leaves for Retire the in-seat clause-② contract review — the rule, its gate label, and the 9k-line checker's gate role #19061 step ② — named, not this diff's. :25548–:25549 read references/contract-review.md and assert its two 降档保险丝 lines; the maintainer's 「降档保险丝 不留」 (5772289798) deletes that section under step ②, which reds those two cases. The seat has instructed the step-② dev to retire exactly those two in its PR as a declared surface increment (region-level concurrency with this PR; the later lander merges once). Nothing in this PR is wrong about them: at this head the section still exists and the pins pass.
  • (g) Nothing narrowed or widened past the ruling. The card is a value reversal plus its rationale; no ladder step, glob or exit code changes. Three of PR pm(tooling): move the contract-review tier off the retired ceiling, and give the fuse the case it never had #19573's acceptance notes are cleared by the revert with no edit (checklist-test/SKILL.md:103's ladder sentence, check-dispatch-gates.mjs:263's 「fable-mandatory surface」, SKILL.md:538's quota exemption having room again) and are recorded in the PR body so nobody files them.
  • (h) Batteries — measured by this seat at this head. check-clause2-carriers.mjs --self-test 1140 cases (it imports the constant and is not derived by --commands for this diff — the same 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; run by hand, green); check:nul-bytes exit 0; eslint over the file 0 / 0; pnpm check:pm-dispatch-gates (1906 cases, ~11 minutes, runs detached) — the dev reports exit 0 with the exit captured to a file; the seat's own run at this head is read before enqueue (③ below).

② Semver level

None — no release. scripts/pm/** is in no package's files[] (the dev enumerated the distinct files[] entries across all 70 published packages: none reaches scripts/); skip-changeset is the correct carrier.

③ Boundary flags

  • The derivation ran from the final head, on the tool being edited — 29 families, byte-identical to the first commit's derivation, all exit 0, reconciled through --ran with 0 NOT-MEASURED. Right: a derivation tool's own change is measured with the changed tool.
  • The long self-test run detached (its own header instructs this; it re-spawns its CLI and exceeds the foreground cap) with the exit written to a file — accepted; the seat's own detached run at this head is the landing reading.
  • Four gates first read exit 3 in a worktree without node_modules — recorded as NOT MEASURED and re-run green after install; only the second reading is reported. Right.
  • A Chinese 维护者速读 in the body of a non-governed PR — a judgment call the dev recorded; harmless, and the maintainer will want to read this one.
  • Net +38 against the route's +40 — inside.
  • CI at this head: Lint & Repo Gates, Test Core (1/6) and Type Check · workspace were in_progress at the seat's read; the rest success / skipped. The seat enqueues only on all green and on check-expected-skips --pr 19684 exit 0.
  • The constants on origin/main still name the opus id until this lands — this record is rendered at the served tier the maintainer named, and its Served-tier: line carries the constant NAME; the value the name resolves to is what this PR restores.

Implemented-by: claude/issue-19680-contract-review-tier-is-fable
Reviewed-by: session_01Wnstp2kTth7sGXfr8fXypc

VERDICT: PASS


Generated by Claude Code

@huangyiirene
huangyiirene marked this pull request as ready for review September 22, 2026 08:04
@huangyiirene
huangyiirene added this pull request to the merge queue Sep 22, 2026
Merged via the queue into main with commit 372931e Sep 22, 2026
37 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-19680-contract-review-tier-is-fable branch September 22, 2026 08:23
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
… 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants