Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/skills/pm-dispatch/SKILL.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -357,8 +357,8 @@ issue 任何人不得认领** —— 那意味着分诊还没走到,不是「可
跑到清空排空稳态,阈值吸收突发(实测 40 分钟到 13 张 ≈ 稳态四倍,审计式倾倒形态)——
没有阈值,倾倒只能按到达顺序摊进常规轮,失掉同族同上下文一次读完的批次红利,
队尾同期继续增长;⛔ 二者不可互相替代。
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起
`claude-fable-5` 分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起 `CONTRACT_REVIEW_TIER` 档
分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
⛔ 不写码不认领;产出落卡,与定时轮分诊同格式同效力(细则见 `references/dispatch-runbook.md`)。
**跨仓 pin 链的窗口级兜底也在本 sweep**(联动单第一产者仍是接受座位;
⛔ 只立单不执行 bump): objectui pin 落后且其队列已空 ⇒ 在本仓立/刷新 console bump 单;
Expand DownExpand Up@@ -587,16 +587,16 @@ closes-list:部分交付 PR 特意写 Refs 而非 Fixes 时它恰为空,正是
**派发模型是 PM 的逐卡显式决定** —— 下限 `sonnet`(机械卡:
正确性由门禁农场机械判定,失败在漏跑门不在判断);默认判断档 `opus`(M/L、
裁决实施、任何带设计判断的卡;拿不准就升一档 —— 错派低档的返工贵过省下的额度);
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:
① 凡改**协议语义面**的卡一律 `model: "claude-fable-5"` —— 面 = 本 `SKILL.md` 主文件、
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:① 凡改
**协议语义面**的卡一律 `CONTRACT_REVIEW_TIER` 档(`--tier` 输出)—— 面 = 本 `SKILL.md` 主文件、
决策框架拷贝所在文件(以 `check:skill-frame-sync` 强制处为准)与 `.claude/agents/os-dev.md`(2026-08-20
收窄,维护者原话:「接受你的建议」—— fable 当审计师用,不当施工队用);
仅 `references/**` 面、或任一 pm-dispatch 治理面上一行级机械文本改动的卡降为 **opus 施工**
(纯机械一行 PM 酌定可至 sonnet 地板),**补偿控制即既有技能面复核档要求**(ACCEPT 路径分叉
①:复核席跑契约复审档位)—— opus 施工的治理面改动只经 fable 档复核到达维护者,
⛔ 不新增标签不新增链;② 凡**改变契约接受/拒绝行为或扩大公开面**的卡(`domain:spec`
语义面;判据即分诊代裁的机械边界测试与 spec 席内分派判据 —— `references/lanes/spec.md`,
⛔ 不另抄第二份)一律 `claude-fable-5`(维护者 2026-08-12 裁定,
⛔ 不另抄第二份)一律 `CONTRACT_REVIEW_TIER` 档(维护者 2026-08-12 裁定,
原话:「同意,就按语义面收窄,立卡并通知 spec 席」)—— 契约错毒化一切下游,
全仓最贵(条款②闸门见「入队与落地」)。**负边界(维护者 2026-08-28 裁定)**:
运行时权限/安全**行为**变更不是条款② —— 归人工地板「安全/权限边界」
Expand Down
58 changes: 32 additions & 26 deletions scripts/pm/dispatch-gates.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -7934,6 +7934,26 @@ export function residueLines(
// Model-tier derivation — the half of the tier decision that IS a path question
// ---------------------------------------------------------------------------

/**
* The single source of truth for the model tier the PM lane's governance
* reads — clause ②'s CONTRACT-REVIEW tier: the tier a card that changes
* contract accept/reject behaviour or widens the public surface must be
* dispatched at, and the tier the `needs:contract-review` re-review sub-round
* must itself be running at (its opening self-check reads this). Declared HERE
* and only here, as a constant, so a model upgrade is a one-line change in one
* file — the clause-① mandate rows below read it, the self-test compares
* against it, and the PM skill's prose names it, so the model id is spelled as
* a VALUE on this one line and nowhere else across `scripts/pm/**` and
* `.claude/skills/pm-dispatch/**` (a second value site is what let this
* constant drift from the served tier unnoticed). The comparison against the
* served tier is EXACT, never a family or prefix floor — widening a governance
* gate's accept set is the maintainer's decision, not a refresh-time
* convenience. The review label deliberately names WHAT is reviewed, never a
* model (maintainer, 2026-08-16: 「needs:fable-review 这个标签不好,下次模型升级怎么办」).
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5-1';

/**
* The globs that MANDATE a model tier for any card whose file surface touches
* them, as DATA. This is the one list in this file besides CHANGE_KIND_GATES,
Expand DownExpand Up@@ -7967,7 +7987,7 @@ export function residueLines(
* about paths:
*
* - clause ①, encoded below: a card editing the PM lane's PROTOCOL-SEMANTIC
* surfaces is `claude-fable-5` — the pm-dispatch SKILL.md main file, every
* surfaces is `CONTRACT_REVIEW_TIER` — the pm-dispatch SKILL.md main file, every
* file carrying an enforced copy of the decision frame (the COPIES table
* of check:skill-frame-sync), and the dev-agent definition. Narrowed from
* "the whole skill tree, references included" by the maintainer's
Expand All@@ -7977,7 +7997,7 @@ export function residueLines(
* file-surface predicate, and exactly what this script takes as argv;
* - clause ②, NOT encoded and deliberately not: a card that changes contract
* accept/reject behaviour or widens the public surface is also
* `claude-fable-5`. That is judged from the card's CONTENT — what the change
* `CONTRACT_REVIEW_TIER`. That is judged from the card's CONTENT — what the change
* does to the contract — and a path cannot answer it. An ordinary-looking
* surface (one package's source file) is the NORMAL shape of a clause-②
* card. The closest a path can honestly get is SUSPICION:
Expand DownExpand Up@@ -8061,35 +8081,21 @@ export function residueLines(
export const MANDATORY_TIER_GLOBS = [
{
glob: '.claude/skills/pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: '.claude/agents/os-dev.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: 'skills/objectstack-pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
];

/**
* Clause ②'s single source of truth for the CONTRACT-REVIEW tier: the tier a
* card that changes contract accept/reject behaviour or widens the public
* surface must be dispatched at, and the tier the `needs:contract-review`
* re-review sub-round must itself be running at (its opening self-check reads
* this). Declared HERE and only here, as a constant, so a model upgrade is a
* one-line change in one file. The review label deliberately names WHAT is
* reviewed, never a model (maintainer, 2026-08-16: 「needs:fable-review 这个标
* 签不好,下次模型升级怎么办」), and the PM skill's prose points at this
* constant instead of spelling a model name.
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5';

/**
* The globs that make a surface a clause-② SUSPECT — a HINT, never a verdict.
*
Expand DownExpand Up@@ -15298,12 +15304,12 @@ function selfTest() {
// mandate, and the ordinary surface that must NOT be mandated (a tool that
// mandates everything is ignored, which loses the guardrail by the other road).
const fableOf = (paths) => deriveTier(paths);
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === 'claude-fable-5');
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === CONTRACT_REVIEW_TIER);
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
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);
const mixed = fableOf(['packages/spec/src/data/filter.zod.ts', '.claude/agents/os-dev.md']);
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === 'claude-fable-5');
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === CONTRACT_REVIEW_TIER);
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'));
t('an ordinary surface carries no path-derived mandate', fableOf(['packages/spec/src/data/filter.zod.ts']).mandatory === false);
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);
Expand All@@ -15316,7 +15322,7 @@ function selfTest() {
// The rendering is where the invariant is actually delivered: the claim
// comment quotes THESE lines.
const mandLines = tierLines(mixed).join('\n');
t('the mandatory rendering names the tier', mandLines.includes('claude-fable-5'));
t('the mandatory rendering names the tier', mandLines.includes(CONTRACT_REVIEW_TIER));
t('the mandatory rendering says MANDATORY in a word a reader cannot skim past', mandLines.includes('MANDATORY'));
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'"));
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'));
Expand All@@ -15337,7 +15343,7 @@ function selfTest() {
let ambiguityRefused = false;
try {
deriveTier(['.claude/skills/pm-dispatch/SKILL.md'], [
{ glob: '.claude/skills/pm-dispatch/**', tier: 'claude-fable-5', why: 'a' },
{ glob: '.claude/skills/pm-dispatch/**', tier: CONTRACT_REVIEW_TIER, why: 'a' },
{ glob: '.claude/skills/**', tier: 'opus', why: 'b' },
]);
} catch {
Expand DownExpand Up@@ -15375,7 +15381,7 @@ function selfTest() {
/* frameFiles stays empty and the cases below fail loudly */
}
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(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === 'claude-fable-5'));
t(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === CONTRACT_REVIEW_TIER));
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'));

