diff --git a/.claude/skills/pm-dispatch/SKILL.md b/.claude/skills/pm-dispatch/SKILL.md index 5f89db0ecb..a1b096367f 100644 --- a/.claude/skills/pm-dispatch/SKILL.md +++ b/.claude/skills/pm-dispatch/SKILL.md @@ -369,14 +369,14 @@ PR 前合一次 main;③ 兄弟卡落地后再合一次;④ 冲突交合并队 > Domain: `domain:` > File surface: `<预期触碰的目录>` (stop on breach; explain in the report) > Container & model: ``, `mode:subagent | mode:cloud`, `model: sonnet | opus | fable`(档位引当次 `node scripts/pm/dispatch-gates.mjs --tier ` 输出,⛔ 不凭记忆) + > Clause-②: yes | no —— 条款②内容肢的强制申报,恒英文机器判据,恰这两种拼写:`Clause-②: yes` / `Clause-②: no`;判据:本卡改变契约接受/拒绝行为或扩大公开面吗?`yes` 绑定入队闸门(见「入队与落地」) > Serial constraints cleared: `<点名同文件/同包的前驱 PR 与在飞认领,及分诊点名的任意车道在飞兄弟卡中本卡 pin 断言其行为者;无则 "none">` 末行把「查过串行约束」变成落在评论里的读数(同包在飞单不点名等于没查);兄弟卡必读,因为文件面 不相交挡不住**读耦合**:本卡 pin 断言兄弟卡在改的行为 ⇒ 各自全绿、先合者令后者 CI 无因变红;派 -发令点名跨车道兄弟时**应**注明本卡 pin 是否断言其面,是则在用例内预登记翻转触发词(兄弟行为落 -地即变红:删除或反转,⛔ 不修绿)。文件面对跨域例外单**必填**、普通单建议(定向在飞检查与 epic -领地判断的唯一输入);分支名必须带 issue 号(否则 `ls-remote | grep issue-` 找不到)。**Assign -与评论是不可分割的一个动作,限流不拆它**:发不出评论就撤 assign 或不开始;配额尽则整对排队,永不留半个。 +发令点名跨车道兄弟时**应**注明本卡 pin 是否断言其面,是则在用例内预登记翻转触发词(兄弟行为落地 +即变红:删除或反转,⛔ 不修绿)。文件面对跨域例外单**必填**、普通单建议(定向在飞检查与 epic 领 +地判断的唯一输入);分支名必须带 issue 号。**限流不拆原子对**:发不出评论就撤 assign 或不开始;配额尽则整对排队,永不留半个。 3. **竞态复读**:自己的认领评论上墙后重读全线程 —— assignment 幂等,两个 agent 都会「成功」,** 认领评论时间戳是唯一仲裁**;更早的评论带不同 session ID/分支 ⇒ 你输了,回「already claimed — yielding」另选。**让行是交接不是退场**:连同让行评论交出已诊断的一切与 PM 侧已取的板面读 @@ -397,13 +397,12 @@ dev 侧推分支要早 —— 远程分支是在飞工作最硬的证据。**死 默认)。**⛔ 强制条款两条**:① 凡改 `.claude/skills/pm-dispatch/**` 的卡一律 `model: "claude-fable-5"`;② 凡**改变契约接受/拒绝行为或扩大公开面**的卡(`domain:spec` 语义面; 判据即分诊代裁的机械边界测试与 spec 席内分派判据,⛔ 不另抄第二份)一律 `claude-fable-5`(维护者 -2026-08-12 裁定,原话:「同意,就按语义面收窄,立卡并通知 spec 席」)—— 契约错毒化一切下游,全仓最贵(条款②嫌疑面 `--tier` 有派发时提示;无论派什么档,入队前按 diff 过条款②闸门,见「入队与落地」) -。两条唯一降档出口:**额度耗尽豁免**(维护者 2026-08-13 原话:「fable 如果用完了,可以用 opus」):仅 -当 fable 实测不可用(额度耗尽/限流;墙杀在中途,重派时同样可降)才落 `opus`,⛔ 不再往下,档位与理由记 -入认领评论「Container & model」行。明确不变(合并后按席内分派面适用):spec 文本面卡照旧 -sonnet/opus;机器面默认 opus,门禁语义设计时升 fable;下游适配卡默认 opus 拿不准升档照旧。**档位逐次派发显式传参,永不省略** -(解析顺序与 pin 语义以 os-dev 定义 frontmatter 注释为权威;被允许名单挡下时回退**继承的**模型, -正是要防的静默继承),连同尺寸写进同一行;分诊 suggestion 行是输入不是决定,不采纳给理由。 +2026-08-12 裁定,原话:「同意,就按语义面收窄,立卡并通知 spec 席」)—— 契约错毒化一切下游,全仓最贵(条款②闸门见「入队与落地」)。两条唯一降档出口:**额度耗尽豁免**(维护者 2026-08-13 +原话:「fable 如果用完了,可以用 opus」):仅当 fable 实测不可用(限流/墙杀中途,重派同样可降) +才落 `opus`,⛔ 不再往下,档位与理由记入认领评论「Container & model」行。明确不变(合并后按席内 +分派面适用):spec 文本面照旧 sonnet/opus;机器面与下游适配默认 opus,门禁语义设计升 fable,拿 +不准升档照旧。**档位逐次派发显式传参,永不省略**(解析顺序、pin 语义与允许名单回退陷阱以 os-dev +定义 frontmatter 注释为权威),连同尺寸写进同一行;分诊 suggestion 行是输入不是决定,不采纳给理由。 **派发词原则(构造细则与条款原文见 `references/dispatch-runbook.md`)。** ⛔ 默认不整段粘 贴 issue 正文 —— 让 dev 自己读 GitHub 全文与全部评论,并**必须**要求 dev 自查正文完整性(截断 @@ -505,9 +504,10 @@ not-reachable 是设计非故障(维护者 2026-08-11 裁定)⛔ 不复测;**接 **入队与落地(细则见 `references/landing-operations.md`,落地窗口查阅)。** 原则: -- **条款②入队闸门(维护者 2026-08-16 批准)**:翻 ready / 入队前先取 PR **实际 diff** —— diff 是事实,卡片语义是预测;派发时 `--tier` 的契约嫌疑行是提示不是裁定(尽力从卡片内容判档, - 无论派什么档,入队前按 diff 过闸门)。diff 触及契约面(`packages/spec/src/**`,含 error-code-ledger 与 `*.zod.ts` 契约 schema)且派发档位低于**契约复审档位**(以 dispatch-gates 常量 - `CONTRACT_REVIEW_TIER` 为准 —— 档位单源,⛔ 本文与标签不写模型名,模型升级只改一行一个文件)⇒ ⛔ 禁止入队。派发席职责止于:卡上记一行认定、挂 `needs:contract-review`(标签命名审的对象,恒英文)、停手;⛔ 禁止自查放行。 +- **条款②入队闸门(维护者 2026-08-16 批准;声明肢 2026-08-17 裁定)**:翻 ready / 入队前先取 PR **实际 diff** —— diff 是事实,卡片语义是预测;`--tier` 嫌疑行是提示不是裁定。**双肢命中 + 任一且派发档位低于契约复审档位 ⇒ ⛔ 禁止入队**:路径肢 = diff 触及契约面(`packages/spec/src/**`,含 error-code-ledger 与 `*.zod.ts` 契约 schema);声明肢 = 认领评论声明 + `Clause-②: yes`,与路径无关(错误的 `no` 是记录在案、可审计的假申报,不再是静默缺口)。⛔ 永不把路径触发读作条款②的完整覆盖 —— 它只盖路径肢;不触契约文件而改变接受/拒绝行为或扩大公开面的**内容 + 肢**由声明行承载。档位以 dispatch-gates 常量 `CONTRACT_REVIEW_TIER` 为准(档位单源,⛔ 本文与标签不写模型名,模型升级只改一行一个文件)。派发席职责止于:卡上记一行认定、挂 `needs:contract-review`(标签命名审的对象,恒英文)、停手;⛔ 禁止自查放行。 - **`needs:contract-review` 复审链**:复审资格双条件,同时满足 —— ① 跑在契约复审档位;② 非该卡派发席(犯规席在结构上无资格补救自己的犯规)。归属:常设 = 分诊席(Routine 模型由维护者在 Routines UI 钉在契约复审档位),分诊轮新增子轮清该标签 —— 只审契约增量 diff、结论一行写在卡上、清标签后卡方可入队;每小时一轮即天然攒批;过渡期(分诊 Routine 未建成前)由 skills 席代行。**降档保险丝**: 子轮开场自检当前模型,非契约复审档位 ⇒ 该子轮整体跳过、标签原样留置 —— 卡在队列外等待是安全态;契约复审 ⛔ 不适用额度耗尽豁免降档(豁免的对象是派发;复审的存在意义就是补偿一次低于地板的派发)。 @@ -672,7 +672,7 @@ pull(今天谁撞上;零拉动默认 defer/ remove);③ AI-agent error-resistanc | 守卫 | 一句话 | |:--|:--| -| `scripts/pm/check-skill-line-ratchet.mjs` | 本文件行数只降不升(`pnpm check:pm-skill-ratchet`);抬上限需维护者裁决引用在 PR 正文 | +| `scripts/pm/check-skill-line-ratchet.mjs` | 本文件行数只降不升(`pnpm check:pm-skill-ratchet`);抬上限需维护者裁决引用在 PR 正文;⛔ re-wrap(折行合并)不得用作筹行 —— 棘轮治理的是内容体量,行数只是机读代理,新增以删减付账;密度优化仅随净减内容的 PR 顺带(维护者 2026-08-17 授权席裁) | | `scripts/pm/check-skill-id-lint.mjs` | 本技能与 os-dev 定义的操作文本 ⛔ 不引用 issue 编号(`pnpm check:pm-skill-id-lint`)—— 经验必须自含 | | `scripts/pm/check-half-states.mjs` | label/assignee/PR 半状态的 report-only 巡查(含已复核就绪却无人落地的孤儿 PR 检测) | | `scripts/pm/dispatch-gates.mjs` | 文件面 → 该跑的门禁族(派发令取数) | diff --git a/scripts/pm/ensure-pm-labels.sh b/scripts/pm/ensure-pm-labels.sh index 100eec022c..01ff6cfc90 100644 --- a/scripts/pm/ensure-pm-labels.sh +++ b/scripts/pm/ensure-pm-labels.sh @@ -18,6 +18,13 @@ # # Requires the `gh` CLI. Where only the GitHub MCP tools are available, mirror # these creations through them — the protocol is identical. +# +# ⚠️ GitHub hard-caps label descriptions at 100 characters: `gh label create` +# / `gh label edit` get HTTP 422 above that, and the `|| true` (needed for +# idempotent reruns) swallows the failure — an over-long -d line means the +# label is NEVER created on a repo where it doesn't exist yet, silently, and a +# rerun can never repair it. Keep every -d in this file ≤100 characters +# (measured: a ~145-char description 422'd in live use). set -uo pipefail @@ -25,7 +32,7 @@ for R in objectstack-ai/objectstack objectstack-ai/objectui objectstack-ai/cloud gh label create pm:queue -R "$R" -c 0e8a16 -d "Ready for the PM dispatch loop" 2>/dev/null || true gh label create pm:dispatched -R "$R" -c 1d76db -d "Dispatched to a dev agent by /pm-dispatch" 2>/dev/null || true gh label create needs-user-decision -R "$R" -c d93f0b -d "Blocked on a maintainer decision — do not dispatch" 2>/dev/null || true - gh label create pm:on-hold -R "$R" -c e4e669 -d "Decision made, deliberately deferred — do not dispatch, do not nag; restart condition in the hold comment" 2>/dev/null || true + gh label create pm:on-hold -R "$R" -c e4e669 -d "Decision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment" 2>/dev/null || true gh label create pm:blocked -R "$R" -c b60205 -d "Blocked by another issue/PR — body carries Blocked-by: #N" 2>/dev/null || true # pm:blocking is a derived CACHE, never hand state (maintainer opinion # 2026-08-13, superseding the earlier derived-only-no-stored-label ruling): @@ -36,17 +43,20 @@ for R in objectstack-ai/objectstack objectstack-ai/objectui objectstack-ai/cloud # corrects it against the index. gh label create pm:blocking -R "$R" -c 8250df -d "Derived cache from the Blocked-by reverse index: open card with open dependents — never hand-set" 2>/dev/null || true gh label create finding -R "$R" -c c2e0c6 -d "Recorded observation — held, not dispatchable until the findings triage round grades it" 2>/dev/null || true - gh label create pm:epic -R "$R" -c 5319e7 -d "Parent delegated to a dedicated epic PM (session + territory in the parent's own body) — other PMs never dispatch into its subtree" 2>/dev/null || true + gh label create pm:epic -R "$R" -c 5319e7 -d "Parent delegated to a dedicated epic PM — other PMs never dispatch into its subtree" 2>/dev/null || true done # needs:contract-review — the clause-② enqueue gate's re-review chain (SKILL.md -# 入队与落地): a PR whose ACTUAL diff touches the contract surface but was -# dispatched below the contract-review tier waits outside the queue under this -# label until the review sub-round clears it. Named consumers: the enqueue gate -# and the triage sub-round's label query. Main repo only — the contract surface -# (packages/spec) lives here. The label names WHAT is reviewed, never a model; -# the tier's single source is CONTRACT_REVIEW_TIER in scripts/pm/dispatch-gates.mjs. -gh label create needs:contract-review -R objectstack-ai/objectstack -c d93f0b -d "Clause-② enqueue gate: contract-surface diff dispatched below the contract-review tier — blocked from enqueue until the review sub-round clears it" 2>/dev/null || true +# 入队与落地): a PR whose ACTUAL diff touches the contract surface — or whose +# card's claim comment declares `Clause-②: yes` (the content limb, judged from +# the card, path-independent) — but was dispatched below the contract-review +# tier waits outside the queue under this label until the review sub-round +# clears it. Named consumers: the enqueue gate and the triage sub-round's label +# query. Main repo only — the contract surface (packages/spec) lives here. The +# label names WHAT is reviewed, never a model; the tier's single source is +# CONTRACT_REVIEW_TIER in scripts/pm/dispatch-gates.mjs. The -d below is kept +# ≤100 chars (the hard cap — see the header) and matches the live label object. +gh label create needs:contract-review -R objectstack-ai/objectstack -c d93f0b -d "Clause-② enqueue gate: dispatched below contract-review tier — blocked until the review clears it" 2>/dev/null || true # Routing labels exist only on the main backlog repo, and mark SEAM cards only # (file-at-destination ruling: pure sibling-repo fixes live in the target repo). @@ -75,7 +85,7 @@ done # and their current colour and description are left exactly as they are. for V in v17; do for R in objectstack-ai/objectstack objectstack-ai/objectui objectstack-ai/cloud; do - gh label create "target:$V" -R "$R" -c ededed -d "Release blocker for $V — stays on the release board until fixed, dropped as no longer valid, or explicitly accepted for GA" 2>/dev/null || true + gh label create "target:$V" -R "$R" -c ededed -d "Release blocker for $V — on the board until fixed, dropped as no longer valid, or accepted for GA" 2>/dev/null || true done done