Skip to content

Commit 0790d0a

Browse files
chore(pm-dispatch): ADR cards dispatch at the contract-review tier, effort floor xhigh (#25780)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"} --> _Requested by **Leehom** · [project thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1X5YusjSEHT8KRnVbiKjJ22B)_ Fixes #25729 Clause-②: no Metadata-protocol: none ## What changes The order quoted in the card body, verbatim (田騏, 2026-10-25T01:40:25Z): 「adr 这种任务都需要 fable extra,重新处理并立skills 卡片」. 1. **Tier row** — `MANDATORY_TIER_GLOB_ROWS` (`scripts/pm/dispatch-gates.data.mjs`) gains `docs/adr/**` → `CONTRACT_REVIEW_TIER`, a `why:` quoting the order, and `oneLineExit: false` (「都需要」 closes the one-line exit by path, the same shape as the `skills/**` row). The table docblock's provenance sentence and its one-line-exit paragraph name the new row. 2. **Effort floor** — carried in skill text, not by a printed line: the clause-① line of `references/execution-duties.md` (dispatch section, where tier and effort are chosen) now reads `凡改协议语义面或 docs/adr/** 的卡一律契约复审档;后者 effort 下限 xhigh`. A floor: difficulty may raise it, never lower it. 3. **Policy text** — the same clause-① line is the predicate the table copies, so the table stays a copy; the no-one-line-exemption line beside it now names `docs/adr/**` beside `skills/**`. 4. **Reason wording in `tierLines`** (`scripts/pm/dispatch-gates.mjs`) — the refusal of the one-line exit hard-coded "(the published catalog, 2026-09-10 ruling)" as the reason for EVERY `oneLineExit: false` glob, which would have printed the catalog's ruling for an ADR card. The existing line now points at the row's own `why`, already printed on the hit line above it. The comment above it is updated to match. 5. **Self-test** (`scripts/pm/dispatch-gates.self-test.mjs`) — the pin "every entry except `skills/**` keeps `oneLineExit` undefined" is widened to the two barred globs; new cases: the `docs/adr/**` row's shape, an ADR file mandated by that row alone, `docs/NORTH-STAR.md` and a `docs/adr-notes.md` prefix sibling NOT mandated, and the ADR rendering refusing the exit by name without borrowing the catalog's reason. `node scripts/pm/dispatch-gates.mjs --tier docs/adr/0028-metadata-naming-and-namespace-isolation.md` now answers `Model tier — MANDATORY: … ⇢ 'docs/adr/**'` with the one-line exit marked NOT available (on `origin/main` `f0cf3383` it answered "no path-derived mandate"). ## Which side of the instrument freeze `references/instrument-discipline.md` freezes `dispatch-gates.mjs`: no new gate, derivation line, ratchet, `--tier` mandated tier or constant. - Item 1 is a data row in the existing table in `dispatch-gates.data.mjs`, mandating the EXISTING `CONTRACT_REVIEW_TIER`; no new constant, no new tier, no new printed line. Whether a new path row counts as a new "`--tier` 强制档" under that wording is flagged for the reviewer below; the freeze also admits additions that quote the maintainer verbatim on the card, which the card body does. - Item 2 deliberately takes the skill-text carrier, so no effort line is added to `--tier`. - Item 4 edits an existing line's wording in place (no line added): it is the freeze's own exception, a derivation that would otherwise print a wrong reason. ## Line budget - `references/execution-duties.md`: 188 → 188 lines (ceiling 188). Both edits are in-place line rewrites, each ≤120 bytes (116 B and 110 B). Paid by deletion, not re-wrap: the line naming `skills/**` dropped the descriptors 「发布的」 and 「整根」 (the glob `skills/**` already spells the whole root; the publish reason `随 npx skills add 发运` stays on the line and in the `skills/**` row's `why`). - `SKILL.md`: untouched (319/319). The claim template's `effort: …按卡难度定` slot is not edited; the floor lives where effort is chosen. ## Gates (local, at `76ffcc7`) Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`: 39 commands, all run, all exit 0; `--ran` reconciliation: `39 derived famil(ies) accounted for — 39 run, 0 NOT-MEASURED`. Includes `pnpm check:pm-dispatch-gates` (fast tier, 1917 cases pass), `check:pm-skill-ratchet`, `check:pm-skill-id-lint`, `check:skill-frame-sync`, `check:pm-governed-merges`, `check:doc-authoring`, `check:nul-bytes`. `check:doc-formula-expressions` first answered exit 3 (prerequisite: `@objectstack/lint` unbuilt), re-run after building it: exit 0. Both-tier self-test result: see the report comment on the card. No changeset: the diff is `.claude/**` + `scripts/pm/**`, which publish nothing (`skip-changeset`). ## 维护者速读(草稿) - **改了什么:** 凡是改 `docs/adr/**`(写 ADR)的卡,派发时一律用最高档模型(契约复审档),effort 至少 `xhigh`,且不再有"一行级小改可降档"的例外。派发工具 `--tier` 对 ADR 路径会直接印出"强制档"。 - **为什么改:** 执行田騏的指令「adr 这种任务都需要 fable extra」。ADR 是后续实现卡的依据,写错要付两次代价(维护者审一次,每张据此拆出的实现卡再付一次);此前 ADR 卡按默认档派发,工具无法提示。 - **风险与代价(含回滚):** ADR 卡的派发成本上升(最高档 + xhigh)。不新增 CI 门禁、不拒任何 PR。回滚 = 撤回本 PR 一次提交。 - **席位意见:** - **你要做的:** 本 PR 触及 `.claude/**`(受管面 Tier S),需契约复审档复核 PASS 在案后由席位落地;如认为调整派发档位规则需要您本人点头,请在此答复。 ## Acceptance notes - Open for the reviewer: does a new path row in `MANDATORY_TIER_GLOB_ROWS` count as a new "`--tier` 强制档" under `instrument-discipline.md`'s freeze? The freeze admits additions that quote the maintainer verbatim on the card; whether 田騏's order is that word is triage's / the director seat's call, as the card's Boundary section says. - `max` stays 「凭维护者点名」 (the effort line right below clause ①); the ADR floor only raises the minimum to `xhigh`, it does not open `max` without that word. - #25765 (review depth for spec/skills/ADR) is out of scope here and remains open; re-dispatching already-merged ADR cards is out of scope. Implemented in the card thread, session `session_01X6Y7QshYEp9SD3ruEazFRS`; claim 6396126096. --- _Generated by [Claude Code](https://claude.ai/code/session_01X6Y7QshYEp9SD3ruEazFRS)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent afe6ab9 commit 0790d0a

4 files changed

Lines changed: 33 additions & 11 deletions

File tree

‎.claude/skills/pm-dispatch/references/execution-duties.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@
9393
- 下限档给机械卡(正确性由门禁农场机械判定)。
9494
- 默认判断档给 M/L、裁决实施与任何带设计判断的卡;拿不准就升一档。
9595
- 上限 = 契约复审档(`CONTRACT_REVIEW_TIER`),给最重协议/流程/编排卡,按卡取用非新默认。
96-
- 强制条款①:凡改协议语义面的卡一律契约复审档。
96+
- 强制条款①:凡改协议语义面或 `docs/adr/**` 的卡一律契约复审档;后者 effort 下限 `xhigh`。
9797
- 协议语义面 = 本 `SKILL.md`、决策框架拷贝所在文件、`.claude/agents/os-dev.md` 与 `skills/**`。
9898
- 仅 `references/**` 面、或治理面上一行级机械文本改动的卡,降为默认判断档施工。
99-
- 发布的 `skills/**` 整根随 `npx skills add` 发运,⛔ 无一行级豁免,逐行判机械不作数。
99+
- `skills/**`(随 `npx skills add` 发运)与 `docs/adr/**` ⛔ 无一行级豁免,逐行判机械不作数。
100100
- 纯机械一行 PM 酌定可至下限档;降档施工的补偿控制 = 复核席跑契约复审档。
101101
- 降档施工只经契约复审档复核到达维护者,⛔ 不新增标签不新增链。
102102
- 强制条款②:放宽接受集或扩大公开面的卡默认判断档施工;命中即 spec 车道的活。

‎scripts/pm/dispatch-gates.data.mjs‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,8 +1098,9 @@ export const CHANGE_KIND_ROWS = [
10981098
* encoded: the one-line-class downgrade does not exist for a surface under
10991099
* `skills/**` — the 2026-09-10 ruling's 必须, because a closed enumeration
11001100
* beats a per-line "is this mechanical" call on a catalog third parties
1101-
* install — so that entry carries `oneLineExit: false` and `tierLines`
1102-
* refuses to offer the exit for any card whose surface hits it.
1101+
* install — nor under `docs/adr/**`, whose 2026-10-25 order says 都需要 —
1102+
* so those entries carry `oneLineExit: false` and `tierLines` refuses to
1103+
* offer the exit for any card whose surface hits one of them.
11031104
*
11041105
* ## Why the globs are matched with `hintCovers`, asymmetry included
11051106
*
@@ -1160,8 +1161,9 @@ export const CHANGE_KIND_ROWS = [
11601161
*
11611162
* The authority for the policy is the maintainer ruling quoted in the PM
11621163
* dispatch skill (2026-08-10 three-tier ruling, clause ① of its 强制条款, as
1163-
* narrowed to protocol semantics by the 2026-08-20 ruling quoted there, and
1164-
* widened to the published `skills/**` catalog by the 2026-09-10 ruling).
1164+
* narrowed to protocol semantics by the 2026-08-20 ruling quoted there,
1165+
* widened to the published `skills/**` catalog by the 2026-09-10 ruling, and
1166+
* to ADR text, `docs/adr/**`, by the 2026-10-25 order quoted in its row).
11651167
* This table is a machine-readable copy of ONE predicate from it, not a second
11661168
* statement of the policy: when they disagree, the skill wins and this table is
11671169
* the thing to fix. The frame-copy half of the predicate is DEFINED by another
@@ -1189,6 +1191,15 @@ export const MANDATORY_TIER_GLOB_ROWS = [
11891191
// beats a per-line call on an installed catalog), so it is data, not prose.
11901192
oneLineExit: false,
11911193
},
1194+
{
1195+
glob: 'docs/adr/**',
1196+
tier: 'CONTRACT_REVIEW_TIER',
1197+
why: 'the 2026-10-25 order (marchtian, verbatim 「adr 这种任务都需要 fable extra,重新处理并立skills 卡片」): an ADR binds every later card cut from it until a superseding ADR says otherwise, and lands only by the maintainer\'s hand, so a drafting error is paid by the maintainer and again by each implementation card — 都需要 leaves no one-line exemption, and the same order fixes the effort floor at xhigh (the PM skill states it beside clause ①)',
1198+
// 都需要 closes the one-line-class exit by PATH, the same shape as the
1199+
// skills/** row; the effort floor is skill text, since this table carries a
1200+
// tier and the dispatch-gates freeze admits no new printed line.
1201+
oneLineExit: false,
1202+
},
11921203
];
11931204

11941205
/**

‎scripts/pm/dispatch-gates.mjs‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12115,17 +12115,19 @@ export function tierLines(result) {
1211512115
];
1211612116
}
1211712117
// The one-line-class exit is a card-CONTENT judgment for every mandated
12118-
// surface but one: a glob declared with `oneLineExit: false` (the published
12119-
// catalog, 2026-09-10 ruling) closes it by PATH, and one such hit closes it
12120-
// for the WHOLE card — the exit is taken per card, not per file.
12118+
// surface except those whose glob is declared with `oneLineExit: false` (the
12119+
// published catalog, 2026-09-10 ruling; ADR text, 2026-10-25 order): that
12120+
// closes it by PATH, and one such hit closes it for the WHOLE card — the exit
12121+
// is taken per card, not per file. The reason printed is the row's own `why`,
12122+
// already on the hit lines above, so this line names no ruling of its own.
1212112123
const oneLineBarred = [...new Set(hits.filter((h) => h.oneLineExit === false).map((h) => h.glob))];
1212212124
const oneLineExit =
1212312125
oneLineBarred.length === 0
1212412126
? 'a one-line-class mechanical governed edit drops to opus execution (sonnet floor for pure one-liners at PM' +
1212512127
` discretion, compensated by the skill-face review at ${CONTRACT_REVIEW_TIER}) — judged from the card CONTENT,` +
1212612128
' never from paths'
1212712129
: `the one-line-class mechanical-edit downgrade is ⛔ NOT available for this surface — ${oneLineBarred.map((g) => `'${g}'`).join(', ')}` +
12128-
' carries no one-line exemption (the published catalog, 2026-09-10 ruling), and one such path closes the exit for the whole card';
12130+
' carries no one-line exemption (its row declares `oneLineExit: false`; the reason is on its hit line above), and one such path closes the exit for the whole card';
1212912131
return [
1213012132
`Model tier — MANDATORY: ${tier} (derived from the file surface, not recalled).`,
1213112133
...hits.map((h) => ` - ${h.path} ⇢ '${h.glob}' — ${h.why}`),

‎scripts/pm/dispatch-gates.self-test.mjs‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9999,7 +9999,16 @@ export function selfTest({ tier = 'full' } = {}) {
99999999
t('no per-file entry for the deleted published PM skill survives it, and its old path carries the root mandate only', !MANDATORY_TIER_GLOBS.some((g) => g.glob.includes('objectstack-pm-dispatch')) && fableOf(['skills/objectstack-pm-dispatch/SKILL.md']).hits.every((h) => h.glob === 'skills/**'));
1000010000
t('skills/** does NOT reach the internal .claude/skills tree — a pm-dispatch references file still carries no mandate', fableOf(['.claude/skills/pm-dispatch/references/state-machine.md']).mandatory === false);
1000110001
t('the skills/** entry is declared with its one-line exit switched off, as data', MANDATORY_TIER_GLOBS.some((g) => g.glob === 'skills/**' && g.oneLineExit === false && g.tier === CONTRACT_REVIEW_TIER));
10002-
t('every other mandatory entry keeps the one-line exit open (the flag is an opt-out, absent by default)', MANDATORY_TIER_GLOBS.filter((g) => g.glob !== 'skills/**').every((g) => g.oneLineExit === undefined) && catalogHit.hits.every((h) => h.glob !== 'skills/**' || h.oneLineExit === false));
10002+
// The ADR order (2026-10-25, 「都需要」): ADR text is the second surface whose
10003+
// one-line exit is closed by path; the rest keep the opt-out absent.
10004+
const ONE_LINE_BARRED = ['skills/**', 'docs/adr/**'];
10005+
t('the docs/adr/** entry is declared at the contract-review tier with its one-line exit switched off, as data', MANDATORY_TIER_GLOBS.some((g) => g.glob === 'docs/adr/**' && g.oneLineExit === false && g.tier === CONTRACT_REVIEW_TIER));
10006+
t('every other mandatory entry keeps the one-line exit open (the flag is an opt-out, absent by default)', MANDATORY_TIER_GLOBS.filter((g) => !ONE_LINE_BARRED.includes(g.glob)).every((g) => g.oneLineExit === undefined) && catalogHit.hits.every((h) => h.glob !== 'skills/**' || h.oneLineExit === false));
10007+
const adrHit = fableOf(['docs/adr/0028-metadata-naming-and-namespace-isolation.md']);
10008+
t('an ADR file is mandated, by the docs/adr/** entry alone', adrHit.tier === CONTRACT_REVIEW_TIER && adrHit.hits.length === 1 && adrHit.hits[0].glob === 'docs/adr/**');
10009+
t('docs/adr/** does NOT reach the rest of docs/ — a sibling docs file carries no mandate', fableOf(['docs/NORTH-STAR.md']).mandatory === false && fableOf(['docs/adr-notes.md']).mandatory === false);
10010+
const adrLines = tierLines(adrHit).join('\n');
10011+
t("the ADR rendering refuses the one-line-class exit, naming the glob, and does not borrow the catalog's reason", adrLines.includes("NOT available for this surface — 'docs/adr/**'") && !adrLines.includes('drops to opus execution') && !adrLines.includes('(the published catalog'));
1000310012
const catalogLines = tierLines(catalogHit).join('\n');
1000410013
t('the published-catalog rendering refuses the one-line-class exit, in those words', catalogLines.includes('one-line-class') && catalogLines.includes('NOT available') && catalogLines.includes('no one-line exemption'));
1000510014
t('and does not offer the opus-execution drop the other mandated surfaces get', !catalogLines.includes('drops to opus execution'));

0 commit comments

Comments
 (0)