// ── Clause-② suspicion (the enqueue-gate card): hit / no hit / wording ────
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/skills/pm-dispatch/SKILL.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -357,8 +357,8 @@ issue 任何人不得认领** —— 那意味着分诊还没走到,不是「可
跑到清空排空稳态,阈值吸收突发(实测 40 分钟到 13 张 ≈ 稳态四倍,审计式倾倒形态)——
没有阈值,倾倒只能按到达顺序摊进常规轮,失掉同族同上下文一次读完的批次红利,
队尾同期继续增长;⛔ 二者不可互相替代。
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起
`claude-fable-5` 分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起 `CONTRACT_REVIEW_TIER` 档
分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
⛔ 不写码不认领;产出落卡,与定时轮分诊同格式同效力(细则见 `references/dispatch-runbook.md`)。
**跨仓 pin 链的窗口级兜底也在本 sweep**(联动单第一产者仍是接受座位;
⛔ 只立单不执行 bump): objectui pin 落后且其队列已空 ⇒ 在本仓立/刷新 console bump 单;
Expand DownExpand Up@@ -587,16 +587,16 @@ closes-list:部分交付 PR 特意写 Refs 而非 Fixes 时它恰为空,正是
**派发模型是 PM 的逐卡显式决定** —— 下限 `sonnet`(机械卡:
正确性由门禁农场机械判定,失败在漏跑门不在判断);默认判断档 `opus`(M/L、
裁决实施、任何带设计判断的卡;拿不准就升一档 —— 错派低档的返工贵过省下的额度);
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:
① 凡改**协议语义面**的卡一律 `model: "claude-fable-5"` —— 面 = 本 `SKILL.md` 主文件、
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:① 凡改
**协议语义面**的卡一律 `CONTRACT_REVIEW_TIER` 档(`--tier` 输出)—— 面 = 本 `SKILL.md` 主文件、
决策框架拷贝所在文件(以 `check:skill-frame-sync` 强制处为准)与 `.claude/agents/os-dev.md`(2026-08-20
收窄,维护者原话:「接受你的建议」—— fable 当审计师用,不当施工队用);
仅 `references/**` 面、或任一 pm-dispatch 治理面上一行级机械文本改动的卡降为 **opus 施工**
(纯机械一行 PM 酌定可至 sonnet 地板),**补偿控制即既有技能面复核档要求**(ACCEPT 路径分叉
①:复核席跑契约复审档位)—— opus 施工的治理面改动只经 fable 档复核到达维护者,
⛔ 不新增标签不新增链;② 凡**改变契约接受/拒绝行为或扩大公开面**的卡(`domain:spec`
语义面;判据即分诊代裁的机械边界测试与 spec 席内分派判据 —— `references/lanes/spec.md`,
⛔ 不另抄第二份)一律 `claude-fable-5`(维护者 2026-08-12 裁定,
⛔ 不另抄第二份)一律 `CONTRACT_REVIEW_TIER` 档(维护者 2026-08-12 裁定,
原话:「同意,就按语义面收窄,立卡并通知 spec 席」)—— 契约错毒化一切下游,
全仓最贵(条款②闸门见「入队与落地」)。**负边界(维护者 2026-08-28 裁定)**:
运行时权限/安全**行为**变更不是条款② —— 归人工地板「安全/权限边界」
Expand Down
58 changes: 32 additions & 26 deletions scripts/pm/dispatch-gates.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -7934,6 +7934,26 @@ export function residueLines(
// Model-tier derivation — the half of the tier decision that IS a path question
// ---------------------------------------------------------------------------

/**
* The single source of truth for the model tier the PM lane's governance
* reads — clause ②'s CONTRACT-REVIEW tier: the tier a card that changes
* contract accept/reject behaviour or widens the public surface must be
* dispatched at, and the tier the `needs:contract-review` re-review sub-round
* must itself be running at (its opening self-check reads this). Declared HERE
* and only here, as a constant, so a model upgrade is a one-line change in one
* file — the clause-① mandate rows below read it, the self-test compares
* against it, and the PM skill's prose names it, so the model id is spelled as
* a VALUE on this one line and nowhere else across `scripts/pm/**` and
* `.claude/skills/pm-dispatch/**` (a second value site is what let this
* constant drift from the served tier unnoticed). The comparison against the
* served tier is EXACT, never a family or prefix floor — widening a governance
* gate's accept set is the maintainer's decision, not a refresh-time
* convenience. The review label deliberately names WHAT is reviewed, never a
* model (maintainer, 2026-08-16: 「needs:fable-review 这个标签不好,下次模型升级怎么办」).
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5-1';

/**
* The globs that MANDATE a model tier for any card whose file surface touches
* them, as DATA. This is the one list in this file besides CHANGE_KIND_GATES,
Expand DownExpand Up@@ -7967,7 +7987,7 @@ export function residueLines(
* about paths:
*
* - clause ①, encoded below: a card editing the PM lane's PROTOCOL-SEMANTIC
* surfaces is `claude-fable-5` — the pm-dispatch SKILL.md main file, every
* surfaces is `CONTRACT_REVIEW_TIER` — the pm-dispatch SKILL.md main file, every
* file carrying an enforced copy of the decision frame (the COPIES table
* of check:skill-frame-sync), and the dev-agent definition. Narrowed from
* "the whole skill tree, references included" by the maintainer's
Expand All@@ -7977,7 +7997,7 @@ export function residueLines(
* file-surface predicate, and exactly what this script takes as argv;
* - clause ②, NOT encoded and deliberately not: a card that changes contract
* accept/reject behaviour or widens the public surface is also
* `claude-fable-5`. That is judged from the card's CONTENT — what the change
* `CONTRACT_REVIEW_TIER`. That is judged from the card's CONTENT — what the change
* does to the contract — and a path cannot answer it. An ordinary-looking
* surface (one package's source file) is the NORMAL shape of a clause-②
* card. The closest a path can honestly get is SUSPICION:
Expand DownExpand Up@@ -8061,35 +8081,21 @@ export function residueLines(
export const MANDATORY_TIER_GLOBS = [
{
glob: '.claude/skills/pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: '.claude/agents/os-dev.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: 'skills/objectstack-pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
];

/**
* Clause ②'s single source of truth for the CONTRACT-REVIEW tier: the tier a
* card that changes contract accept/reject behaviour or widens the public
* surface must be dispatched at, and the tier the `needs:contract-review`
* re-review sub-round must itself be running at (its opening self-check reads
* this). Declared HERE and only here, as a constant, so a model upgrade is a
* one-line change in one file. The review label deliberately names WHAT is
* reviewed, never a model (maintainer, 2026-08-16: 「needs:fable-review 这个标
* 签不好,下次模型升级怎么办」), and the PM skill's prose points at this
* constant instead of spelling a model name.
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5';

/**
* The globs that make a surface a clause-② SUSPECT — a HINT, never a verdict.
*
Expand DownExpand Up@@ -15298,12 +15304,12 @@ function selfTest() {
// mandate, and the ordinary surface that must NOT be mandated (a tool that
// mandates everything is ignored, which loses the guardrail by the other road).
const fableOf = (paths) => deriveTier(paths);
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === 'claude-fable-5');
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === CONTRACT_REVIEW_TIER);
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
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);
const mixed = fableOf(['packages/spec/src/data/filter.zod.ts', '.claude/agents/os-dev.md']);
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === 'claude-fable-5');
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === CONTRACT_REVIEW_TIER);
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'));
t('an ordinary surface carries no path-derived mandate', fableOf(['packages/spec/src/data/filter.zod.ts']).mandatory === false);
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);
Expand All@@ -15316,7 +15322,7 @@ function selfTest() {
// The rendering is where the invariant is actually delivered: the claim
// comment quotes THESE lines.
const mandLines = tierLines(mixed).join('\n');
t('the mandatory rendering names the tier', mandLines.includes('claude-fable-5'));
t('the mandatory rendering names the tier', mandLines.includes(CONTRACT_REVIEW_TIER));
t('the mandatory rendering says MANDATORY in a word a reader cannot skim past', mandLines.includes('MANDATORY'));
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'"));
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'));
Expand All@@ -15337,7 +15343,7 @@ function selfTest() {
let ambiguityRefused = false;
try {
deriveTier(['.claude/skills/pm-dispatch/SKILL.md'], [
{ glob: '.claude/skills/pm-dispatch/**', tier: 'claude-fable-5', why: 'a' },
{ glob: '.claude/skills/pm-dispatch/**', tier: CONTRACT_REVIEW_TIER, why: 'a' },
{ glob: '.claude/skills/**', tier: 'opus', why: 'b' },
]);
} catch {
Expand DownExpand Up@@ -15375,7 +15381,7 @@ function selfTest() {
/* frameFiles stays empty and the cases below fail loudly */
}
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(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === 'claude-fable-5'));
t(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === CONTRACT_REVIEW_TIER));
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'));

