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
30 changes: 15 additions & 15 deletions .claude/skills/pm-dispatch/SKILL.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -369,14 +369,14 @@ PR 前合一次 main;③ 兄弟卡落地后再合一次;④ 冲突交合并队
> Domain: `domain:<x>`
> File surface: `<预期触碰的目录>` (stop on breach; explain in the report)
> Container & model: `<S 级机械卡 / M / L>`, `mode:subagent | mode:cloud`, `model: sonnet | opus | fable`(档位引当次 `node scripts/pm/dispatch-gates.mjs --tier <paths>` 输出,⛔ 不凭记忆)
> Clause-②: yes | no —— 条款②内容肢的强制申报,恒英文机器判据,恰这两种拼写:`Clause-②: yes` / `Clause-②: no`;判据:本卡改变契约接受/拒绝行为或扩大公开面吗?`yes` 绑定入队闸门(见「入队与落地」)
> Serial constraints cleared: `<点名同文件/同包的前驱 PR 与在飞认领,及分诊点名的任意车道在飞兄弟卡中本卡 pin 断言其行为者;无则 "none">`

末行把「查过串行约束」变成落在评论里的读数(同包在飞单不点名等于没查);兄弟卡必读,因为文件面
不相交挡不住**读耦合**:本卡 pin 断言兄弟卡在改的行为 ⇒ 各自全绿、先合者令后者 CI 无因变红;派
发令点名跨车道兄弟时**应**注明本卡 pin 是否断言其面,是则在用例内预登记翻转触发词(兄弟行为落
地即变红:删除或反转,⛔ 不修绿)。文件面对跨域例外单**必填**、普通单建议(定向在飞检查与 epic
领地判断的唯一输入);分支名必须带 issue 号(否则 `ls-remote | grep issue-<n>` 找不到)。**Assign
与评论是不可分割的一个动作,限流不拆它**:发不出评论就撤 assign 或不开始;配额尽则整对排队,永不留半个。
发令点名跨车道兄弟时**应**注明本卡 pin 是否断言其面,是则在用例内预登记翻转触发词(兄弟行为落地
即变红:删除或反转,⛔ 不修绿)。文件面对跨域例外单**必填**、普通单建议(定向在飞检查与 epic 领
地判断的唯一输入);分支名必须带 issue 号。**限流不拆原子对**:发不出评论就撤 assign 或不开始;配额尽则整对排队,永不留半个。
3. **竞态复读**:自己的认领评论上墙后重读全线程 —— assignment 幂等,两个 agent 都会「成功」,**
认领评论时间戳是唯一仲裁**;更早的评论带不同 session ID/分支 ⇒ 你输了,回「already claimed
— yielding」另选。**让行是交接不是退场**:连同让行评论交出已诊断的一切与 PM 侧已取的板面读
Expand All@@ -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 自查正文完整性(截断
Expand DownExpand Up@@ -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 席代行。**降档保险丝**:
子轮开场自检当前模型,非契约复审档位 ⇒ 该子轮整体跳过、标签原样留置 —— 卡在队列外等待是安全态;契约复审 ⛔ 不适用额度耗尽豁免降档(豁免的对象是派发;复审的存在意义就是补偿一次低于地板的派发)。
Expand DownExpand Up@@ -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` | 文件面 → 该跑的门禁族(派发令取数) |
Expand Down
30 changes: 20 additions & 10 deletions scripts/pm/ensure-pm-labels.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,14 +18,21 @@
#
# 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

for R in objectstack-ai/objectstack objectstack-ai/objectui objectstack-ai/cloud; do
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):
Expand All@@ -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).
Expand DownExpand Up@@ -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

Expand Down
Loading