Repository navigation
pm(tooling): the contract-review tier is fable — revert #19573's opus constant and retired-word list; a temporary lack of authorization never changes the skills #19680
Description
Activity
- addedpriority:p2Medium: important, M3Medium: important, M3and removed
on Sep 22, 2026 huangyiirene commented
on Sep 22, 2026 CollaboratorAuthorMore actionsClaim: PM loop round R1 (skills seat 1)
Session:session_01Wnstp2kTth7sGXfr8fXypc
Branch:claude/issue-19680-contract-review-tier-is-fable
Worktree:objectstack-issue-19680
Domain:domain:skills
Seat:domain:skills#1
File surface:scripts/pm/dispatch-gates.mjsonly — the two constants (CONTRACT_REVIEW_TIER,RETIRED_TIER_WORDS), the retired-spelling guard's pins (around :25479–:25481), the self-test pins PR #19573 added, and the docblock beside the constants; ⛔ no.claude/**, no other script, no workflow (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: TIER_DEFAULT—node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier scripts/pm/dispatch-gates.mjsat97f4f8c828(2026-09-22T06:44Z) printed verbatim: "Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s), derived here, not recalled." Not governed; the seat lands it through the queue on green with an in-seat## Contract reviewrecord rendered at this session's served tier (fable — the maintainer's word 5771798588).
Clause-②: no
Thread-read: none
Ruling-ref: 5771798588
Serial constraints cleared: none — all 21 open PRs' file lists read at 2026-09-22T06:44Z; no open PR touchesscripts/pm/dispatch-gates.mjs. Same-file sibling in this lane: the two residual Acceptance notes from #19172 (theWalked …denominator, composite-action steps) stay off this card's surface.Dispatch basis: the maintainer's direct instruction, three sentences verbatim on 2026-09-22 in this seat's chat — 「复核档应该就是 fable 啊」 · 「某个agent临时没有fable给的特殊授权,不应该改变skills」 · 「fable 撤回卡 你来创建」 — recorded 5771798588 and quoted on the card body; a 维护者直派插队卡 takes the head of the take order (SKILL.md:476) and the filing-door deviation is audited on the body. Premise re-read on
origin/main97f4f8c828:scripts/pm/dispatch-gates.mjs:12265readsCONTRACT_REVIEW_TIER = 'claude-opus-5',:12517readsRETIRED_TIER_WORDS = Object.freeze(['fable']),:12506derivesTIER_CEILINGfrom the constant, and--tier .claude/skills/pm-dispatch/SKILL.mdprints the opus id as MANDATORY — the state PR #19573 left. Premise live.
Generated by Claude Code
huangyiirene commented
on Sep 22, 2026 CollaboratorAuthorMore actionsos-dev-report
{
"issue": 19680,
"status": "done",
"branch": "claude/issue-19680-contract-review-tier-is-fable",
"pr": "#19684",
"session": "session_01Wnstp2kTth7sGXfr8fXypc",
"premise_still_valid": true,
"premise_evidence": "Re-measured on the worktree at origin/main 97f4f8c BEFORE editing: dispatch-gates.mjs:12265 read CONTRACT_REVIEW_TIER = 'claude-opus-5', :12517 read RETIRED_TIER_WORDS = Object.freeze(['fable']), :12506 derived TIER_CEILING from the constant, and --tier on the SKILL.md surface printed the opus id as MANDATORY with the ladder reading 'ceiling opus'. The state PR #19573 left was live; the card's premise held.",
"summary": "Reverted PR #19573's two value changes in scripts/pm/dispatch-gates.mjs and nothing else. 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'. RETIRED_TIER_WORDS is Object.freeze([]) -- no tier word is retired. PR #19573's structurally sound half is KEPT deliberately: tierWordOf(), the derived TIER_CEILING, the retired-spelling guard itself, and every rendering that reads the constant (ladder, exits, clause-2 note, suspicion line) -- deriving the ceiling from the constant is exactly what stops the pair drifting, in either direction. The docblock beside the constants now carries the maintainer's principle as one rule, quoting the three sentences of 2026-09-22 verbatim with their date and the ruling record (issue comment 5771798588): a tier word is RETIRED only by a maintainer ruling that NAMES a retirement; a 429, an exhausted quota or a missing authorization on one session is never a retirement; a seat not served the tier renders the review through an isolated at-tier subagent or waits outside the queue, never by editing the constant. The assignee was already set by the PM dispatch and was not written by me; claim comment 5772291677 is the newest and only Claim: on the thread and names this branch.",
"pin_disposition": "PR #19573 touched this file in 6 hunks; each judged. (1) constant docblock plus value: value INVERTED, docblock REWRITTEN to carry the reversal and the principle. (2) exits docblock reworded to 'the mandated tier EXHAUSTED means the default tier, never lower; a RETIRED tier is a maintainer ruling instead': KEPT, tier-agnostic and correct. (3) tierWordOf() plus TIER_CEILING plus RETIRED_TIER_WORDS: function and derivation KEPT, list emptied, its docblock rewritten to state EMPTY as the correct steady state. (4) ladder line 'ceiling fable' replaced by the derivation: KEPT, and it now renders 'ceiling fable' again on its own. (5) exits rendering reading TIER_DEFAULT: KEPT. (6) the self-test battery: every case KEPT except the one that cannot hold on an empty list. No pin anywhere in the tree asserts the opus VALUE or 'the contract-review tier sits at the default-tier constant' -- a grep over scripts/pm and .claude found the opus id spelled only at the constant itself, so only the retired-list non-vacuity pin needed inverting.",
"h2_repin": "The pin 'the retired-spelling guard is not vacuous -- it names at least one word' cannot hold on an empty list. The guard now runs through ONE function both legs call (retiredWordHits), and four cases stand where three stood: the live reading (RETIRED_TIER_WORDS 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 (fable) REDS, printed on every run as 'mutated hits: 0/fable 1/fable 2/fable 3/fable', with a negative control ('a-word-no-rendering-prints' finds nothing) so the search is proved to discriminate; and the invariant that copy stands for -- no tier word still in the ladder may ever enter the live list. The live reading RETIRED_TIER_WORDS.length === 0 is true trivially and is reported as such: the guard's green is carried by the mutated copy, not by the empty list.",
"tier_render_mandatory": "Model tier — MANDATORY: claude-fable-5-1 (derived from the file surface, not recalled).",
"tier_render_no_mandate": [
"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)."
],
"tests": "All measurements at the FINAL head c39a60e (tree clean, blob f04cb35cdb94). (1) pnpm check:pm-dispatch-gates EXIT=0, 'dispatch-gates self-test: 1906 cases pass.', 0 reds, battery 675.0s -- run detached per this file's own header (it re-spawns its CLI and exceeds the container foreground cap) with the exit code written to a file, never read through a pipe. (2) The --commands derivation at the final head produced 29 commands, byte-identical to the derivation at the first commit; all 29 run, each exit captured before any pipe, reconciled 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).' (3) Four gates first exited 3 PREREQUISITE NOT MET in a worktree with no node_modules -- recorded as NOT MEASURED, re-run green after pnpm install; only the second reading is reported. (4) check-clause2-carriers.mjs --self-test exit 0, 1140 cases pass, run by hand because it imports CONTRACT_REVIEW_TIER while the path derivation does not name it. (5) eslint on the one changed file with --no-inline-config: exit 0, 0 errors 0 warnings; the narrowing is measured three ways -- population read from eslint.config.mjs:971, file count read from --format json (1), and eslint.config.mjs:327 stating no type-aware linting is enabled for ANY file, so this diff cannot move the verdict on a file it does not touch; the repo-wide pnpm lint sweep is CI's run. (6) Control bytes: grep -naP over the control class prints nothing (exit 1); the positive control, the same -P engine asked for printable ASCII on the same file, fires on 28427 lines; pnpm check:nul-bytes exit 0. ABLATION (reverse verification, direction declared BEFORE the run): through scripts/ablation-replace.mjs, anchor 1 to 0 and blob f04cb35cdb94 to 2d7ada7ab81a on disk, putting 'fable' back into the retired list produced 'dispatch-gates self-test: 3 of 1906 case(s) failed' -- exactly the three predicted cases in the retired-guard block and no others; the restore is proved by 'blob == HEAD (f04cb35cdb94) and git diff HEAD is empty', not by an exit code. This file has no dist or build stage (node reads the source), so the on-disk proof is the anchor count plus the blob hashes.",
"gates": [
{
"command": "node scripts/check-ci-filter-parity.mjs",
"exit": 0,
"verdict": "OK: all 185 declared cross-package glob(s) (132 unique) are covered bycoreorcrosspkg, everycrosspkgentry still covers one, and thetestjob'sif:still nam"
},
{
"command": "node scripts/check-closing-keyword-parity.mjs",
"exit": 0,
"verdict": "check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 9196 tracked file(s), "
},
{
"command": "node scripts/check-closing-keyword-parity.mjs --self-test",
"exit": 0,
"verdict": "✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to red."
},
{
"command": "node scripts/check-comment-mask-corpus.mjs",
"exit": 0,
"verdict": "✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 6998 files, 0 disagree, 0 unparseable, 57.3s (comparator self-test: 26 cases pass)."
},
{
"command": "node scripts/check-declaration-mirrors.mjs",
"exit": 0,
"verdict": "OK: 10 hand-written declaration(s) agree with their modules on name, kind and required arity."
},
{
"command": "node scripts/check-declaration-mirrors.mjs --self-test",
"exit": 0,
"verdict": "All 29 self-test cases passed."
},
{
"command": "node scripts/check-scripts-symbol-anchors.mjs",
"exit": 0,
"verdict": "✅ check-scripts-symbol-anchors: 3641 anchors across 274 scripts resolve — 52 symbol (52 declaration, 0 literal), 3589 file-level, 0 cross-repo, 1 exempt, 2 continuation. "
},
{
"command": "node scripts/check-scripts-symbol-anchors.mjs --self-test",
"exit": 0,
"verdict": "✅ check-scripts-symbol-anchors --self-test: every finding class provoked, comment-prose projection wired, declined shapes counted not missed, allowance rows exact both wa"
},
{
"command": "node scripts/check-self-test-wired.mjs",
"exit": 0,
"verdict": "✓ check-self-test-wired: every one of the 223 script(s) CI runs that ship a--self-testhas that self-test run by CI."
},
{
"command": "node scripts/check-self-test-wired.mjs --self-test",
"exit": 0,
"verdict": "check-self-test-wired --self-test: 3 live ledger row(s) verified, plus the comment mask, the right boundary, alias resolution and both audit directions — 11 declared batt"
},
{
"command": "node scripts/check-self-test-workflow-commands.mjs",
"exit": 0,
"verdict": "✓ check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner would parse as a workflow command."
},
{
"command": "node scripts/check-self-test-workflow-commands.mjs --self-test",
"exit": 0,
"verdict": "check-self-test-workflow-commands --self-test: both measured parse rules pinned (legacy form anywhere in a line, current form only at line start), the innocent-output and"
},
{
"command": "node scripts/check-whole-set-label-write.mjs",
"exit": 0,
"verdict": "✓ check-whole-set-label-write: 0 violations — 339 file(s) over 3 root(s) · 12 raw mention(s) · 12 in comments/prose (cleared) · 0 in EXECUTABLE content (judged) · 193us" }, { "command": "node scripts/check-whole-set-label-write.mjs --self-test", "exit": 0, "verdict": "✓ check-whole-set-label-write --self-test: all cases pass (24 fixture trees + 5 refusals + 1 allowlist hatch)" }, { "command": "pnpm check:agent-test-spelling", "exit": 0, "verdict": "✓ check-agent-test-spelling: 0 violations — 531 file(s) · 9467 bare--token(s) · 1797 launcher-rooted run(s) · 13 separator(s) JUDGED · 6 vitest-backed script name(s) " }, { "command": "pnpm check:bash32-floor", "exit": 0, "verdict": "✓ check-bash32-floor: 31 tracked shell file(s) under scripts/**, .claude/hooks/**, .githooks/** name no bash 4+ construct outside a comment, a guarded ${VAR:-} read, or a" }, { "command": "pnpm check:cli-command-ids", "exit": 0, "verdict": "✓ check-cli-command-ids: 63 module(s) under packages/cli/src/commands examined, all of them default-export a class whose inheritance chain reaches oclif'sCommand(0 de" }, { "command": "pnpm check:cross-package-test-inputs", "exit": 0, "verdict": "OK: 29 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them on a split \"test:repo\" task); 13 walked root(s) judged, 3 on" }, { "command": "pnpm check:declared-population-live", "exit": 0, "verdict": "✓ check:declared-population-live — 258 of 319 famil(ies) declare a path population, and every one of them reaches this tree's 9196 tracked file(s)." }, { "command": "pnpm check:driver-memory-census", "exit": 0, "verdict": "check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states \"#6664 census: 2 ruled consumers\". This gate polic" }, { "command": "pnpm check:entry-guard", "exit": 0, "verdict": "✓ check:entry-guard: 274 scripts/ file(s) — every entry guard goes through invoked-as.mjs; 213 export bindings, 213 of them inert on import (0 known-unsafe, ⛔ SHRINK-ONLY" }, { "command": "pnpm check:gitlink-declared", "exit": 0, "verdict": "check-gitlink-declared: OK (9196 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so nothing is declared; nothing to declare)." }, { "command": "pnpm check:nul-bytes", "exit": 0, "verdict": "check-nul-bytes: OK (scanned 9189 text file(s) -- 9189 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes)." }, { "command": "pnpm check:parse-guard", "exit": 0, "verdict": "✓ check:parse-guard: 273 scripts/ file(s) — every TypeScript parse goes through ts-parse.mjs." }, { "command": "pnpm check:pm-dispatch-gates", "exit": 0, "verdict": "EXIT=0 captured to a file, never through a pipe; \"dispatch-gates self-test: 1906 cases pass.\", 0 reds, battery 675.0s" }, { "command": "pnpm check:pnpm-filter-targets", "exit": 0, "verdict": "✓ check:pnpm-filter-targets: 152/207--filter` occurrence(s) across 41 file(s) resolve against 81 workspace package(s); 55 not judged (2 foreign, 30 interpolated, 23 pat"
},
{
"command": "pnpm check:ratchet-remedy-authority",
"exit": 0,
"verdict": "OK check-ratchet-remedy-authority: 268 scripts swept (scripts/.{mjs,mts} + scripts/pm/.{mjs,mts}); 15 mark the expanding remedy ⛔ MAINTAINER-ONLY, 5 turn it down outri"
},
{
"command": "pnpm check:refd-timer-probe",
"exit": 0,
"verdict": "OK check-refd-timer-probe: 6993 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else."
},
{
"command": "pnpm check:watch-hint-literal",
"exit": 0,
"verdict": "✓ check-watch-hint-literal: 71 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 47, ROOT_FILE_WATCH_HINTS 13, ROOT_WATCH_HINTS 3, DECLARED_WATCH_HINTS 8 -"
}
],
"governed_merges_reading": "node scripts/pm/check-governed-merges.mjs --pr 19684, exit 0: 'governed-surface predicate: 0 of 1 path(s) hit the register (6 surfaces, repo-agnostic).' and 'NOT governed -- ordinary queue landing applies to a PR with exactly this file list.' Size: 62 changed line(s) (+50 / -12), at most the 5000 human-merge threshold. Head repo read from the PR object by --pr.",
"clause2_pair_reading": "node scripts/pm/check-clause2-carriers.mjs --pair 19684, exit 4 (C6): card #19680 declares 'Clause-2: no' and carries domain:skills, a lane that owes the contract review on EVERY round, and NO '## Contract review' record exists on head c39a60e (0 PR comments and 1 card comment read). This is the expected state at a dev hand-off: the record is the SEAT's act, and until it exists the PR stays draft and out of the queue. Reported as a reading only -- I wrote no review label and posted no review record.",
"line_budget": "62 changed lines, +50 / -12, 1 file -- net +38 against the route's stated ceiling of +40. Read from the tool's own change-set line at the final head and confirmed by GitHub on the PR object (+50 / -12).",
"files_changed": [
"scripts/pm/dispatch-gates.mjs"
],
"changeset": "skip-changeset, measured not assumed: all 70 published packages in this workspace declare an explicit files[], and the distinct entries across every one of them are CHANGELOG.md, README.md, api-surface, dist, json-schema, liveness, llms.txt, prompts, spec-changes.json and src//*.zod.ts. None reaches scripts/, so this file ships in no tarball. Label applied through scripts/pm/label-write.mjs (additive POST plus read-back): 'read-back -- 1 label(s): skip-changeset -- MATCHES the target'.",
"mcp_calls": "0 -- no MCP GitHub tool was called, read or write. Every GitHub read and write went through the REST proxy with curl, or through scripts/pm/label-write.mjs, which is itself REST.",
"api_writes": "3 REST writes: POST /repos/objectstack-ai/objectstack/pulls (draft PR #19684); POST /repos//issues/19684/labels (skip-changeset, via scripts/pm/label-write.mjs); POST /repos//issues/19680/comments (this report). Plus 2 git pushes on the one branch line item: the empty-branch write-routing probe before any edit, then the single commit c39a60e. No PATCH of any body, no second comment, no assignee write, no POST /issues.",
"deviations": "(1) The worktree was created without node_modules and the first pass of four derived gates exited 3 PREREQUISITE NOT MET; pnpm install was run and all four re-measured green -- the first reading is recorded as NOT MEASURED, never as a pass. (2) pnpm check:pm-dispatch-gates and the ablation battery were run DETACHED rather than in the foreground, on this file's own header instruction (it re-spawns its CLI and took 675.0s, over the container's roughly 10-minute foreground cap); each was waited out synchronously inside this turn by blocking on process and tree state, and the exit code of the definitive run was captured to a file before any pipe. (3) The lint sweep was narrowed to the one changed file, with all three pieces of evidence the discipline requires. Nothing else deviates: no .claude/ file, no workflow and no other script was touched. One judgment call recorded rather than taken silently: the PR body carries a Chinese 'maintainer quick-read (draft)' section although check-governed-merges reads this diff as NOT governed -- included because this file is the lane's rule layer and the change reverses a governance value the maintainer will want to read.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: #19061 step 2 (dispatched in parallel, owns .claude/skills/pm-dispatch/references/contract-review.md) -- two self-test cases in scripts/pm/dispatch-gates.mjs read that file and assert its two downgrade-fuse lines. Deleting that section per the maintainer's ruling turns both red; #19061's diff must retire those two pins in the same PR. Content-wise the deleted line stays true of this change; only the pins need moving. Named in the PR's Acceptance notes. noted, not filed.",
"carrier: this PR -- three of PR #19573's own acceptance notes are cleared by the revert with no edit and are recorded in the PR body so nobody files them again: .claude/skills/checklist-test/SKILL.md:103 states the ladder as a live rule and the ladder prints exactly that again; scripts/pm/check-dispatch-gates.mjs:263 calls the surface 'the fable-mandatory surface', accurate again; and .claude/skills/pm-dispatch/SKILL.md:538's quota exemption had no room left while the ceiling equalled the default and has room again. noted, not filed.",
"carrier: none -- scripts/pm/check-clause2-carriers.mjs imports CONTRACT_REVIEW_TIER but is still not derived by 'dispatch-gates --commands' for a diff that changes it, the same watch-hint blind spot PR #19573 reported. Not one of the three fileable classes (not a reproducible defect today, not a declared-contract violation, not a metadata-authoring trap): a tooling blind spot with a named workaround, run by hand here and green. Acceptance notes only. Dedupe words if the seat disagrees: check-clause2-carriers, watch-hint, CONTRACT_REVIEW_TIER, dispatch-gates --commands, derivation blind spot."
]
}
Generated by Claude Code
huangyiirene commented
on Sep 22, 2026 CollaboratorAuthorMore actionsACCEPT —
domain:skillsseat 1 (session_01Wnstp2kTth7sGXfr8fXypc, seat post #7623), 2026-09-22T08:04Z — PR #19684 at headc39a60ef49212a3ecc212043b35c27c8f23a5a50(round 0; the maintainer's direct-dispatch reversal, filed by this seat on their instruction).Checklist, read against GitHub and the fetched head, not the report: draft, base
main, body first lineFixes #19680and no other closing keyword,Clause-②: noat a line start; one filescripts/pm/dispatch-gates.mjs(+50 / −12, one commitc39a60eonorigin/main97f4f8c):CONTRACT_REVIEW_TIERback to the fable id (:12282),RETIRED_TIER_WORDSemptied (:12542) with its guard re-pinned on a mutated copy (:25513–:25520),TIER_CEILINGstill derived from the constant (:12523), the docblock beside the constants carrying the maintainer's three sentences of this date verbatim with the ruling record 5771798588 and the principle as one rule; the opus id spelled nowhere in the file at this head (grep 0); PR #19573's six hunks each dispositioned (values inverted, the structurally sound half kept).skip-changesetcorrect (scripts/pm/**in no package'sfiles[]— the dev enumerated every published package's entries);check-governed-merges.mjs --pr 19684exit 0 — NOT governed, 62 lines. Measured by this seat on its own worktree atc39a60e:--tier .claude/skills/pm-dispatch/SKILL.mdprints the fable id as MANDATORY, derived not recalled;--tier scripts/pm/dispatch-gates.mjsprints 「no path-derived mandate … (floor sonnet · default opus · ceiling fable)」;pnpm check:pm-dispatch-gatesrun detached, exit 0 captured to a file, 「dispatch-gates self-test: 1906 cases pass」;check-clause2-carriers.mjs --self-test1140 (imports the constant; not derived for this diff — the blind spot PR #19573 reported, run by hand);check:nul-bytes0; eslint over the file 0 / 0;check-expected-skips --pr 19684OK. Contract review of record: PASS 5773019944, rendered in-seat by this session at the served tier the maintainer named (「复核档应该就是 fable 啊」, 5771798588);check-clause2-carriers.mjs --pair 19684exit 0 with C6-RECORD naming it. Report 5772848037 parses;mcp_calls0;api_writes3 + 2 pushes as listed; ⛔ no body PATCH by the dev; the seat wrote none either — the body reads correctly for this head.Deviations recorded, none blocking: the long self-test and the ablation ran detached on the file's own header instruction (the exit captured to a file, never through a pipe) — the seat's own detached run agrees; four derived gates first read exit 3 PREREQUISITE NOT MET in a worktree without
node_modulesand were re-measured green after install (the first reading recorded as NOT MEASURED); the lint sweep narrowed to the one file with the three readings; a Chinese 维护者速读 in the body of a non-governed PR — a judgment call, harmless. Handed to #19061 step ② (in flight): the two self-test cases at :25548–:25549 that assert the reference's 降档保险丝 lines red when that section is deleted under the maintainer's 「不留」 — retired there as a declared surface increment, region-level concurrency with this PR. Cleared by the revert with no edit and recorded in the body:checklist-test/SKILL.md:103,check-dispatch-gates.mjs:263,SKILL.md:538.Landing: not governed — ready + auto-merge through the CCR route in this act on all-green checks. At MERGED:
Fixescloseout —pm:dispatchedand the assignee off #19680, the card closedcompleted; from that head the ladder the seat dispatches by reads 「ceiling fable」 again and the fuse's remedy for a session not served at the tier is the isolated at-tier subagent, ⛔ never a constant change.
Generated by Claude Code
- added 2 commits that reference this issue
on Sep 28, 2026
立卡门类别 ③ — a maintainer direct-dispatch task, filed by the
domain:skillsseat 1 (session_01Wnstp2kTth7sGXfr8fXypc, seat post #7623) on the maintainer's explicit instruction. ⛔ Filed and claimed in one act by the same seat (维护者直派插队卡, SKILL.md:476).Source, three parts (谁的指令 / 原话 / 在哪说): the maintainer · verbatim, three sentences on 2026-09-22 in this seat's chat, in order: 「复核档应该就是 fable 啊」 · 「某个agent临时没有fable给的特殊授权,不应该改变skills」 · 「fable 撤回卡 你来创建」 · recorded on seat post #7623 (comment 5771798588; the third sentence is the filing instruction).
Filing-door note, so the audit reads it here:
references/filing-gate.md:33 has a User-type write identity file no new card. This card is filed under the maintainer's explicit instruction quoted above, through the 维护者直派通道 (SKILL.md:233–:234 — 「当面指挥的 PM 会话直接路由,只对明示指挥的卡成立;直派的审计评论逐字引用授权指令」). This paragraph is that audit comment.Dedupe: the seat's token cannot reach
/search(HTTP 403 through the proxy); the 31 opendomain:skillscards and the lane's 16-row queue were read on this date — none touches the tier ladder. The closed twin is #19544 (its PR #19573 is what this card reverses). No open twin.The defect
On 2026-09-21 two review dispatches died on their first request with HTTP 429 「You've reached your Fable limit」; the
domain:specseat asked, the maintainer answered 「fable 没有了」, and #19544 / PR #19573 (77df0f6) movedCONTRACT_REVIEW_TIERfromclaude-fable-5-1toclaude-opus-5, addedRETIRED_TIER_WORDS = ['fable']with a retired-spelling guard, and rewordedreferences/contract-review.md:60. The maintainer's word today: that was one agent's temporary lack of authorization, ⛔ not a retirement of the model, and it must not have changed the skills. The contract-review tier is fable.What lands —
scripts/pm/dispatch-gates.mjsonly (not governed; the seat lands it through the queue)CONTRACT_REVIEW_TIER = 'claude-fable-5-1';TIER_CEILINGderives to the fable word again — ladder floor sonnet · default opus · ceiling fable, as it read before PR pm(tooling): move the contract-review tier off the retired ceiling, and give the fuse the case it never had #19573.RETIRED_TIER_WORDS = Object.freeze([])— no tier word is retired. The retired-spelling guard stays; its non-vacuity pin (around :25479–:25481, 「names at least one word」) is re-pinned to hold on an EMPTY list: the control becomes 「a word that IS in the ladder can never appear in the list」, proved on a mutated copy rather than on the live list.model: fable) or waits outside the queue — ⛔ never by changing a constant. (references/contract-review.md's 降档保险丝 section is deleted by Retire the in-seat clause-② contract review — the rule, its gate label, and the 9k-line checker's gate role #19061 step ② per the maintainer's 「不留」 on this date, and SKILL.md:516 makes this file the single source of tier values — so this docblock is the principle's home.).claude/**(step ② of Retire the in-seat clause-② contract review — the rule, its gate label, and the 9k-line checker's gate role #19061 owns the reference text);scripts/check-commit-card-trailers.mjs:848(already lists the fable id); any workflow.Acceptance
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier .claude/skills/pm-dispatch/SKILL.mdprints the fable id as MANDATORY, derived not recalled;--tier scripts/pm/**still prints 「no path-derived mandate」 with the ladder 「floor sonnet · default opus · ceiling fable」.RETIRED_TIER_WORDS.length === 0and the guard's control is proved non-vacuous on a mutated copy;pnpm check:pm-dispatch-gatesexit 0; the--commandsderivation and--ranreconciliation at the final head;check-governed-merges.mjs --pr NNOT governed;skip-changeset(scripts/pm/**publishes nothing).## Contract reviewrecord for the PR is rendered in-seat at fable (this seat's served tier),--pair0, every check green ⇒ ready + auto-merge through the CCR route.Dedupe words:
CONTRACT_REVIEW_TIER·RETIRED_TIER_WORDS· fable · 复核档 · #19573 · 降档保险丝 · 临缺授权 ≠ 退役Generated by Claude Code