// ── Clause-② suspicion (the enqueue-gate card): hit / no hit / wording ────
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/skills/pm-dispatch/SKILL.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -357,8 +357,8 @@ issue 任何人不得认领** —— 那意味着分诊还没走到,不是「可
跑到清空排空稳态,阈值吸收突发(实测 40 分钟到 13 张 ≈ 稳态四倍,审计式倾倒形态)——
没有阈值,倾倒只能按到达顺序摊进常规轮,失掉同族同上下文一次读完的批次红利,
队尾同期继续增长;⛔ 二者不可互相替代。
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起
`claude-fable-5` 分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起 `CONTRACT_REVIEW_TIER` 档
分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
⛔ 不写码不认领;产出落卡,与定时轮分诊同格式同效力(细则见 `references/dispatch-runbook.md`)。
**跨仓 pin 链的窗口级兜底也在本 sweep**(联动单第一产者仍是接受座位;
⛔ 只立单不执行 bump): objectui pin 落后且其队列已空 ⇒ 在本仓立/刷新 console bump 单;
Expand DownExpand Up@@ -587,16 +587,16 @@ closes-list:部分交付 PR 特意写 Refs 而非 Fixes 时它恰为空,正是
**派发模型是 PM 的逐卡显式决定** —— 下限 `sonnet`(机械卡:
正确性由门禁农场机械判定,失败在漏跑门不在判断);默认判断档 `opus`(M/L、
裁决实施、任何带设计判断的卡;拿不准就升一档 —— 错派低档的返工贵过省下的额度);
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:
① 凡改**协议语义面**的卡一律 `model: "claude-fable-5"` —— 面 = 本 `SKILL.md` 主文件、
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:① 凡改
**协议语义面**的卡一律 `CONTRACT_REVIEW_TIER` 档(`--tier` 输出)—— 面 = 本 `SKILL.md` 主文件、
决策框架拷贝所在文件(以 `check:skill-frame-sync` 强制处为准)与 `.claude/agents/os-dev.md`(2026-08-20
收窄,维护者原话:「接受你的建议」—— fable 当审计师用,不当施工队用);
仅 `references/**` 面、或任一 pm-dispatch 治理面上一行级机械文本改动的卡降为 **opus 施工**
(纯机械一行 PM 酌定可至 sonnet 地板),**补偿控制即既有技能面复核档要求**(ACCEPT 路径分叉
①:复核席跑契约复审档位)—— opus 施工的治理面改动只经 fable 档复核到达维护者,
⛔ 不新增标签不新增链;② 凡**改变契约接受/拒绝行为或扩大公开面**的卡(`domain:spec`
语义面;判据即分诊代裁的机械边界测试与 spec 席内分派判据 —— `references/lanes/spec.md`,
⛔ 不另抄第二份)一律 `claude-fable-5`(维护者 2026-08-12 裁定,
⛔ 不另抄第二份)一律 `CONTRACT_REVIEW_TIER` 档(维护者 2026-08-12 裁定,
原话:「同意,就按语义面收窄,立卡并通知 spec 席」)—— 契约错毒化一切下游,
全仓最贵(条款②闸门见「入队与落地」)。**负边界(维护者 2026-08-28 裁定)**:
运行时权限/安全**行为**变更不是条款② —— 归人工地板「安全/权限边界」
Expand Down
58 changes: 32 additions & 26 deletions scripts/pm/dispatch-gates.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -7934,6 +7934,26 @@ export function residueLines(
// Model-tier derivation — the half of the tier decision that IS a path question
// ---------------------------------------------------------------------------

/**
* The single source of truth for the model tier the PM lane's governance
* reads — clause ②'s CONTRACT-REVIEW tier: the tier a card that changes
* contract accept/reject behaviour or widens the public surface must be
* dispatched at, and the tier the `needs:contract-review` re-review sub-round
* must itself be running at (its opening self-check reads this). Declared HERE
* and only here, as a constant, so a model upgrade is a one-line change in one
* file — the clause-① mandate rows below read it, the self-test compares
* against it, and the PM skill's prose names it, so the model id is spelled as
* a VALUE on this one line and nowhere else across `scripts/pm/**` and
* `.claude/skills/pm-dispatch/**` (a second value site is what let this
* constant drift from the served tier unnoticed). The comparison against the
* served tier is EXACT, never a family or prefix floor — widening a governance
* gate's accept set is the maintainer's decision, not a refresh-time
* convenience. The review label deliberately names WHAT is reviewed, never a
* model (maintainer, 2026-08-16: 「needs:fable-review 这个标签不好,下次模型升级怎么办」).
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5-1';

/**
* The globs that MANDATE a model tier for any card whose file surface touches
* them, as DATA. This is the one list in this file besides CHANGE_KIND_GATES,
Expand DownExpand Up@@ -7967,7 +7987,7 @@ export function residueLines(
* about paths:
*
* - clause ①, encoded below: a card editing the PM lane's PROTOCOL-SEMANTIC
* surfaces is `claude-fable-5` — the pm-dispatch SKILL.md main file, every
* surfaces is `CONTRACT_REVIEW_TIER` — the pm-dispatch SKILL.md main file, every
* file carrying an enforced copy of the decision frame (the COPIES table
* of check:skill-frame-sync), and the dev-agent definition. Narrowed from
* "the whole skill tree, references included" by the maintainer's
Expand All@@ -7977,7 +7997,7 @@ export function residueLines(
* file-surface predicate, and exactly what this script takes as argv;
* - clause ②, NOT encoded and deliberately not: a card that changes contract
* accept/reject behaviour or widens the public surface is also
* `claude-fable-5`. That is judged from the card's CONTENT — what the change
* `CONTRACT_REVIEW_TIER`. That is judged from the card's CONTENT — what the change
* does to the contract — and a path cannot answer it. An ordinary-looking
* surface (one package's source file) is the NORMAL shape of a clause-②
* card. The closest a path can honestly get is SUSPICION:
Expand DownExpand Up@@ -8061,35 +8081,21 @@ export function residueLines(
export const MANDATORY_TIER_GLOBS = [
{
glob: '.claude/skills/pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: '.claude/agents/os-dev.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: 'skills/objectstack-pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
];

/**
* Clause ②'s single source of truth for the CONTRACT-REVIEW tier: the tier a
* card that changes contract accept/reject behaviour or widens the public
* surface must be dispatched at, and the tier the `needs:contract-review`
* re-review sub-round must itself be running at (its opening self-check reads
* this). Declared HERE and only here, as a constant, so a model upgrade is a
* one-line change in one file. The review label deliberately names WHAT is
* reviewed, never a model (maintainer, 2026-08-16: 「needs:fable-review 这个标
* 签不好,下次模型升级怎么办」), and the PM skill's prose points at this
* constant instead of spelling a model name.
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5';

/**
* The globs that make a surface a clause-② SUSPECT — a HINT, never a verdict.
*
Expand DownExpand Up@@ -15298,12 +15304,12 @@ function selfTest() {
// mandate, and the ordinary surface that must NOT be mandated (a tool that
// mandates everything is ignored, which loses the guardrail by the other road).
const fableOf = (paths) => deriveTier(paths);
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === 'claude-fable-5');
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === CONTRACT_REVIEW_TIER);
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
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);
const mixed = fableOf(['packages/spec/src/data/filter.zod.ts', '.claude/agents/os-dev.md']);
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === 'claude-fable-5');
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === CONTRACT_REVIEW_TIER);
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'));
t('an ordinary surface carries no path-derived mandate', fableOf(['packages/spec/src/data/filter.zod.ts']).mandatory === false);
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);
Expand All@@ -15316,7 +15322,7 @@ function selfTest() {
// The rendering is where the invariant is actually delivered: the claim
// comment quotes THESE lines.
const mandLines = tierLines(mixed).join('\n');
t('the mandatory rendering names the tier', mandLines.includes('claude-fable-5'));
t('the mandatory rendering names the tier', mandLines.includes(CONTRACT_REVIEW_TIER));
t('the mandatory rendering says MANDATORY in a word a reader cannot skim past', mandLines.includes('MANDATORY'));
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'"));
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'));
Expand All@@ -15337,7 +15343,7 @@ function selfTest() {
let ambiguityRefused = false;
try {
deriveTier(['.claude/skills/pm-dispatch/SKILL.md'], [
{ glob: '.claude/skills/pm-dispatch/**', tier: 'claude-fable-5', why: 'a' },
{ glob: '.claude/skills/pm-dispatch/**', tier: CONTRACT_REVIEW_TIER, why: 'a' },
{ glob: '.claude/skills/**', tier: 'opus', why: 'b' },
]);
} catch {
Expand DownExpand Up@@ -15375,7 +15381,7 @@ function selfTest() {
/* frameFiles stays empty and the cases below fail loudly */
}
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(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === 'claude-fable-5'));
t(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === CONTRACT_REVIEW_TIER));
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'));

