Skip to content

Commit c39a60e

Browse files
committed
pm(tooling): the contract-review tier is fable — revert the opus constant 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>
1 parent 97f4f8c commit c39a60e

1 file changed

Lines changed: 50 additions & 12 deletions

File tree

‎scripts/pm/dispatch-gates.mjs‎

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12243,7 +12243,8 @@ export function residueLines(
1224312243
* convenience. The review label deliberately names WHAT is reviewed, never a
1224412244
* model (maintainer, 2026-08-16: 「needs:fable-review 这个标签不好,下次模型升级怎么办」).
1224512245
*
12246-
* ## A tier that is GONE is not a tier that is EXHAUSTED (#19544)
12246+
* ## A tier that is GONE is not a tier that is EXHAUSTED — and a SESSION not
12247+
* served one is neither of them (#19544, reversed by #19680)
1224712248
*
1224812249
* The exits below carry a QUOTA exemption: a tier that is exhausted comes
1224912250
* back, so the card waits out of the queue and the review is never downgraded
@@ -12254,15 +12255,31 @@ export function residueLines(
1225412255
* retirement is a maintainer ruling and ⛔ never a seat's reading — and when
1225512256
* the ruling lands, this VALUE is the one line that moves. The ceiling of the
1225612257
* ladder `tierLines` prints is DERIVED from it ({@link TIER_CEILING}) so the
12257-
* two cannot drift apart. This value moved off the retired ceiling on the
12258-
* maintainer's 2026-09-21 ruling — verbatim: 「fable 没有了」, answered with
12259-
* 「改成 opus」 — which is why the docblocks below still name the retired tier
12260-
* where they record what was ruled AT THE TIME; a record of what was served
12261-
* then stays true. Rule text: `references/contract-review.md` 「降档保险丝」.
12258+
* two cannot drift apart.
12259+
*
12260+
* A THIRD case is what this line was once actually moved on, and it is neither
12261+
* of the two above. On 2026-09-21 two review dispatches died on their first
12262+
* request with an HTTP 429 quota refusal at this tier; that reading — ONE
12263+
* agent, temporarily, not authorized — was written in here as a retirement,
12264+
* and the skills moved with it. It is not a retirement. Maintainer,
12265+
* 2026-09-22, verbatim and untranslated (ruling record: issue comment
12266+
* 5771798588): 「复核档应该就是 fable 啊」 ·
12267+
* 「某个agent临时没有fable给的特殊授权,不应该改变skills」 ·
12268+
* 「fable 撤回卡 你来创建」.
12269+
*
12270+
* As one rule, and this docblock is its home — the PM skill points every tier
12271+
* value at this file, so there is nowhere else it could live: a tier word is
12272+
* RETIRED only by the maintainer's explicit ruling that NAMES a retirement; a
12273+
* 429, an exhausted quota or a missing authorization on one session is ⛔
12274+
* never a retirement; and a seat this tier is not served to renders the review
12275+
* through an isolated at-tier subagent or waits outside the queue — ⛔ never
12276+
* by editing this line. What one session is authorized for is a property of
12277+
* that session; this constant is a property of the lane's governance, and the
12278+
* two ⛔ never trade places.
1226212279
*
1226312280
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
1226412281
*/
12265-
export const CONTRACT_REVIEW_TIER = 'claude-opus-5';
12282+
export const CONTRACT_REVIEW_TIER = 'claude-fable-5-1';
1226612283

1226712284
/**
1226812285
* The globs that MANDATE a model tier for any card whose file surface touches
@@ -12506,15 +12523,23 @@ export function tierWordOf(modelId) {
1250612523
export const TIER_CEILING = tierWordOf(CONTRACT_REVIEW_TIER);
1250712524

1250812525
/**
12509-
* Tier family words this ladder once printed and must never print again.
12526+
* Tier family words a maintainer ruling has RETIRED — none today.
1251012527
*
1251112528
* ⛔ Not a tier table and ⛔ not an ordering — a RETIRED-SPELLING guard, the
1251212529
* same shape as the retired clause-② keys pinned further down. The self-test
1251312530
* asserts no rendering contains one, so the day a ceiling is written down by
1251412531
* hand again it reds instead of quietly outliving the harness that served it.
12515-
* A word leaves this list only when a maintainer ruling brings the tier back.
12532+
*
12533+
* EMPTY is this list's correct steady state, ⛔ not a disabled guard. A word
12534+
* enters it only on the maintainer's explicit ruling that names a retirement
12535+
* and leaves it only on a ruling that brings the tier back — the conditions
12536+
* {@link CONTRACT_REVIEW_TIER}'s docblock states; the one entry this list held
12537+
* was written on a session's quota refusal, which is none of those. Because an
12538+
* empty list clears every rendering for free, the self-test proves the guard
12539+
* on a MUTATED copy — a list naming a word the ladder really prints has to red
12540+
* — so the green above it is measured rather than vacuous.
1251612541
*/
12517-
export const RETIRED_TIER_WORDS = Object.freeze(['fable']);
12542+
export const RETIRED_TIER_WORDS = Object.freeze([]);
1251812543

1251912544
/**
1252012545
* Place a card's file surface against the mandatory globs. Pure over its
@@ -25476,9 +25501,22 @@ function selfTest() {
2547625501
t('the ladder prints a ceiling DERIVED from the contract-review constant, so the two cannot drift apart', ladderLine.includes(`ceiling ${tierWordOf(CONTRACT_REVIEW_TIER)})`), ladderLine || 'no ladder line was rendered at all');
2547725502
t("…in the ladder's own vocabulary — the constant's FAMILY word, so a parseable id NEVER reaches the ladder verbatim", /^claude-[a-z]+-/.test(CONTRACT_REVIEW_TIER) && TIER_CEILING === tierWordOf(CONTRACT_REVIEW_TIER) && !ladderLine.includes(CONTRACT_REVIEW_TIER), ladderLine);
2547825503
t('tierWordOf reads the family out of an id, and hands an unreadable one back VERBATIM rather than guessing a word', tierWordOf('claude-example-9-9') === 'example' && tierWordOf('an-unfamiliar-shape') === 'an-unfamiliar-shape' && tierWordOf(null) === '');
25479-
t(`⛔ no RETIRED tier word survives in any live RULE — ladder, exits, clause-② note and suspicion line all read the constant (dirty: ${ladderRenderings.map((l, i) => RETIRED_TIER_WORDS.filter((w) => l.toLowerCase().includes(w)).map((w) => `${i}/${w}`).join(' ')).filter(Boolean).join(' ') || 'none'})`, ladderRenderings.every((l) => RETIRED_TIER_WORDS.every((w) => !l.toLowerCase().includes(w))));
25504+
// The guard is driven through ONE function so the live reading and its
25505+
// control run the same search. RETIRED_TIER_WORDS is EMPTY — no ruling has
25506+
// retired a tier word — so the live case below clears every rendering for
25507+
// free, and a case asserting only that green would pass just as happily on a
25508+
// search that had stopped matching anything. The non-vacuity case therefore
25509+
// moved off the LIVE list, where it could only ever count entries, and onto
25510+
// a MUTATED copy: feed the guard a word the ladder demonstrably prints and
25511+
// it must red. That is the invariant an empty list has to keep provable — a
25512+
// word still in the ladder can never be in this list.
25513+
const retiredWordHits = (words) =>
25514+
ladderRenderings.flatMap((l, i) => words.filter((w) => l.toLowerCase().includes(w)).map((w) => `${i}/${w}`));
25515+
t(`⛔ no RETIRED tier word survives in any live RULE — ladder, exits, clause-② note and suspicion line all read the constant (dirty: ${retiredWordHits(RETIRED_TIER_WORDS).join(' ') || 'none'})`, retiredWordHits(RETIRED_TIER_WORDS).length === 0);
2548025516
t('…and the guard is not reading an empty string — every rendering it clears still carries its own rule text', ladderRenderings.every((l) => l.includes('Model tier')) && ladderRenderings.some((l) => l.includes('Exits,')));
25481-
t('the retired-spelling guard is not vacuous — it names at least one word, and none of them is a tier still in the ladder', RETIRED_TIER_WORDS.length > 0 && !RETIRED_TIER_WORDS.includes(TIER_FLOOR) && !RETIRED_TIER_WORDS.includes(TIER_DEFAULT) && !RETIRED_TIER_WORDS.includes(TIER_CEILING));
25517+
t(`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: ${[...RETIRED_TIER_WORDS].join(', ') || 'none'})`, Array.isArray(RETIRED_TIER_WORDS) && RETIRED_TIER_WORDS.length === 0 && Object.isFrozen(RETIRED_TIER_WORDS));
25518+
t(`…and the guard is NOT vacuous, proved on a MUTATED copy: a list naming the ladder's own ceiling word (${TIER_CEILING}) REDS, so the green above is the empty list and not a broken search (mutated hits: ${retiredWordHits([TIER_CEILING]).join(' ') || 'NONE — the control never fired'})`, retiredWordHits([TIER_CEILING]).length > 0 && retiredWordHits(['a-word-no-rendering-prints']).length === 0);
25519+
t('…and the invariant that copy stands for: no tier word still in the ladder may ever enter the live list', [TIER_FLOOR, TIER_DEFAULT, TIER_CEILING].every((w) => !RETIRED_TIER_WORDS.includes(w)));
2548225520
// A sibling case further down pins the constant's CURRENT value to exactly
2548325521
// one site under these roots. What that case cannot see is a RETIRED id left
2548425522
// behind — it is not the current value, so nothing compares it to anything,

0 commit comments

Comments
 (0)