You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* The globs that MANDATE a model tier for any card whose file surface touches
7939
7959
* them, as DATA. This is the one list in this file besides CHANGE_KIND_GATES,
@@ -7967,7 +7987,7 @@ export function residueLines(
7967
7987
* about paths:
7968
7988
*
7969
7989
* - clause ①, encoded below: a card editing the PM lane's PROTOCOL-SEMANTIC
7970
-
* surfaces is `claude-fable-5` — the pm-dispatch SKILL.md main file, every
7990
+
* surfaces is `CONTRACT_REVIEW_TIER` — the pm-dispatch SKILL.md main file, every
7971
7991
* file carrying an enforced copy of the decision frame (the COPIES table
7972
7992
* of check:skill-frame-sync), and the dev-agent definition. Narrowed from
7973
7993
* "the whole skill tree, references included" by the maintainer's
@@ -7977,7 +7997,7 @@ export function residueLines(
7977
7997
* file-surface predicate, and exactly what this script takes as argv;
7978
7998
* - clause ②, NOT encoded and deliberately not: a card that changes contract
7979
7999
* accept/reject behaviour or widens the public surface is also
7980
-
* `claude-fable-5`. That is judged from the card's CONTENT — what the change
8000
+
* `CONTRACT_REVIEW_TIER`. That is judged from the card's CONTENT — what the change
7981
8001
* does to the contract — and a path cannot answer it. An ordinary-looking
7982
8002
* surface (one package's source file) is the NORMAL shape of a clause-②
7983
8003
* card. The closest a path can honestly get is SUSPICION:
@@ -8061,35 +8081,21 @@ export function residueLines(
8061
8081
export const MANDATORY_TIER_GLOBS = [
8062
8082
{
8063
8083
glob: '.claude/skills/pm-dispatch/SKILL.md',
8064
-
tier: 'claude-fable-5',
8084
+
tier: CONTRACT_REVIEW_TIER,
8065
8085
why: 'clause ① of the model-tiering ruling (narrowed to protocol semantics, 2026-08-20): the PM dispatch skill MAIN file is the lane\'s own operating protocol and a wrong edit propagates to every later dispatch — references/** dropped out of the path mandate that day',
8066
8086
},
8067
8087
{
8068
8088
glob: '.claude/agents/os-dev.md',
8069
-
tier: 'claude-fable-5',
8089
+
tier: CONTRACT_REVIEW_TIER,
8070
8090
why: 'clause ① (2026-08-20 narrowing): the dev-agent definition is protocol semantics — every dispatched dev runs under it, and it carries an enforced copy of the decision frame',
8071
8091
},
8072
8092
{
8073
8093
glob: 'skills/objectstack-pm-dispatch/SKILL.md',
8074
-
tier: 'claude-fable-5',
8094
+
tier: CONTRACT_REVIEW_TIER,
8075
8095
why: 'clause ① (2026-08-20 narrowing): the published PM skill carries two enforced copies of the decision frame (check:skill-frame-sync COPIES) and ships verbatim to third-party projects',
8076
8096
},
8077
8097
];
8078
8098
8079
-
/**
8080
-
* Clause ②'s single source of truth for the CONTRACT-REVIEW tier: the tier a
8081
-
* card that changes contract accept/reject behaviour or widens the public
8082
-
* surface must be dispatched at, and the tier the `needs:contract-review`
8083
-
* re-review sub-round must itself be running at (its opening self-check reads
8084
-
* this). Declared HERE and only here, as a constant, so a model upgrade is a
8085
-
* one-line change in one file. The review label deliberately names WHAT is
8086
-
* reviewed, never a model (maintainer, 2026-08-16: 「needs:fable-review 这个标
8087
-
* 签不好,下次模型升级怎么办」), and the PM skill's prose points at this
8088
-
* constant instead of spelling a model name.
8089
-
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
* The globs that make a surface a clause-② SUSPECT — a HINT, never a verdict.
8095
8101
*
@@ -15298,12 +15304,12 @@ function selfTest() {
15298
15304
// mandate, and the ordinary surface that must NOT be mandated (a tool that
15299
15305
// mandates everything is ignored, which loses the guardrail by the other road).
15300
15306
const fableOf = (paths) => deriveTier(paths);
15301
-
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
15302
-
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === 'claude-fable-5');
15303
-
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
15307
+
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
15308
+
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === CONTRACT_REVIEW_TIER);
15309
+
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
15304
15310
t('a pm-dispatch REFERENCES path carries NO path mandate — the 2026-08-20 narrowing, inverted from the pre-narrowing pin', fableOf(['.claude/skills/pm-dispatch/references/review-checklist.md']).mandatory === false);
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === 'claude-fable-5');
15312
+
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === CONTRACT_REVIEW_TIER);
15307
15313
t('the mixed verdict reports the offending path, not just the verdict', mixed.hits.length === 1 && mixed.hits[0].path.endsWith('.claude/agents/os-dev.md'));
15308
15314
t('an ordinary surface carries no path-derived mandate', fableOf(['packages/spec/src/data/filter.zod.ts']).mandatory === false);
15309
15315
t("this tool's own file is not mandatory — the card that added this section reads itself correctly", fableOf(['scripts/pm/dispatch-gates.mjs']).mandatory === false);
@@ -15316,7 +15322,7 @@ function selfTest() {
15316
15322
// The rendering is where the invariant is actually delivered: the claim
15317
15323
// comment quotes THESE lines.
15318
15324
const mandLines = tierLines(mixed).join('\n');
15319
-
t('the mandatory rendering names the tier', mandLines.includes('claude-fable-5'));
15325
+
t('the mandatory rendering names the tier', mandLines.includes(CONTRACT_REVIEW_TIER));
15320
15326
t('the mandatory rendering says MANDATORY in a word a reader cannot skim past', mandLines.includes('MANDATORY'));
15321
15327
t('the mandatory rendering shows its provenance — the path and the glob that covered it', mandLines.includes("- .claude/agents/os-dev.md ⇢ '.claude/agents/os-dev.md'"));
15322
15328
t('the mandatory rendering names every sanctioned exit, so a downgrade needs a stated reason', mandLines.includes('quota exemption') && mandLines.includes('opus, never lower') && mandLines.includes('one-line-class') && mandLines.includes('proactive low-headroom'));
/* frameFiles stays empty and the cases below fail loudly */
15376
15382
}
15377
15383
t('the frame-sync COPIES table is readable and non-empty, so the pin below is not vacuous', frameProbe.status === 0 && Array.isArray(frameFiles) && frameFiles.length > 0);
t('the SKILL.md main file and the dev-agent definition are declared in their own right, not only via the frame table', MANDATORY_TIER_GLOBS.some((g) => g.glob === '.claude/skills/pm-dispatch/SKILL.md') && MANDATORY_TIER_GLOBS.some((g) => g.glob === '.claude/agents/os-dev.md'));
15380
15386
15381
15387
// ── Clause-② suspicion (the enqueue-gate card): hit / no hit / wording ────
0 commit comments