// ── Clause-② suspicion (the enqueue-gate card): hit / no hit / wording ────
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/skills/pm-dispatch/SKILL.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -357,8 +357,8 @@ issue 任何人不得认领** —— 那意味着分诊还没走到,不是「可
跑到清空排空稳态,阈值吸收突发(实测 40 分钟到 13 张 ≈ 稳态四倍,审计式倾倒形态)——
没有阈值,倾倒只能按到达顺序摊进常规轮,失掉同族同上下文一次读完的批次红利,
队尾同期继续增长;⛔ 二者不可互相替代。
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起
`claude-fable-5` 分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起 `CONTRACT_REVIEW_TIER` 档
分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
⛔ 不写码不认领;产出落卡,与定时轮分诊同格式同效力(细则见 `references/dispatch-runbook.md`)。
**跨仓 pin 链的窗口级兜底也在本 sweep**(联动单第一产者仍是接受座位;
⛔ 只立单不执行 bump): objectui pin 落后且其队列已空 ⇒ 在本仓立/刷新 console bump 单;
Expand DownExpand Up@@ -587,16 +587,16 @@ closes-list:部分交付 PR 特意写 Refs 而非 Fixes 时它恰为空,正是
**派发模型是 PM 的逐卡显式决定** —— 下限 `sonnet`(机械卡:
正确性由门禁农场机械判定,失败在漏跑门不在判断);默认判断档 `opus`(M/L、
裁决实施、任何带设计判断的卡;拿不准就升一档 —— 错派低档的返工贵过省下的额度);
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:
① 凡改**协议语义面**的卡一律 `model: "claude-fable-5"` —— 面 = 本 `SKILL.md` 主文件、
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:① 凡改
**协议语义面**的卡一律 `CONTRACT_REVIEW_TIER` 档(`--tier` 输出)—— 面 = 本 `SKILL.md` 主文件、
决策框架拷贝所在文件(以 `check:skill-frame-sync` 强制处为准)与 `.claude/agents/os-dev.md`(2026-08-20
收窄,维护者原话:「接受你的建议」—— fable 当审计师用,不当施工队用);
仅 `references/**` 面、或任一 pm-dispatch 治理面上一行级机械文本改动的卡降为 **opus 施工**
(纯机械一行 PM 酌定可至 sonnet 地板),**补偿控制即既有技能面复核档要求**(ACCEPT 路径分叉
①:复核席跑契约复审档位)—— opus 施工的治理面改动只经 fable 档复核到达维护者,
⛔ 不新增标签不新增链;② 凡**改变契约接受/拒绝行为或扩大公开面**的卡(`domain:spec`
语义面;判据即分诊代裁的机械边界测试与 spec 席内分派判据 —— `references/lanes/spec.md`,
⛔ 不另抄第二份)一律 `claude-fable-5`(维护者 2026-08-12 裁定,
⛔ 不另抄第二份)一律 `CONTRACT_REVIEW_TIER` 档(维护者 2026-08-12 裁定,
原话:「同意,就按语义面收窄,立卡并通知 spec 席」)—— 契约错毒化一切下游,
全仓最贵(条款②闸门见「入队与落地」)。**负边界(维护者 2026-08-28 裁定)**:
运行时权限/安全**行为**变更不是条款② —— 归人工地板「安全/权限边界」
Expand Down
58 changes: 32 additions & 26 deletions scripts/pm/dispatch-gates.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -7934,6 +7934,26 @@ export function residueLines(
// Model-tier derivation — the half of the tier decision that IS a path question
// ---------------------------------------------------------------------------

/**
* The single source of truth for the model tier the PM lane's governance
* reads — clause ②'s CONTRACT-REVIEW tier: the tier a card that changes
* contract accept/reject behaviour or widens the public surface must be
* dispatched at, and the tier the `needs:contract-review` re-review sub-round
* must itself be running at (its opening self-check reads this). Declared HERE
* and only here, as a constant, so a model upgrade is a one-line change in one
* file — the clause-① mandate rows below read it, the self-test compares
* against it, and the PM skill's prose names it, so the model id is spelled as
* a VALUE on this one line and nowhere else across `scripts/pm/**` and
* `.claude/skills/pm-dispatch/**` (a second value site is what let this
* constant drift from the served tier unnoticed). The comparison against the
* served tier is EXACT, never a family or prefix floor — widening a governance
* gate's accept set is the maintainer's decision, not a refresh-time
* convenience. The review label deliberately names WHAT is reviewed, never a
* model (maintainer, 2026-08-16: 「needs:fable-review 这个标签不好,下次模型升级怎么办」).
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5-1';

/**
* The globs that MANDATE a model tier for any card whose file surface touches
* them, as DATA. This is the one list in this file besides CHANGE_KIND_GATES,
Expand DownExpand Up@@ -7967,7 +7987,7 @@ export function residueLines(
* about paths:
*
* - clause ①, encoded below: a card editing the PM lane's PROTOCOL-SEMANTIC
* surfaces is `claude-fable-5` — the pm-dispatch SKILL.md main file, every
* surfaces is `CONTRACT_REVIEW_TIER` — the pm-dispatch SKILL.md main file, every
* file carrying an enforced copy of the decision frame (the COPIES table
* of check:skill-frame-sync), and the dev-agent definition. Narrowed from
* "the whole skill tree, references included" by the maintainer's
Expand All@@ -7977,7 +7997,7 @@ export function residueLines(
* file-surface predicate, and exactly what this script takes as argv;
* - clause ②, NOT encoded and deliberately not: a card that changes contract
* accept/reject behaviour or widens the public surface is also
* `claude-fable-5`. That is judged from the card's CONTENT — what the change
* `CONTRACT_REVIEW_TIER`. That is judged from the card's CONTENT — what the change
* does to the contract — and a path cannot answer it. An ordinary-looking
* surface (one package's source file) is the NORMAL shape of a clause-②
* card. The closest a path can honestly get is SUSPICION:
Expand DownExpand Up@@ -8061,35 +8081,21 @@ export function residueLines(
export const MANDATORY_TIER_GLOBS = [
{
glob: '.claude/skills/pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: '.claude/agents/os-dev.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: 'skills/objectstack-pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
];

/**
* Clause ②'s single source of truth for the CONTRACT-REVIEW tier: the tier a
* card that changes contract accept/reject behaviour or widens the public
* surface must be dispatched at, and the tier the `needs:contract-review`
* re-review sub-round must itself be running at (its opening self-check reads
* this). Declared HERE and only here, as a constant, so a model upgrade is a
* one-line change in one file. The review label deliberately names WHAT is
* reviewed, never a model (maintainer, 2026-08-16: 「needs:fable-review 这个标
* 签不好,下次模型升级怎么办」), and the PM skill's prose points at this
* constant instead of spelling a model name.
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5';

/**
* The globs that make a surface a clause-② SUSPECT — a HINT, never a verdict.
*
Expand DownExpand Up@@ -15298,12 +15304,12 @@ function selfTest() {
// mandate, and the ordinary surface that must NOT be mandated (a tool that
// mandates everything is ignored, which loses the guardrail by the other road).
const fableOf = (paths) => deriveTier(paths);
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === 'claude-fable-5');
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === CONTRACT_REVIEW_TIER);
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
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);
const mixed = fableOf(['packages/spec/src/data/filter.zod.ts', '.claude/agents/os-dev.md']);
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === 'claude-fable-5');
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === CONTRACT_REVIEW_TIER);
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'));
t('an ordinary surface carries no path-derived mandate', fableOf(['packages/spec/src/data/filter.zod.ts']).mandatory === false);
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);
Expand All@@ -15316,7 +15322,7 @@ function selfTest() {
// The rendering is where the invariant is actually delivered: the claim
// comment quotes THESE lines.
const mandLines = tierLines(mixed).join('\n');
t('the mandatory rendering names the tier', mandLines.includes('claude-fable-5'));
t('the mandatory rendering names the tier', mandLines.includes(CONTRACT_REVIEW_TIER));
t('the mandatory rendering says MANDATORY in a word a reader cannot skim past', mandLines.includes('MANDATORY'));
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'"));
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'));
Expand All@@ -15337,7 +15343,7 @@ function selfTest() {
let ambiguityRefused = false;
try {
deriveTier(['.claude/skills/pm-dispatch/SKILL.md'], [
{ glob: '.claude/skills/pm-dispatch/**', tier: 'claude-fable-5', why: 'a' },
{ glob: '.claude/skills/pm-dispatch/**', tier: CONTRACT_REVIEW_TIER, why: 'a' },
{ glob: '.claude/skills/**', tier: 'opus', why: 'b' },
]);
} catch {
Expand DownExpand Up@@ -15375,7 +15381,7 @@ function selfTest() {
/* frameFiles stays empty and the cases below fail loudly */
}
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(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === 'claude-fable-5'));
t(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === CONTRACT_REVIEW_TIER));
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'));

// ── Clause-② suspicion (the enqueue-gate card): hit / no hit / wording ────
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/skills/pm-dispatch/SKILL.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -357,8 +357,8 @@ issue 任何人不得认领** —— 那意味着分诊还没走到,不是「可
跑到清空排空稳态,阈值吸收突发(实测 40 分钟到 13 张 ≈ 稳态四倍,审计式倾倒形态)——
没有阈值,倾倒只能按到达顺序摊进常规轮,失掉同族同上下文一次读完的批次红利,
队尾同期继续增长;⛔ 二者不可互相替代。
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起
`claude-fable-5` 分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起 `CONTRACT_REVIEW_TIER` 档
分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
⛔ 不写码不认领;产出落卡,与定时轮分诊同格式同效力(细则见 `references/dispatch-runbook.md`)。
**跨仓 pin 链的窗口级兜底也在本 sweep**(联动单第一产者仍是接受座位;
⛔ 只立单不执行 bump): objectui pin 落后且其队列已空 ⇒ 在本仓立/刷新 console bump 单;
Expand DownExpand Up@@ -587,16 +587,16 @@ closes-list:部分交付 PR 特意写 Refs 而非 Fixes 时它恰为空,正是
**派发模型是 PM 的逐卡显式决定** —— 下限 `sonnet`(机械卡:
正确性由门禁农场机械判定,失败在漏跑门不在判断);默认判断档 `opus`(M/L、
裁决实施、任何带设计判断的卡;拿不准就升一档 —— 错派低档的返工贵过省下的额度);
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:
① 凡改**协议语义面**的卡一律 `model: "claude-fable-5"` —— 面 = 本 `SKILL.md` 主文件、
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:① 凡改
**协议语义面**的卡一律 `CONTRACT_REVIEW_TIER` 档(`--tier` 输出)—— 面 = 本 `SKILL.md` 主文件、
决策框架拷贝所在文件(以 `check:skill-frame-sync` 强制处为准)与 `.claude/agents/os-dev.md`(2026-08-20
收窄,维护者原话:「接受你的建议」—— fable 当审计师用,不当施工队用);
仅 `references/**` 面、或任一 pm-dispatch 治理面上一行级机械文本改动的卡降为 **opus 施工**
(纯机械一行 PM 酌定可至 sonnet 地板),**补偿控制即既有技能面复核档要求**(ACCEPT 路径分叉
①:复核席跑契约复审档位)—— opus 施工的治理面改动只经 fable 档复核到达维护者,
⛔ 不新增标签不新增链;② 凡**改变契约接受/拒绝行为或扩大公开面**的卡(`domain:spec`
语义面;判据即分诊代裁的机械边界测试与 spec 席内分派判据 —— `references/lanes/spec.md`,
⛔ 不另抄第二份)一律 `claude-fable-5`(维护者 2026-08-12 裁定,
⛔ 不另抄第二份)一律 `CONTRACT_REVIEW_TIER` 档(维护者 2026-08-12 裁定,
原话:「同意,就按语义面收窄,立卡并通知 spec 席」)—— 契约错毒化一切下游,
全仓最贵(条款②闸门见「入队与落地」)。**负边界(维护者 2026-08-28 裁定)**:
运行时权限/安全**行为**变更不是条款② —— 归人工地板「安全/权限边界」
Expand Down
58 changes: 32 additions & 26 deletions scripts/pm/dispatch-gates.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -7934,6 +7934,26 @@ export function residueLines(
// Model-tier derivation — the half of the tier decision that IS a path question
// ---------------------------------------------------------------------------

/**
* The single source of truth for the model tier the PM lane's governance
* reads — clause ②'s CONTRACT-REVIEW tier: the tier a card that changes
* contract accept/reject behaviour or widens the public surface must be
* dispatched at, and the tier the `needs:contract-review` re-review sub-round
* must itself be running at (its opening self-check reads this). Declared HERE
* and only here, as a constant, so a model upgrade is a one-line change in one
* file — the clause-① mandate rows below read it, the self-test compares
* against it, and the PM skill's prose names it, so the model id is spelled as
* a VALUE on this one line and nowhere else across `scripts/pm/**` and
* `.claude/skills/pm-dispatch/**` (a second value site is what let this
* constant drift from the served tier unnoticed). The comparison against the
* served tier is EXACT, never a family or prefix floor — widening a governance
* gate's accept set is the maintainer's decision, not a refresh-time
* convenience. The review label deliberately names WHAT is reviewed, never a
* model (maintainer, 2026-08-16: 「needs:fable-review 这个标签不好,下次模型升级怎么办」).
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5-1';

/**
* The globs that MANDATE a model tier for any card whose file surface touches
* them, as DATA. This is the one list in this file besides CHANGE_KIND_GATES,
Expand DownExpand Up@@ -7967,7 +7987,7 @@ export function residueLines(
* about paths:
*
* - clause ①, encoded below: a card editing the PM lane's PROTOCOL-SEMANTIC
* surfaces is `claude-fable-5` — the pm-dispatch SKILL.md main file, every
* surfaces is `CONTRACT_REVIEW_TIER` — the pm-dispatch SKILL.md main file, every
* file carrying an enforced copy of the decision frame (the COPIES table
* of check:skill-frame-sync), and the dev-agent definition. Narrowed from
* "the whole skill tree, references included" by the maintainer's
Expand All@@ -7977,7 +7997,7 @@ export function residueLines(
* file-surface predicate, and exactly what this script takes as argv;
* - clause ②, NOT encoded and deliberately not: a card that changes contract
* accept/reject behaviour or widens the public surface is also
* `claude-fable-5`. That is judged from the card's CONTENT — what the change
* `CONTRACT_REVIEW_TIER`. That is judged from the card's CONTENT — what the change
* does to the contract — and a path cannot answer it. An ordinary-looking
* surface (one package's source file) is the NORMAL shape of a clause-②
* card. The closest a path can honestly get is SUSPICION:
Expand DownExpand Up@@ -8061,35 +8081,21 @@ export function residueLines(
export const MANDATORY_TIER_GLOBS = [
{
glob: '.claude/skills/pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: '.claude/agents/os-dev.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: 'skills/objectstack-pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
];

/**
* Clause ②'s single source of truth for the CONTRACT-REVIEW tier: the tier a
* card that changes contract accept/reject behaviour or widens the public
* surface must be dispatched at, and the tier the `needs:contract-review`
* re-review sub-round must itself be running at (its opening self-check reads
* this). Declared HERE and only here, as a constant, so a model upgrade is a
* one-line change in one file. The review label deliberately names WHAT is
* reviewed, never a model (maintainer, 2026-08-16: 「needs:fable-review 这个标
* 签不好,下次模型升级怎么办」), and the PM skill's prose points at this
* constant instead of spelling a model name.
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5';

/**
* The globs that make a surface a clause-② SUSPECT — a HINT, never a verdict.
*
Expand DownExpand Up@@ -15298,12 +15304,12 @@ function selfTest() {
// mandate, and the ordinary surface that must NOT be mandated (a tool that
// mandates everything is ignored, which loses the guardrail by the other road).
const fableOf = (paths) => deriveTier(paths);
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === 'claude-fable-5');
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === CONTRACT_REVIEW_TIER);
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
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);
const mixed = fableOf(['packages/spec/src/data/filter.zod.ts', '.claude/agents/os-dev.md']);
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === 'claude-fable-5');
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === CONTRACT_REVIEW_TIER);
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'));
t('an ordinary surface carries no path-derived mandate', fableOf(['packages/spec/src/data/filter.zod.ts']).mandatory === false);
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);
Expand All@@ -15316,7 +15322,7 @@ function selfTest() {
// The rendering is where the invariant is actually delivered: the claim
// comment quotes THESE lines.
const mandLines = tierLines(mixed).join('\n');
t('the mandatory rendering names the tier', mandLines.includes('claude-fable-5'));
t('the mandatory rendering names the tier', mandLines.includes(CONTRACT_REVIEW_TIER));
t('the mandatory rendering says MANDATORY in a word a reader cannot skim past', mandLines.includes('MANDATORY'));
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'"));
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'));
Expand All@@ -15337,7 +15343,7 @@ function selfTest() {
let ambiguityRefused = false;
try {
deriveTier(['.claude/skills/pm-dispatch/SKILL.md'], [
{ glob: '.claude/skills/pm-dispatch/**', tier: 'claude-fable-5', why: 'a' },
{ glob: '.claude/skills/pm-dispatch/**', tier: CONTRACT_REVIEW_TIER, why: 'a' },
{ glob: '.claude/skills/**', tier: 'opus', why: 'b' },
]);
} catch {
Expand DownExpand Up@@ -15375,7 +15381,7 @@ function selfTest() {
/* frameFiles stays empty and the cases below fail loudly */
}
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(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === 'claude-fable-5'));
t(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === CONTRACT_REVIEW_TIER));
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'));

// ── Clause-② suspicion (the enqueue-gate card): hit / no hit / wording ────
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/skills/pm-dispatch/SKILL.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -357,8 +357,8 @@ issue 任何人不得认领** —— 那意味着分诊还没走到,不是「可
跑到清空排空稳态,阈值吸收突发(实测 40 分钟到 13 张 ≈ 稳态四倍,审计式倾倒形态)——
没有阈值,倾倒只能按到达顺序摊进常规轮,失掉同族同上下文一次读完的批次红利,
队尾同期继续增长;⛔ 二者不可互相替代。
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起
`claude-fable-5` 分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起 `CONTRACT_REVIEW_TIER` 档
分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
⛔ 不写码不认领;产出落卡,与定时轮分诊同格式同效力(细则见 `references/dispatch-runbook.md`)。
**跨仓 pin 链的窗口级兜底也在本 sweep**(联动单第一产者仍是接受座位;
⛔ 只立单不执行 bump): objectui pin 落后且其队列已空 ⇒ 在本仓立/刷新 console bump 单;
Expand DownExpand Up@@ -587,16 +587,16 @@ closes-list:部分交付 PR 特意写 Refs 而非 Fixes 时它恰为空,正是
**派发模型是 PM 的逐卡显式决定** —— 下限 `sonnet`(机械卡:
正确性由门禁农场机械判定,失败在漏跑门不在判断);默认判断档 `opus`(M/L、
裁决实施、任何带设计判断的卡;拿不准就升一档 —— 错派低档的返工贵过省下的额度);
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:
① 凡改**协议语义面**的卡一律 `model: "claude-fable-5"` —— 面 = 本 `SKILL.md` 主文件、
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:① 凡改
**协议语义面**的卡一律 `CONTRACT_REVIEW_TIER` 档(`--tier` 输出)—— 面 = 本 `SKILL.md` 主文件、
决策框架拷贝所在文件(以 `check:skill-frame-sync` 强制处为准)与 `.claude/agents/os-dev.md`(2026-08-20
收窄,维护者原话:「接受你的建议」—— fable 当审计师用,不当施工队用);
仅 `references/**` 面、或任一 pm-dispatch 治理面上一行级机械文本改动的卡降为 **opus 施工**
(纯机械一行 PM 酌定可至 sonnet 地板),**补偿控制即既有技能面复核档要求**(ACCEPT 路径分叉
①:复核席跑契约复审档位)—— opus 施工的治理面改动只经 fable 档复核到达维护者,
⛔ 不新增标签不新增链;② 凡**改变契约接受/拒绝行为或扩大公开面**的卡(`domain:spec`
语义面;判据即分诊代裁的机械边界测试与 spec 席内分派判据 —— `references/lanes/spec.md`,
⛔ 不另抄第二份)一律 `claude-fable-5`(维护者 2026-08-12 裁定,
⛔ 不另抄第二份)一律 `CONTRACT_REVIEW_TIER` 档(维护者 2026-08-12 裁定,
原话:「同意,就按语义面收窄,立卡并通知 spec 席」)—— 契约错毒化一切下游,
全仓最贵(条款②闸门见「入队与落地」)。**负边界(维护者 2026-08-28 裁定)**:
运行时权限/安全**行为**变更不是条款② —— 归人工地板「安全/权限边界」
Expand Down
58 changes: 32 additions & 26 deletions scripts/pm/dispatch-gates.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -7934,6 +7934,26 @@ export function residueLines(
// Model-tier derivation — the half of the tier decision that IS a path question
// ---------------------------------------------------------------------------

/**
* The single source of truth for the model tier the PM lane's governance
* reads — clause ②'s CONTRACT-REVIEW tier: the tier a card that changes
* contract accept/reject behaviour or widens the public surface must be
* dispatched at, and the tier the `needs:contract-review` re-review sub-round
* must itself be running at (its opening self-check reads this). Declared HERE
* and only here, as a constant, so a model upgrade is a one-line change in one
* file — the clause-① mandate rows below read it, the self-test compares
* against it, and the PM skill's prose names it, so the model id is spelled as
* a VALUE on this one line and nowhere else across `scripts/pm/**` and
* `.claude/skills/pm-dispatch/**` (a second value site is what let this
* constant drift from the served tier unnoticed). The comparison against the
* served tier is EXACT, never a family or prefix floor — widening a governance
* gate's accept set is the maintainer's decision, not a refresh-time
* convenience. The review label deliberately names WHAT is reviewed, never a
* model (maintainer, 2026-08-16: 「needs:fable-review 这个标签不好,下次模型升级怎么办」).
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5-1';

/**
* The globs that MANDATE a model tier for any card whose file surface touches
* them, as DATA. This is the one list in this file besides CHANGE_KIND_GATES,
Expand DownExpand Up@@ -7967,7 +7987,7 @@ export function residueLines(
* about paths:
*
* - clause ①, encoded below: a card editing the PM lane's PROTOCOL-SEMANTIC
* surfaces is `claude-fable-5` — the pm-dispatch SKILL.md main file, every
* surfaces is `CONTRACT_REVIEW_TIER` — the pm-dispatch SKILL.md main file, every
* file carrying an enforced copy of the decision frame (the COPIES table
* of check:skill-frame-sync), and the dev-agent definition. Narrowed from
* "the whole skill tree, references included" by the maintainer's
Expand All@@ -7977,7 +7997,7 @@ export function residueLines(
* file-surface predicate, and exactly what this script takes as argv;
* - clause ②, NOT encoded and deliberately not: a card that changes contract
* accept/reject behaviour or widens the public surface is also
* `claude-fable-5`. That is judged from the card's CONTENT — what the change
* `CONTRACT_REVIEW_TIER`. That is judged from the card's CONTENT — what the change
* does to the contract — and a path cannot answer it. An ordinary-looking
* surface (one package's source file) is the NORMAL shape of a clause-②
* card. The closest a path can honestly get is SUSPICION:
Expand DownExpand Up@@ -8061,35 +8081,21 @@ export function residueLines(
export const MANDATORY_TIER_GLOBS = [
{
glob: '.claude/skills/pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: '.claude/agents/os-dev.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: 'skills/objectstack-pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
];

/**
* Clause ②'s single source of truth for the CONTRACT-REVIEW tier: the tier a
* card that changes contract accept/reject behaviour or widens the public
* surface must be dispatched at, and the tier the `needs:contract-review`
* re-review sub-round must itself be running at (its opening self-check reads
* this). Declared HERE and only here, as a constant, so a model upgrade is a
* one-line change in one file. The review label deliberately names WHAT is
* reviewed, never a model (maintainer, 2026-08-16: 「needs:fable-review 这个标
* 签不好,下次模型升级怎么办」), and the PM skill's prose points at this
* constant instead of spelling a model name.
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5';

/**
* The globs that make a surface a clause-② SUSPECT — a HINT, never a verdict.
*
Expand DownExpand Up@@ -15298,12 +15304,12 @@ function selfTest() {
// mandate, and the ordinary surface that must NOT be mandated (a tool that
// mandates everything is ignored, which loses the guardrail by the other road).
const fableOf = (paths) => deriveTier(paths);
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === 'claude-fable-5');
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === CONTRACT_REVIEW_TIER);
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
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);
const mixed = fableOf(['packages/spec/src/data/filter.zod.ts', '.claude/agents/os-dev.md']);
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === 'claude-fable-5');
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === CONTRACT_REVIEW_TIER);
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'));
t('an ordinary surface carries no path-derived mandate', fableOf(['packages/spec/src/data/filter.zod.ts']).mandatory === false);
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);
Expand All@@ -15316,7 +15322,7 @@ function selfTest() {
// The rendering is where the invariant is actually delivered: the claim
// comment quotes THESE lines.
const mandLines = tierLines(mixed).join('\n');
t('the mandatory rendering names the tier', mandLines.includes('claude-fable-5'));
t('the mandatory rendering names the tier', mandLines.includes(CONTRACT_REVIEW_TIER));
t('the mandatory rendering says MANDATORY in a word a reader cannot skim past', mandLines.includes('MANDATORY'));
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'"));
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'));
Expand All@@ -15337,7 +15343,7 @@ function selfTest() {
let ambiguityRefused = false;
try {
deriveTier(['.claude/skills/pm-dispatch/SKILL.md'], [
{ glob: '.claude/skills/pm-dispatch/**', tier: 'claude-fable-5', why: 'a' },
{ glob: '.claude/skills/pm-dispatch/**', tier: CONTRACT_REVIEW_TIER, why: 'a' },
{ glob: '.claude/skills/**', tier: 'opus', why: 'b' },
]);
} catch {
Expand DownExpand Up@@ -15375,7 +15381,7 @@ function selfTest() {
/* frameFiles stays empty and the cases below fail loudly */
}
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(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === 'claude-fable-5'));
t(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === CONTRACT_REVIEW_TIER));
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'));

// ── Clause-② suspicion (the enqueue-gate card): hit / no hit / wording ────
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/skills/pm-dispatch/SKILL.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -357,8 +357,8 @@ issue 任何人不得认领** —— 那意味着分诊还没走到,不是「可
跑到清空排空稳态,阈值吸收突发(实测 40 分钟到 13 张 ≈ 稳态四倍,审计式倾倒形态)——
没有阈值,倾倒只能按到达顺序摊进常规轮,失掉同族同上下文一次读完的批次红利,
队尾同期继续增长;⛔ 二者不可互相替代。
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起
`claude-fable-5` 分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起 `CONTRACT_REVIEW_TIER` 档
分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
⛔ 不写码不认领;产出落卡,与定时轮分诊同格式同效力(细则见 `references/dispatch-runbook.md`)。
**跨仓 pin 链的窗口级兜底也在本 sweep**(联动单第一产者仍是接受座位;
⛔ 只立单不执行 bump): objectui pin 落后且其队列已空 ⇒ 在本仓立/刷新 console bump 单;
Expand DownExpand Up@@ -587,16 +587,16 @@ closes-list:部分交付 PR 特意写 Refs 而非 Fixes 时它恰为空,正是
**派发模型是 PM 的逐卡显式决定** —— 下限 `sonnet`(机械卡:
正确性由门禁农场机械判定,失败在漏跑门不在判断);默认判断档 `opus`(M/L、
裁决实施、任何带设计判断的卡;拿不准就升一档 —— 错派低档的返工贵过省下的额度);
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:
① 凡改**协议语义面**的卡一律 `model: "claude-fable-5"` —— 面 = 本 `SKILL.md` 主文件、
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:① 凡改
**协议语义面**的卡一律 `CONTRACT_REVIEW_TIER` 档(`--tier` 输出)—— 面 = 本 `SKILL.md` 主文件、
决策框架拷贝所在文件(以 `check:skill-frame-sync` 强制处为准)与 `.claude/agents/os-dev.md`(2026-08-20
收窄,维护者原话:「接受你的建议」—— fable 当审计师用,不当施工队用);
仅 `references/**` 面、或任一 pm-dispatch 治理面上一行级机械文本改动的卡降为 **opus 施工**
(纯机械一行 PM 酌定可至 sonnet 地板),**补偿控制即既有技能面复核档要求**(ACCEPT 路径分叉
①:复核席跑契约复审档位)—— opus 施工的治理面改动只经 fable 档复核到达维护者,
⛔ 不新增标签不新增链;② 凡**改变契约接受/拒绝行为或扩大公开面**的卡(`domain:spec`
语义面;判据即分诊代裁的机械边界测试与 spec 席内分派判据 —— `references/lanes/spec.md`,
⛔ 不另抄第二份)一律 `claude-fable-5`(维护者 2026-08-12 裁定,
⛔ 不另抄第二份)一律 `CONTRACT_REVIEW_TIER` 档(维护者 2026-08-12 裁定,
原话:「同意,就按语义面收窄,立卡并通知 spec 席」)—— 契约错毒化一切下游,
全仓最贵(条款②闸门见「入队与落地」)。**负边界(维护者 2026-08-28 裁定)**:
运行时权限/安全**行为**变更不是条款② —— 归人工地板「安全/权限边界」
Expand Down
58 changes: 32 additions & 26 deletions scripts/pm/dispatch-gates.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -7934,6 +7934,26 @@ export function residueLines(
// Model-tier derivation — the half of the tier decision that IS a path question
// ---------------------------------------------------------------------------

/**
* The single source of truth for the model tier the PM lane's governance
* reads — clause ②'s CONTRACT-REVIEW tier: the tier a card that changes
* contract accept/reject behaviour or widens the public surface must be
* dispatched at, and the tier the `needs:contract-review` re-review sub-round
* must itself be running at (its opening self-check reads this). Declared HERE
* and only here, as a constant, so a model upgrade is a one-line change in one
* file — the clause-① mandate rows below read it, the self-test compares
* against it, and the PM skill's prose names it, so the model id is spelled as
* a VALUE on this one line and nowhere else across `scripts/pm/**` and
* `.claude/skills/pm-dispatch/**` (a second value site is what let this
* constant drift from the served tier unnoticed). The comparison against the
* served tier is EXACT, never a family or prefix floor — widening a governance
* gate's accept set is the maintainer's decision, not a refresh-time
* convenience. The review label deliberately names WHAT is reviewed, never a
* model (maintainer, 2026-08-16: 「needs:fable-review 这个标签不好,下次模型升级怎么办」).
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5-1';

/**
* The globs that MANDATE a model tier for any card whose file surface touches
* them, as DATA. This is the one list in this file besides CHANGE_KIND_GATES,
Expand DownExpand Up@@ -7967,7 +7987,7 @@ export function residueLines(
* about paths:
*
* - clause ①, encoded below: a card editing the PM lane's PROTOCOL-SEMANTIC
* surfaces is `claude-fable-5` — the pm-dispatch SKILL.md main file, every
* surfaces is `CONTRACT_REVIEW_TIER` — the pm-dispatch SKILL.md main file, every
* file carrying an enforced copy of the decision frame (the COPIES table
* of check:skill-frame-sync), and the dev-agent definition. Narrowed from
* "the whole skill tree, references included" by the maintainer's
Expand All@@ -7977,7 +7997,7 @@ export function residueLines(
* file-surface predicate, and exactly what this script takes as argv;
* - clause ②, NOT encoded and deliberately not: a card that changes contract
* accept/reject behaviour or widens the public surface is also
* `claude-fable-5`. That is judged from the card's CONTENT — what the change
* `CONTRACT_REVIEW_TIER`. That is judged from the card's CONTENT — what the change
* does to the contract — and a path cannot answer it. An ordinary-looking
* surface (one package's source file) is the NORMAL shape of a clause-②
* card. The closest a path can honestly get is SUSPICION:
Expand DownExpand Up@@ -8061,35 +8081,21 @@ export function residueLines(
export const MANDATORY_TIER_GLOBS = [
{
glob: '.claude/skills/pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: '.claude/agents/os-dev.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: 'skills/objectstack-pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
];

/**
* Clause ②'s single source of truth for the CONTRACT-REVIEW tier: the tier a
* card that changes contract accept/reject behaviour or widens the public
* surface must be dispatched at, and the tier the `needs:contract-review`
* re-review sub-round must itself be running at (its opening self-check reads
* this). Declared HERE and only here, as a constant, so a model upgrade is a
* one-line change in one file. The review label deliberately names WHAT is
* reviewed, never a model (maintainer, 2026-08-16: 「needs:fable-review 这个标
* 签不好,下次模型升级怎么办」), and the PM skill's prose points at this
* constant instead of spelling a model name.
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5';

/**
* The globs that make a surface a clause-② SUSPECT — a HINT, never a verdict.
*
Expand DownExpand Up@@ -15298,12 +15304,12 @@ function selfTest() {
// mandate, and the ordinary surface that must NOT be mandated (a tool that
// mandates everything is ignored, which loses the guardrail by the other road).
const fableOf = (paths) => deriveTier(paths);
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === 'claude-fable-5');
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === CONTRACT_REVIEW_TIER);
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
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);
const mixed = fableOf(['packages/spec/src/data/filter.zod.ts', '.claude/agents/os-dev.md']);
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === 'claude-fable-5');
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === CONTRACT_REVIEW_TIER);
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'));
t('an ordinary surface carries no path-derived mandate', fableOf(['packages/spec/src/data/filter.zod.ts']).mandatory === false);
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);
Expand All@@ -15316,7 +15322,7 @@ function selfTest() {
// The rendering is where the invariant is actually delivered: the claim
// comment quotes THESE lines.
const mandLines = tierLines(mixed).join('\n');
t('the mandatory rendering names the tier', mandLines.includes('claude-fable-5'));
t('the mandatory rendering names the tier', mandLines.includes(CONTRACT_REVIEW_TIER));
t('the mandatory rendering says MANDATORY in a word a reader cannot skim past', mandLines.includes('MANDATORY'));
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'"));
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'));
Expand All@@ -15337,7 +15343,7 @@ function selfTest() {
let ambiguityRefused = false;
try {
deriveTier(['.claude/skills/pm-dispatch/SKILL.md'], [
{ glob: '.claude/skills/pm-dispatch/**', tier: 'claude-fable-5', why: 'a' },
{ glob: '.claude/skills/pm-dispatch/**', tier: CONTRACT_REVIEW_TIER, why: 'a' },
{ glob: '.claude/skills/**', tier: 'opus', why: 'b' },
]);
} catch {
Expand DownExpand Up@@ -15375,7 +15381,7 @@ function selfTest() {
/* frameFiles stays empty and the cases below fail loudly */
}
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(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === 'claude-fable-5'));
t(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === CONTRACT_REVIEW_TIER));
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'));

// ── Clause-② suspicion (the enqueue-gate card): hit / no hit / wording ────
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .claude/skills/pm-dispatch/SKILL.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -357,8 +357,8 @@ issue 任何人不得认领** —— 那意味着分诊还没走到,不是「可
跑到清空排空稳态,阈值吸收突发(实测 40 分钟到 13 张 ≈ 稳态四倍,审计式倾倒形态)——
没有阈值,倾倒只能按到达顺序摊进常规轮,失掉同族同上下文一次读完的批次红利,
队尾同期继续增长;⛔ 二者不可互相替代。
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起
`claude-fable-5` 分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
**紧急卡直接分诊** (维护者 2026-08-13):维护者点名或 p0 嫌疑 ⇒ 立即起 `CONTRACT_REVIEW_TIER` 档
分诊子代理,不等定时轮次;授权面 = 分诊本身(定级/路由/标签/既有评论格式),
⛔ 不写码不认领;产出落卡,与定时轮分诊同格式同效力(细则见 `references/dispatch-runbook.md`)。
**跨仓 pin 链的窗口级兜底也在本 sweep**(联动单第一产者仍是接受座位;
⛔ 只立单不执行 bump): objectui pin 落后且其队列已空 ⇒ 在本仓立/刷新 console bump 单;
Expand DownExpand Up@@ -587,16 +587,16 @@ closes-list:部分交付 PR 特意写 Refs 而非 Fixes 时它恰为空,正是
**派发模型是 PM 的逐卡显式决定** —— 下限 `sonnet`(机械卡:
正确性由门禁农场机械判定,失败在漏跑门不在判断);默认判断档 `opus`(M/L、
裁决实施、任何带设计判断的卡;拿不准就升一档 —— 错派低档的返工贵过省下的额度);
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:
① 凡改**协议语义面**的卡一律 `model: "claude-fable-5"` —— 面 = 本 `SKILL.md` 主文件、
上限 `fable`(最重协议/流程/编排卡,按卡取用非新默认)。**⛔ 强制条款两条**:① 凡改
**协议语义面**的卡一律 `CONTRACT_REVIEW_TIER` 档(`--tier` 输出)—— 面 = 本 `SKILL.md` 主文件、
决策框架拷贝所在文件(以 `check:skill-frame-sync` 强制处为准)与 `.claude/agents/os-dev.md`(2026-08-20
收窄,维护者原话:「接受你的建议」—— fable 当审计师用,不当施工队用);
仅 `references/**` 面、或任一 pm-dispatch 治理面上一行级机械文本改动的卡降为 **opus 施工**
(纯机械一行 PM 酌定可至 sonnet 地板),**补偿控制即既有技能面复核档要求**(ACCEPT 路径分叉
①:复核席跑契约复审档位)—— opus 施工的治理面改动只经 fable 档复核到达维护者,
⛔ 不新增标签不新增链;② 凡**改变契约接受/拒绝行为或扩大公开面**的卡(`domain:spec`
语义面;判据即分诊代裁的机械边界测试与 spec 席内分派判据 —— `references/lanes/spec.md`,
⛔ 不另抄第二份)一律 `claude-fable-5`(维护者 2026-08-12 裁定,
⛔ 不另抄第二份)一律 `CONTRACT_REVIEW_TIER` 档(维护者 2026-08-12 裁定,
原话:「同意,就按语义面收窄,立卡并通知 spec 席」)—— 契约错毒化一切下游,
全仓最贵(条款②闸门见「入队与落地」)。**负边界(维护者 2026-08-28 裁定)**:
运行时权限/安全**行为**变更不是条款② —— 归人工地板「安全/权限边界」
Expand Down
58 changes: 32 additions & 26 deletions scripts/pm/dispatch-gates.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -7934,6 +7934,26 @@ export function residueLines(
// Model-tier derivation — the half of the tier decision that IS a path question
// ---------------------------------------------------------------------------

/**
* The single source of truth for the model tier the PM lane's governance
* reads — clause ②'s CONTRACT-REVIEW tier: the tier a card that changes
* contract accept/reject behaviour or widens the public surface must be
* dispatched at, and the tier the `needs:contract-review` re-review sub-round
* must itself be running at (its opening self-check reads this). Declared HERE
* and only here, as a constant, so a model upgrade is a one-line change in one
* file — the clause-① mandate rows below read it, the self-test compares
* against it, and the PM skill's prose names it, so the model id is spelled as
* a VALUE on this one line and nowhere else across `scripts/pm/**` and
* `.claude/skills/pm-dispatch/**` (a second value site is what let this
* constant drift from the served tier unnoticed). The comparison against the
* served tier is EXACT, never a family or prefix floor — widening a governance
* gate's accept set is the maintainer's decision, not a refresh-time
* convenience. The review label deliberately names WHAT is reviewed, never a
* model (maintainer, 2026-08-16: 「needs:fable-review 这个标签不好,下次模型升级怎么办」).
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5-1';

/**
* The globs that MANDATE a model tier for any card whose file surface touches
* them, as DATA. This is the one list in this file besides CHANGE_KIND_GATES,
Expand DownExpand Up@@ -7967,7 +7987,7 @@ export function residueLines(
* about paths:
*
* - clause ①, encoded below: a card editing the PM lane's PROTOCOL-SEMANTIC
* surfaces is `claude-fable-5` — the pm-dispatch SKILL.md main file, every
* surfaces is `CONTRACT_REVIEW_TIER` — the pm-dispatch SKILL.md main file, every
* file carrying an enforced copy of the decision frame (the COPIES table
* of check:skill-frame-sync), and the dev-agent definition. Narrowed from
* "the whole skill tree, references included" by the maintainer's
Expand All@@ -7977,7 +7997,7 @@ export function residueLines(
* file-surface predicate, and exactly what this script takes as argv;
* - clause ②, NOT encoded and deliberately not: a card that changes contract
* accept/reject behaviour or widens the public surface is also
* `claude-fable-5`. That is judged from the card's CONTENT — what the change
* `CONTRACT_REVIEW_TIER`. That is judged from the card's CONTENT — what the change
* does to the contract — and a path cannot answer it. An ordinary-looking
* surface (one package's source file) is the NORMAL shape of a clause-②
* card. The closest a path can honestly get is SUSPICION:
Expand DownExpand Up@@ -8061,35 +8081,21 @@ export function residueLines(
export const MANDATORY_TIER_GLOBS = [
{
glob: '.claude/skills/pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: '.claude/agents/os-dev.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
{
glob: 'skills/objectstack-pm-dispatch/SKILL.md',
tier: 'claude-fable-5',
tier: CONTRACT_REVIEW_TIER,
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',
},
];

/**
* Clause ②'s single source of truth for the CONTRACT-REVIEW tier: the tier a
* card that changes contract accept/reject behaviour or widens the public
* surface must be dispatched at, and the tier the `needs:contract-review`
* re-review sub-round must itself be running at (its opening self-check reads
* this). Declared HERE and only here, as a constant, so a model upgrade is a
* one-line change in one file. The review label deliberately names WHAT is
* reviewed, never a model (maintainer, 2026-08-16: 「needs:fable-review 这个标
* 签不好,下次模型升级怎么办」), and the PM skill's prose points at this
* constant instead of spelling a model name.
* Rulebook: `.claude/skills/pm-dispatch/SKILL.md` 「入队与落地」 — the clause-② gate and the `needs:contract-review` review-chain bullets.
*/
export const CONTRACT_REVIEW_TIER = 'claude-fable-5';

/**
* The globs that make a surface a clause-② SUSPECT — a HINT, never a verdict.
*
Expand DownExpand Up@@ -15298,12 +15304,12 @@ function selfTest() {
// mandate, and the ordinary surface that must NOT be mandated (a tool that
// mandates everything is ignored, which loses the guardrail by the other road).
const fableOf = (paths) => deriveTier(paths);
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === 'claude-fable-5');
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === 'claude-fable-5');
t('the pm-dispatch SKILL.md MAIN file is fable-mandatory', fableOf(['.claude/skills/pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
t('the dev-agent definition is fable-mandatory', fableOf(['.claude/agents/os-dev.md']).tier === CONTRACT_REVIEW_TIER);
t('the published PM skill (two enforced frame copies) is fable-mandatory', fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).tier === CONTRACT_REVIEW_TIER);
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);
const mixed = fableOf(['packages/spec/src/data/filter.zod.ts', '.claude/agents/os-dev.md']);
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === 'claude-fable-5');
t('a MIXED surface is mandatory — one mandatory path decides, ordinary paths do not dilute it', mixed.mandatory && mixed.tier === CONTRACT_REVIEW_TIER);
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'));
t('an ordinary surface carries no path-derived mandate', fableOf(['packages/spec/src/data/filter.zod.ts']).mandatory === false);
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);
Expand All@@ -15316,7 +15322,7 @@ function selfTest() {
// The rendering is where the invariant is actually delivered: the claim
// comment quotes THESE lines.
const mandLines = tierLines(mixed).join('\n');
t('the mandatory rendering names the tier', mandLines.includes('claude-fable-5'));
t('the mandatory rendering names the tier', mandLines.includes(CONTRACT_REVIEW_TIER));
t('the mandatory rendering says MANDATORY in a word a reader cannot skim past', mandLines.includes('MANDATORY'));
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'"));
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'));
Expand All@@ -15337,7 +15343,7 @@ function selfTest() {
let ambiguityRefused = false;
try {
deriveTier(['.claude/skills/pm-dispatch/SKILL.md'], [
{ glob: '.claude/skills/pm-dispatch/**', tier: 'claude-fable-5', why: 'a' },
{ glob: '.claude/skills/pm-dispatch/**', tier: CONTRACT_REVIEW_TIER, why: 'a' },
{ glob: '.claude/skills/**', tier: 'opus', why: 'b' },
]);
} catch {
Expand DownExpand Up@@ -15375,7 +15381,7 @@ function selfTest() {
/* frameFiles stays empty and the cases below fail loudly */
}
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(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === 'claude-fable-5'));
t(`every frame-sync-enforced copy is fable-mandated (unmandated: ${frameFiles.filter((f) => !deriveTier([f]).mandatory).join(', ') || 'none'})`, frameFiles.length > 0 && frameFiles.every((f) => deriveTier([f]).tier === CONTRACT_REVIEW_TIER));
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'));

// ── Clause-② suspicion (the enqueue-gate card): hit / no hit / wording ────
Expand Down
Loading