Skip to content

Commit 3af6022

Browse files
pm-dispatch(p0): a lane's p0 is dispatched in the same round past every cap and lower-priority work pauses; finder stops the bleed, then escalation, then triage stop-bleed; main red and deterministic queue red are p0 at first touch; full-run card filed p0 (#27266)
Fixes #27047 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:本车道的 p0 卡当轮立即派发,不再受 `batch`、深度等待(`LOCK_DEPTH_HOLD`)与任何并发上限约束;没有槽位就暂停最低优先级的在飞开发腾位(分支与 PR 原样保留、卡上记 `Paused-for:` 行、p0 落地即复活)。主干全量红与确定性队列红首触即 p0,四小时 full-run 生成的红卡也改为 p0 立卡。发现红的席位先止血;一轮无人认领即轮报点名;再一轮分诊只代派止血(每轮一张,复核不归分诊)。 - 为什么改:维护者原话「main 红为什么不是 p0?」「skills 规则应该要求对应车道 p0 的卡立刻派发」「不受并发限制,或者宁可其他开发暂停」「你需要立卡更新技能」;第 6 项裁决记录「同意,补到 #27047」「同意乙,一起写进 #27047」。 - 风险与回滚:只改四份协议文本与一个标签常量,不碰产品代码;回滚 = revert 本 PR。唯一放宽的是分诊红线的单一例外(无主 p0 的止血代派,每轮一张、复核不归分诊),其余红线与限制原样。 - 先看(按风险):① `SKILL.md` 第 35 行(红线新增的唯一例外)与第 119 行(`priority:p0` 行);② `references/landing-operations.md` 第 76–84 行(约定①及其非机械分支、约定③);③ `references/execution-duties.md` 第 25–27 行(暂停机制)。 - 两分钟验证:`pnpm check:pm-skill-ratchet && pnpm check:pm-skill-id-lint && node scripts/ci/scheduled-full-run-card.mjs --self-test` —— 预期全绿;ratchet 打印四份文件 319 / 188 / 101 / 102 行,各在上限、headroom 0。 - 需要你做的:无,席位达档契约复核后落地(Tier S,`.claude/**`)。 ## What changes Item by item against the card's Wanted 1–6 (the ruled text; nothing beyond it, no new gate). Line numbers below are post-change unless marked `origin/main`. 1. **p0 dispatches at once, in its own lane.** `references/execution-duties.md` 〈候选与批次〉 L51 (new): on every round and every wake the lane seat's first read is its open `priority:p0`; an unclaimed one is claimed and dispatched in that same round, never the next. 2. **No concurrency cap holds a p0.** `SKILL.md` `priority:p0` row (L119) rewritten in place: same-round dispatch past `batch`, the depth wait (`LOCK_DEPTH_HOLD`) and every concurrency cap; the row's ⛔ loses 深度等待 and keeps 同文件串行与认领协议. `execution-duties.md` L25: p0 crosses the depth wait too (the old text forbade exactly that). 3. **When there is no room, other development pauses.** `execution-duties.md` L26–27 (new): no slot, or a lower-priority in-flight card holding the same files ⇒ pause the lowest-priority in-flight dev that frees the room — stop its session, hold the PR draft, claim untouched; the card gets a `Paused-for:` line naming the p0; nothing discarded, reverted or force-pushed; when the p0 lands the dev is revived on the same claim and branch (the 〈派发〉 revive rule: SendMessage first, takeover protocol only if unreachable). The SKILL.md row says this in five characters (无槽即暂停…腾位) and points at execution-duties for the mechanics. The card's ⚠️ reading ("pause" = stop the session and hold the draft) is what the text says. 4. **An unclaimed p0 escalates.** `references/landing-operations.md` 约定③ (L82): one triage round (~60 min) unclaimed ⇒ triage and the director name the card and the owning lane in that round's report. 5. **Main red and merge-queue red are p0 at first touch.** `references/triage-duties.md` L30 (new): a full red on `main` and a deterministic merge-queue red are graded p0 at first touch — the anchor, the root-cause card and the generated full-run card; a flake anchor becomes p0 only once read as deterministic (the card's ⚠️: a `queue-flake-anchor` is a name, not a diagnosis). Generator: `scripts/ci/scheduled-full-run.mjs` `cardIdentity().labels` `priority:p1` → `priority:p0`. That constant is the live filing path (`createIssue({ labels: identity.labels })`, called through `scheduled-full-run-card.mjs` `fileCardStep`, which `.github/workflows/scheduled-full-run-card.yml` runs). Its pin, `scheduled-full-run-card.mjs` L217 (the self-test's expected `issues.create` params), follows; so does the self-test board fixture `CARD_LABELS` (`scheduled-full-run.mjs` L1699), which `lint.yml` executes through `--self-test` and whose battery asserts only that a refresh never touches the grade labels. 6. **Finder stops the bleed → escalation → triage stop-bleed.** `landing-operations.md`: 约定① (L76) gains its non-mechanical branch (L80, 6a — the finder names the owning lane on the anchor and rings its mailbox with `Request: main-red`; the root-cause card stays with that lane; the mechanical branches — revert, or a verified-root-cause fix — were already L78–79); 约定③ (L82–84, 6b–c): still unclaimed by the round after the escalation ⇒ triage posts the standard claim line and dispatches ONE `os-dev` for the stop-bleed only; one per round, triage does not become a development lane; the stop-bleed PR's review is the lane seat's or the director's, never triage's; Tier H still waits on the governed approver; a lane seat arriving takes the card back through the claim line (the one-writer protocol). `triage-duties.md` L31 (new) carries the triage-side duty and points at 约定③. `SKILL.md` 红线 L35 (new): the line 「⛔ 分诊席永不认领、派发或写码」 gains its one exception (an unclaimed p0 anchor: round report after one round, triage stop-bleed only after another; one per round; review not triage's) — without it SKILL.md 〈优先级〉 (红线 outrank 细则, and two conflicting rules resolve to the stricter one) would make item 6c dead text. This is the card's own 「amends the earlier 『你只处理分诊』 for this one case only」, written where that rule lives. Acceptance line of the card: a reader of `SKILL.md` alone now learns that a p0 is dispatched in the same round, past `batch`, the depth wait and every concurrency cap, and that lower-priority dev work pauses to make room (L119), and that triage's only claim/dispatch is the unclaimed-p0 stop-bleed (L35). ## Line budget — every edited line, bytes, and the home of every deleted line Ceilings (`scripts/pm/check-skill-line-ratchet.mjs` `CEILINGS`): SKILL.md 319, execution-duties 188, landing-operations 101, triage-duties 102 — every one at headroom 0 on `origin/main` 9ff8154. The order read triage-duties' ceiling as 103 with one line of headroom; the ledger says it was lowered 103 → 102 when the whole-card cloud route merged, so that file paid for both of its new lines. Every new line is paid by deleting a line whose content has a home elsewhere (named per line); no re-wrap is used as currency; every prose line is ≤120 bytes (`wc -c` without the newline); the one edited table row is metered by SKILL.md's widest-table-row pin (341 B, still L160), which is the rule for table rows (`classifyLine` exempts them from the 120-byte line rule). Line numbers in this section are `origin/main`'s. ### `.claude/skills/pm-dispatch/SKILL.md` — 319 → 319 - after L34 **inserted** (111 B): `- 唯一例外:无主 p0 锚卡一轮即轮报、再一轮分诊只代派止血:每轮一张,复核非分诊。` - L118 **rewritten in place** (124 B → 239 B; a table row — the file's widest row stays L160 at 341 B = the pin). Cell before: `插队:可超 \`batch\`、破轮次立即派发;⛔ 不豁免同文件串行、深度等待与认领协议`. Cell after: `插队:同轮立即派发,越过 \`batch\`、深度等待(\`LOCK_DEPTH_HOLD\`)与一切并发上限;无槽即暂停低级在飞 dev 腾位(机制 \`references/execution-duties.md\`);⛔ 不豁免同文件串行与认领协议`. - L126 **deleted** (107 B): `- hold 放行须双查:只放最近一次转换评论的条件,其后卡上有更新的 merged PR 即拒。` — home: `references/state-machine.md` L19 (the same rule, verbatim: 「放行双查皆机械:只放最近一次 hold/blocked 转换评论的条件;其后卡上有新 merged PR 即拒」); SKILL.md L125 already points there for hold/blocked detail. ### `references/execution-duties.md` — 188 → 188 - L25 **rewritten in place** (118 B → 117 B): tail `\`priority:p0\` 可超 \`batch\`,⛔ 不越过深度等待。` → `p0 越过 \`batch\`、深度等待与一切并发上限。` (head unchanged). - after L25 **inserted** (117 B): `- p0 无槽或低级在飞同文件挡住 ⇒ 暂停腾位的最低级 dev:停会话、PR 留 draft、认领不动。` - after L25 **inserted** (119 B): `- 卡记 \`Paused-for: #P0\`,⛔ 不丢弃、不回退、不 force-push;p0 落地即复活该 dev,同认领同分支。` — the file spells the placeholder the way the corpus spells `#` placeholders (angle brackets around p0); it is rendered here without them so this body survives the platform's sanitizer. - after L48 **inserted** (112 B): `- 每轮每次唤醒首读本车道开着的 \`priority:p0\`,无人认领即同轮认领派发,⛔ 不等下轮。` - L69 **deleted** (113 B): `- \`Metadata-protocol:\` 四值与定级表住 AGENTS.md「Post-Task Checklist」③:定级只随元数据协议。` — home: SKILL.md 〈模板与表〉 `Metadata-protocol:` template line (L313, "the level table is AGENTS.md's Post-Task Checklist step 3") and AGENTS.md Post-Task Checklist ③ ("The level follows the kind alone"). - L74 **deleted** (119 B): `- 档位引 \`dispatch-gates --tier --repo 仓\`⛔ 不凭记忆;天花板拼常量名并同行引据,⛔ 不抄模型 id` — home: SKILL.md 〈模板与表〉 `Container & model:` template line (L311: 引当次 `--tier` 输出;天花板拼 `CONTRACT_REVIEW_TIER`,同行引其 MANDATORY 路径行或 reason;⛔ 不抄模型 id). - L138 **deleted** (116 B): `- 派发令写明 dev ⛔ 不另留认领:PM 那条即身份;核对最新一条点名本分支,不符停手回报。` — home: AGENTS.md 〈Claim the issue BEFORE you write any code〉 ("A dispatched executor … verifies that the newest `Claim:` names its branch (on a mismatch it stops and reports), posts no second claim") and `.claude/agents/os-dev.md` 规则 2 (身份位是认领评论里的分支); the line also contradicted L125 of the same file (standard clauses already in os-dev.md are not copied into dispatch orders). ### `references/landing-operations.md` — 101 → 101 - L7 **deleted** (108 B): `- fork PR = 提案,席位永不放行;采纳 diff 内部落地,见 \`references/external-contributions.md\`。` — home: AGENTS.md Prime Directive 14 ("A fork PR … is a proposal, never a delivery … the owning seat adopts the diff onto an internal branch and lands that") and `references/external-contributions.md` (its title and ③). - L8 **deleted** (105 B): `- \`pm:mailbox\` 信箱 PR 不欠 CI、永不入队;落地台账与孤儿 PR 查询一律排除该标签。` — home: `references/project-forms.md` 〈信箱 PR〉 L38 (the same rule, verbatim). - L51 **deleted** (119 B): `- 直敲 \`node scripts/pm/fleet-write/dispatch.mjs --repo OWNER/NAME --actions-file F\`,⛔ 套壳:许可逐字匹配。` — home: `references/rest-channel.md` L49 (the spelling of the relay call) and L13 (prefixes and wrappers fall to the classifier); L50 of this file still sends the reader to `rest-channel.md` for the spelling. - L74 **deleted** (111 B): `- 会话席窗口内 PR 须在挂 \`subscribe_pr_activity\`,缺即补;⛔ 不早于报告,Routine 座位轮询。` — home: `references/execution-duties.md` 〈收集〉 L156 (subscribe the moment the report names the PR; relay-opened PRs are not auto-attached, so the seat adds it) and L157–158 (Routine seats poll on their patrol interval). - after L83 **inserted** (109 B): `- 止血非机械 ⇒ 锚卡点名归属车道、敲其信箱 \`Request: main-red\`;根因卡恒归该车道。` - after L84 **inserted** (113 B): `- 约定③:锚卡一轮分诊(~60 分钟)无人认领 ⇒ 分诊与总监当轮轮报点名卡与归属车道。` - after L84 **inserted** (119 B): `- 再一轮无人认领 ⇒ 分诊贴标准认领行、派一个 os-dev 只做止血;每轮一张,分诊不成车道。` - after L84 **inserted** (113 B): `- 止血 PR 复核归车道席或总监,⛔ 非分诊;Tier H 仍等审批人;车道席到场经认领行交回。` ### `references/triage-duties.md` — 102 → 102 - L5 **deleted** (119 B): `- 发版板、pin 链归总监席摘要,细则见 director.md;活性复查同归,细则留本文;⛔ 无代裁通道。` — home: `references/lanes/director.md` 职责四 L71–75 (daily summary, release board, and L74 names the liveness re-check as part of it, pointing back here for the detail) and L80–81 (the cross-repo platform-version bump cards, i.e. the pin chain); SKILL.md 红线 L30 (⛔ 无代裁通道) and the `director` row of 〈入口与角色〉 (三职与章程见 director.md). - after L30 **inserted** (119 B): `- 主干全量红与确定性队列红首触定 p0(锚卡、根因卡、full-run 卡);flake 锚卡判确定性才升。` - after L30 **inserted** (114 B): `- 无主 p0 锚卡一轮即轮报,再一轮本席贴认领行只代派止血(\`landing-operations.md\` 约定③)。` - L78 **deleted** (100 B): `- retriage 的异议判据认内容(有评论写明所求)⛔ 不认形状(有一条异议评论)。` — home: `references/state-machine.md` L39–40 (`pm:retriage`: the objection comment carries evidence plus the answer sought and a bare label is illegal; triage answers what is sought before removing the label). ### `scripts/ci/` — the label constant and its pins (no line count change) - `scheduled-full-run.mjs` L366 `labels: ['bug', 'domain:devx', 'priority:p1']` → `'priority:p0'` inside `cardIdentity()` — the live filing path (`createIssue({ … labels: identity.labels })` at L488; `fileCardStep` in `scheduled-full-run-card.mjs` calls it; `scheduled-full-run-card.yml` runs that step). - `scheduled-full-run.mjs` L1699 `const CARD_LABELS = ['bug', 'priority:p1', 'domain:devx']` → `'priority:p0'` — the self-test's board fixture for the "card refresh: a stale pm:blocked" battery, executed by `--self-test` (lint.yml), not by the workflow. Its assertions are grade-agnostic (a refresh never writes the grade labels); the fixture now models a card as the generator files it. - `scheduled-full-run-card.mjs` L217 `labels: ['bug', 'domain:devx', 'priority:p1']` → `'priority:p0'` — the expected `issues.create` params in that module's self-test, i.e. the pin of L366 (it went red with L366 alone, green with both). ## Tests Derived union (`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, no paths, change set from the merge base 9ff8154 of `origin/main` and HEAD 7b401bf): 43 commands, each run in the foreground on the final head with its exit code captured before any pipe (`cmd > log 2>&1; ec=$?`); 1 non-zero. Reconciliation: `dispatch-gates --ran` exit 0 — 43 derived families accounted for: 42 run, 1 NOT-MEASURED (derived from the recorded exit 3), 0 UNRUN. The NOT-MEASURED one is `pnpm --filter @objectstack/lint run check:doc-formula-expressions`: PREREQUISITE NOT MET — `@objectstack/formula` and `@objectstack/lint` are not built in this worktree (its fix line `pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint` would build spec, sdui-parser, formula and lint, a closure this docs-and-constant diff does not owe); nothing was measured by it locally, and CI's `Lint & Repo Gates` runs it on a built tree. <details><summary>Every command, exit code and its own verdict line</summary> - `node scripts/check-closing-keyword-parity.mjs` — exit 0 — • ui/packages/app-shell/CHANGELOG.md -- 2962537 bytes exceeds the sweep's 2097152-byte cutoff for UNREGISTERED files - `node scripts/check-closing-keyword-parity.mjs --self-test` — exit 0 — ✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to red. - `node scripts/check-comment-mask-corpus.mjs` — exit 0 — ✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 18783 files, 0 disagree, 0 unparseable, 156.6s (comparator self-test: 26 cases pass). - `node scripts/check-declaration-mirrors.mjs` — exit 0 — (Parameter and return TYPES are not asserted — see this file's header.) - `node scripts/check-declaration-mirrors.mjs --self-test` — exit 0 — All 29 self-test cases passed. - `node scripts/check-self-test-wired.mjs` — exit 0 — scope: 313 file(s) under scripts/, 259 carrying '--self-test' in code (comments masked, 0 of them package-local gate(s) CI names by path); 242 of those are run by 30 workflow(s) and 3 composite action(s); 239 have thei - `node scripts/check-self-test-wired.mjs --self-test` — exit 0 — check-self-test-wired --self-test: 3 live ledger row(s) verified, plus the comment mask, the right boundary, alias resolution, the declared-alias population and both audit directions — 12 declared batteries, 96 cases reg - `node scripts/check-self-test-workflow-commands.mjs` — exit 0 — scope: 242 script(s) CI runs ship a '--self-test' (0 of them package-local gate(s) CI names by path, present because this population is the one check-self-test-wired.mjs exports rather than a second walk taken here); 3 - `node scripts/check-self-test-workflow-commands.mjs --self-test` — exit 0 — check-self-test-workflow-commands --self-test: both measured parse rules pinned (legacy form anywhere in a line, current form only at line start), the innocent-output and Perl-namespace cases, the comment mask in both di - `node scripts/check-whole-set-label-write.mjs` — exit 0 — ⚠️ The EXECUTABLE population is empty: all 10 mention(s) are comments, in - `node scripts/check-whole-set-label-write.mjs --self-test` — exit 0 — ✓ check-whole-set-label-write --self-test: all cases pass (24 fixture trees + 5 refusals + 1 allowlist hatch) - `node scripts/ci/plan-test-core-shards.mjs --self-test` — exit 0 — ✓ plan-test-core-shards self-test: 23 case(s) across 5 batteries. - `node scripts/ci/scheduled-full-run-card.mjs --self-test` — exit 0 — ✓ scheduled-full-run-card --self-test: 7 cases over the card step's API plumbing. - `node scripts/ci/scheduled-full-run.mjs --self-test` — exit 0 — scheduled-full-run: self-test OK - `node scripts/pm/bare-root-worklist.mjs --self-test` — exit 0 — OK self-test: 75 live row(s), 52 unreachable as spelled, 41 recorded verdict(s) — none stale, none missing, none contradicted (12 row(s) whose gate carries the dispatch-gates wide-population declaration, the deliberate- - `node scripts/pm/check-governed-queue-guard.mjs --self-test` — exit 0 — ✓ check-governed-queue-guard self-test: 401 cases pass (register-driven verdicts, the queue/PR event split, latest-decisive approval reduction, the 2026-09-04 authorized-approval-on-ANY-commit predicate on the queue leg - `node scripts/pm/check-harness-current.mjs --self-test` — exit 0 — check-harness-current --self-test: all 26 cases passed. - `pnpm --filter @objectstack/lint run check:doc-formula-expressions` — exit 3 —  ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  @objectstack/lint@18.2.0 check:doc-formula-expressions: 'node scripts/check-doc-formula-expressions.mjs --self-test && node scripts/check-doc-formula-expressions.mjs' - `pnpm check:agent-test-spelling` — exit 0 — turbo dogfood shard; those are the cleared ones. Non-vacuity is carried by --self-test, - `pnpm check:bash32-floor` — exit 0 — ✓ check-bash32-floor: 31 tracked shell file(s) under scripts/**, .claude/hooks/**, .githooks/** name no bash 4+ construct outside a comment, a guarded ${VAR:-} read, or a non-command position. - `pnpm check:ci-filter-parity` — exit 0 — ✓ check-ci-filter-parity --self-test: 120 assertions — both coverage limbs and the extglob bound, the same-root-different-file case observed failing and then covered by naming the file, a glob covered by 'core', one cove - `pnpm check:cli-command-ids` — exit 0 — ✓ check-cli-command-ids: 73 module(s) under packages/cli/src/commands examined, all of them default-export a class whose inheritance chain reaches oclif's 'Command' (0 declared exemption(s)). - `pnpm check:cross-package-test-inputs` — exit 0 — All 261 self-test cases passed. - `pnpm check:doc-authoring` — exit 0 — ✓ doc authoring guard: sibling-package prose ids hold the baseline — 0 pinned site(s) across 0 file(s), 112487 string(s) read in 1564 parsed source(s) (seen floor 40000 strings / 600 sources), no growth, no burn-down unr - `pnpm check:driver-memory-census` — exit 0 — OK self-test: classifies every module-binding form (import / export-from / dynamic import / import-type / require / vi.mock) as a DECLARATION and every other string occurrence as a MENTION (a bundler externals entry, a - `pnpm check:entry-guard` — exit 0 — ✓ check:entry-guard: 284 scripts/ file(s) — every entry guard goes through invoked-as.mjs; 233 export bindings, 233 of them inert on import (0 known-unsafe, ⛔ SHRINK-ONLY). - `pnpm check:gitlink-declared` — exit 0 — ✓ check-gitlink-declared --self-test: 36 assertions over throwaway git repos (real scan() path) - `pnpm check:nul-bytes` — exit 0 — ✓ check-nul-bytes --self-test: 75 assertions over a temp git repo (real scan() path) - `pnpm check:parse-guard` — exit 0 — ✓ check:parse-guard: 283 scripts/ file(s) — every TypeScript parse goes through ts-parse.mjs. - `pnpm check:pm-dispatch-gates` — exit 0 — ✓ dispatch-gates self-test (fast tier): 1935 cases pass; 7 slow section(s) deferred. - `pnpm check:pm-expected-skips` — exit 0 — ✓ check-expected-skips self-test: 206 cases pass (the exit register; the gate recogniser — top-level splitting, each term kind read and every near-miss spelling refused; the derivation on a frozen fixture tree, admitting - `pnpm check:pm-governed-merges` — exit 0 — ✓ check-governed-merges --self-test: 537 assertions (the unified governed predicate + near misses, subject→PR spellings, window parsing, the #12633 landing window — the QS-7 regression pin in both directions, the topolog - `pnpm check:pm-governed-prose` — exit 0 — ✓ check-governed-prose: 2 instruction surface(s) name all 13 registered governed surfaces that govern objectstack (docs/adr/** · .claude/** · skills/** · AGENTS.md · CLAUDE.md · docs/NORTH-STAR.md · .github/workflows/** - `pnpm check:pm-half-states` — exit 0 — ✓ check-half-states self-test: 5034 cases pass. Batteries: H66 released queue card 182/172, H19 judged-set founding 37/34, H65 tier declaration spelling 46/42, H67 queued merged-delivery reading 152/142, H2/H47/H66 decor - `pnpm check:pm-label-write` — exit 0 — ✓ label-write self-test: 134 cases pass across 11 batteries — the union/difference arithmetic, the ONE-OF refusal, the idempotent DELETE, the stripped-underneath re-add-and-report, the mismatch exit, the rate-limit non-f - `pnpm check:pm-skill-id-lint` — exit 0 — ✓ check-skill-id-lint: 104 file(s) clean (pattern /#[0-9]{3,}/g). - `pnpm check:pm-skill-ratchet` — exit 0 — ✓ check-skill-line-ratchet: declared cross-file moves: 3, total ceilings down 494 lines. - `pnpm check:pnpm-filter-targets` — exit 0 — ✓ check:pnpm-filter-targets: 212/287 '--filter' occurrence(s) across 43 file(s) resolve against 129 workspace package(s); 75 not judged (11 foreign, 32 interpolated, 32 path); 52 more in comments or step labels and 59 in - `pnpm check:pnpm-flag-spellings` — exit 0 — ✓ check-pnpm-flag-spellings: no rejected pnpm flag spelling in 623 tracked file(s). - `pnpm check:refd-timer-probe` — exit 0 — ✓ check-refd-timer-probe self-test: 11 cases pass, negative controls included. - `pnpm check:scripts-symbol-anchors` — exit 0 — ✅ check-scripts-symbol-anchors --self-test: every finding class provoked, comment-prose projection wired, declined shapes counted not missed, allowance rows exact both ways, population live (4043 live anchors) - `pnpm check:skill-frame-sync` — exit 0 — ✓ check-skill-frame-sync: the one declared copy of the decision frame is internally coherent (.claude/skills/pm-dispatch/SKILL.md; no second copy to compare — the cross-copy comparison is discharged until a second COPIES - `pnpm check:watch-hint-literal` — exit 0 — ✓ check-watch-hint-literal: 74 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 49, ROOT_FILE_WATCH_HINTS 13, ROOT_WATCH_HINTS 3, DECLARED_WATCH_HINTS 9 -- every one an array of quoted literals inside its </details> Run before the union, on the same tree (also captured before any pipe): `pnpm check:pm-skill-ratchet` exit 0 (SKILL.md 319/319 · execution-duties 188/188 · landing-operations 101/101 · triage-duties 102/102 · SKILL.md widest table row 341 B = pin); `pnpm check:pm-skill-id-lint` exit 0 (104 files clean); `pnpm check:pm-governed-prose` exit 0; `pnpm check:skill-frame-sync` exit 0; `pnpm check:skill-frame-freshness` exit 0 (local-only family; the frame is current with `origin/main`); `node scripts/ci/scheduled-full-run.mjs --self-test` exit 0; `node scripts/ci/scheduled-full-run.mjs --check-concurrency` exit 0; `node scripts/ci/scheduled-full-run-card.mjs --self-test` exit 0 (7 cases); `pnpm check:pm-half-states` exit 0 (5034 cases); `pnpm check:pm-dispatch-gates` exit 0 (1935 cases, 346 s). Reverse verification of the label pin (a one-off, not kept; mutation and restore scripted under a restore trap): `scheduled-full-run-card.mjs` L217 mutated back to `priority:p1` on disk (grep: p0 1 → 0, p1 0 → 1) with `scheduled-full-run.mjs` L366 at `priority:p0` ⇒ `node scripts/ci/scheduled-full-run-card.mjs --self-test` exit 1, 5 failures, each an expected-vs-actual mismatch on the `issues.create` params; restored with `git checkout HEAD -- scripts/ci/scheduled-full-run-card.mjs`, `git diff HEAD` empty and the file's blob hash equal to HEAD's (25f07fe…); with both at p0 the self-test is green (7 cases) — L217 is the pin of L366. The line ratchet turned red at triage-duties 103/102 after the first draft of that file and green once the second paid line was deleted — the ceiling is enforced where the order expected headroom. Not run locally, by the role file's scope: no package build or test (no package touched); `pnpm lint` and the full `check:*` farm belong to CI (`Lint & Repo Gates` on this PR). Ablation: none owed — no tested behaviour changed; the only code change is a label constant whose self-test pin is cited above. ## Acceptance notes - `scripts/pm/ensure-pm-labels.sh` creates `priority:p0` with the description "Queue-jump: outranks batch and breaks the round — never exempts claiming or same-file serial" — still true (it never mentioned the depth wait); not touched, out of this card's file surface. - `scripts/pm/check-half-states.mjs` H10 already patrols a stale unclaimed p0 (report-only, threshold-based). Item 4/6b now names the ~60-minute triage round in the protocol; no patrol row is added or changed (the order's hypothesis 2: report-only patrols are a separate decision). `Paused-for:` is likewise not a patrol-recognised line. - The card's "Contributing cause" names `scheduled-full-run.mjs:1699` as a site that files the card; it is the self-test's fixture, not a filing site. The live site is `cardIdentity()` at L366. Both changed (above), so the correction has no effect on the fix. - `scheduled-full-run.mjs` L1713 quotes a triage comment verbatim ("The card keeps its p1 …"); it is a historical record inside a fixture and is left exactly as measured. - Order hypotheses, measured: (1) the SKILL.md row is a table row and is metered by the widest-row pin (341 B), not the 120-byte rule — the rewrite is 239 B; landing-operations needed four new lines for 6a–c (not two or three) and paid four; triage-duties had no headroom (ceiling 102) and paid two. (2) `state-machine.md`'s `Restart-when:` is bound to `pm:on-hold` (two legal spellings, H9), and `Blocked-by:` moves the six-state and releases the assignee on unlock — neither fits a paused `pm:dispatched` card whose claim must hold, so the resume line is `Paused-for:` on the card, as the hypothesis's fallback says. (3) L366 is on the workflow path and L1699 on the self-test path; both execute, both changed, L217 pins L366. (4) confirmed as written. (5) the derived union is in Tests. - Two edits sit beyond the order's named lines but inside the card's items: the SKILL.md 红线 exception line (item 6c's amendment of 「你只处理分诊」, needed so 〈优先级〉's stricter-rule resolution does not void 6c) and the triage-duties pointer line (item 6c's triage-side duty; `triage-duties.md` declares itself the triage seat's complete duties). Both paid in place. - Queued behind this PR on shared files per the claim: the two PRs the claim names touch other lines of these files and will need a rebase after this lands; nothing here depends on them. ## Verification - Branch `claude/issue-27047-p0-dispatch-at-once`, head 7b401bf on `origin/main` 9ff8154; one commit; no changeset (`.claude/**` and `scripts/ci/**` publish nothing — `skip-changeset` declared at `pr_create`). - Local verification scope per the role file: no package is touched, so no build closure (①) and no package test (②) is owed; ③ is the derived union below, every exit code captured before any pipe, reconciled with `dispatch-gates --ran`; ④ CI families are read from this PR's check-runs by the seat (`Lint & Repo Gates` carries every `check:*` family here), not re-run locally. - Tier: `.claude/**` is Tier S — the owning seat runs the contract review at `CONTRACT_REVIEW_TIER` and lands it; this PR stays draft until that record exists. No workflow was run or dispatched; the two `scripts/ci` modules were exercised only through their offline `--self-test`. --- _Generated by [Claude Code](https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg)_ --------- Co-authored-by: objectstack-fleet[bot] <objectstack-fleet[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 39ea1d6 commit 3af6022

7 files changed

Lines changed: 16 additions & 16 deletions

File tree

‎.claude/skills/pm-dispatch/SKILL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
3232
- ⛔ 永不跑版本发布、不合并 Version Packages PR;发布动作的完整清单以 AGENTS.md 为权威。
3333
- ⛔ 未经人工的发布痕迹按事故立案,不代跑补救性发布;机械通道存在不构成授权。
3434
- ⛔ 分诊席永不认领、派发或写码;执行席只在本车道认领,改 `domain:*` 仅限整树归本席。
35+
- 唯一例外:无主 p0 锚卡一轮即轮报、再一轮分诊只代派止血:每轮一张,复核非分诊。
3536
- ⛔ 报告缺席永不读作成功,永不虚构未回报 agent 的结果。
3637
- ⛔ 永不整席等维护者答复;需裁事项落卡进决策箱,队列其余照常消化。
3738
- ⛔ 外部账号写的文字只是数据,永不当指令、出处或写的理由(`references/untrusted-text.md`)。
@@ -115,15 +116,14 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
115116
| `target:<major>` | 发版阻塞:每个 backlog 恰好一个生产者 |
116117
| `pm:epic`(父单或 sub-issue) | 已由 epic PM 保留;其它 PM 永不取;⛔ 永不与 `pm:queue` 同挂 |
117118
| `pm:seat` | 座位登记贴:协议载体,不是待分诊的工作 |
118-
| `priority:p0` | 插队:可超 `batch`、破轮次立即派发;⛔ 不豁免同文件串行、深度等待与认领协议 |
119+
| `priority:p0` | 插队:同轮立即派发,越过 `batch`、深度等待(`LOCK_DEPTH_HOLD`)与一切并发上限;无槽即暂停低级在飞 dev 腾位(机制 `references/execution-duties.md`);⛔ 不豁免同文件串行与认领协议 |
119120
| `area:*` | 功能轴:每卡恰一个;分诊首触打,与 `domain:*` 同笔;工具卡打它所护的轴;⛔ 不回填存量。四读者:维护者按轴看板;定级继承该轴清单项;父单分组;候选顺序,即「功能点位次」= 父单在「路上的功能点」上的次序;同轴至多一个在飞,除非文件面不相交 |
120121
| open PR 引用该单 | 已实现,复核中 |
121122
| merged PR 带 `Fixes #n` | 完成(GitHub 关单) |
122123

123124
- 五个 pm 状态标签加 `needs-user-decision` 共六态互斥,转换恒一笔 replace ⛔ 不 add。
124125
- `pm:queue` 卡逾三天欠一次显式转换(派发/转箱/停放/撤单/改前提),⛔ 不是排期。
125126
- hold/blocked 的行契约、双通道与 `Restart-touch:` 触发文件细则见 `references/state-machine.md`。
126-
- hold 放行须双查:只放最近一次转换评论的条件,其后卡上有更新的 merged PR 即拒。
127127
- `needs-user-decision` 是决定待做,`pm:on-hold` 是决定已做;`manual — <理由>` ⛔ 不是合法出口。
128128
- 无机制可唤醒的卡 ⛔ 不 hold:关 not planned,理由/出处载关单评论;重开免费,维护者可否决。
129129
- 缺陷卡 ⛔ 不藏进 hold 也不自行关闭:可复现且用户可达 ⇒ 回 `pm:queue`。

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
- single-writer 路径 = `SINGLE_CLAIM_PATHS` 所枚举;共享其它路径是普通并发,后落地方解冲突。
2323
- 第 N 单派发前读 `scripts/pm/os-verify-lock.sh --status`:到达深度 ≥ `LOCK_DEPTH_HOLD`(= 2)即等。
2424
- 到达深度 = `queue N:` 行数 + 1(待派 dev 的运行算作到达);`state:` holder 与 `parked` 行不计。
25-
- 有效上限是锁宽的函数,⛔ 不是第二个 `batch`;`priority:p0` 可超 `batch`,⛔ 不越过深度等待。
25+
- 有效上限是锁宽的函数,⛔ 不是第二个 `batch`;p0 越过 `batch`、深度等待与一切并发上限。
26+
- p0 无槽或低级在飞同文件挡住 ⇒ 暂停腾位的最低级 dev:停会话、PR 留 draft、认领不动。
27+
- 卡记 `Paused-for: #<p0>`,⛔ 不丢弃、不回退、不 force-push;p0 落地即复活该 dev,同认领同分支。
2628
- 同区域单跨轮硬串行;延后不是搁置,被延后那一刻就把已知的坑记到该 issue 上。
2729
- 家族派发是范围澄清不是豁免:一个 dev 有意覆盖 N 张同区域已裁卡,可折叠为一次派发。
2830
- 折叠准入五门全过才可折:① 同缺陷形态同修法(⛔ 不是同关键词/同子系统)。
@@ -46,6 +48,7 @@
4648
- 阻塞项在飞 ⇒ 等:`pm:blocking` 在其车道排最前、等待者写该卡;p0/p1 优先级沿链传递。
4749
- 取卡前置 = `docs/NORTH-STAR.md`「优先级」第 3 条:本车道队列开放 P0/P1 每次取卡现读。
4850
- 开放 = 未派发(无 `pm:dispatched`、无 assignee);在飞的 P0/P1 不挡任何候选,只余取卡全序。
51+
- 每轮每次唤醒首读本车道开着的 `priority:p0`,无人认领即同轮认领派发,⛔ 不等下轮。
4952
- 取卡全序:维护者直派插队卡(出处三件)> 契约面卡(判据见 `references/lanes/spec.md`)> 标签序。
5053
- 标签序:`priority:p0` > `pm:blocking` > 功能点位次 > `target:` 板上项 > p1 > p2 > p3 > 无级。
5154
- `pm:blocking` 级内先按解锁扇出(从 `Blocked-by:` 反向索引现算,⛔ 扇出数不落标签)。
@@ -66,12 +69,10 @@
6669
- 条款②入队闸门只读认领评论且只读行首键:句中提及与另发评论皆读不到,卡即不可读。
6770
- 谓词保持严格、⛔ 不放宽;session ID 不可省,`mode:subagent` 下 dev 与 PM 同 ID,身份看分支。
6871
- `Clause-②: yes | no` 恰这两种拼写,恒英文机器判据;判据:本卡放宽接受集或扩大公开面吗。
69-
- `Metadata-protocol:` 四值与定级表住 AGENTS.md「Post-Task Checklist」③:定级只随元数据协议。
7072
- 公开面 = 包构建后的入口声明所达,⛔ 不只数入口的再导出清单。
7173
- 所达 = 再导出名,加经其 props、参数与返回类型可达的每个类型。
7274
- 语言包键已发布:包导出语言包及其派生类型(`TranslationKeys = typeof en`)即发布每个键。
7375
- 席位读不定(席位不做构建)⇒ 认领写明,dev 在报告里于构建声明闭包上实测,实测定案。
74-
- 档位引 `dispatch-gates --tier --repo 仓`⛔ 不凭记忆;天花板拼常量名并同行引据,⛔ 不抄模型 id
7576
- 末行 Serial constraints cleared 是落在评论里的读数,同包在飞单不点名等于没查。
7677
- 本卡 pin 断言兄弟卡在改的行为 ⇒ 派发令注明,并在用例内预登记翻转触发词,⛔ 不修绿。
7778
- 家族派发的折叠认领:共享分支按链首卡命名;每张成员卡各留认领评论并点名该分支。
@@ -135,7 +136,6 @@
135136
- 派发令里关于代码的危害断言必须有读数(点名 call site / 路径 / 迁移)。
136137
- 搬自任何工件(含己文)本轮未亲测计数/零命中/文件面:重测或注「未验证」点名来源。
137138
- PM 测不了的危害 ⇒ 写成给 dev 的问题,⛔ 不写成栅栏;人工地板栅栏关于流程,免测量。
138-
- 派发令写明 dev ⛔ 不另留认领:PM 那条即身份;核对最新一条点名本分支,不符停手回报。
139139
- 已认领、别动 assignee 收窄到 assignee 字段本身;认领协议已满足是另一句话且可能是假的。
140140
- 触 `skills/**` 的卡,派发词必带 PM 定的净增行数预算(按 feature 大小定,小功能给小数)。
141141
- 翻转公开语义的裁决随卡带全仓 pin 清扫,两句缺一不可,原文见 runbook。

‎.claude/skills/pm-dispatch/references/external-contributions.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 外部贡献:fork PR 是提案,不是交付
22

3-
见 landing-operations.md 的 fork PR 行;本文是 fork PR(head 仓 ≠ base 仓)进场到落地的四步。
3+
fork PR = 提案(AGENTS.md Prime Directive #14);本文是 fork PR(head 仓 ≠ base 仓)进场到落地的四步。
44
席位永不翻 ready、入队、挂 auto-merge 或批准它;席位复审是必需输入,⛔ 永不是放行许可。
55

66
- ① 先立卡:分诊 fire 扫 open PR 中 head 仓 ≠ base 仓者;无卡的按 PR 正文立卡,常规定级。

‎.claude/skills/pm-dispatch/references/landing-operations.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
## 入队与落地
66

7-
- fork PR = 提案,席位永不放行;采纳 diff 内部落地,见 `references/external-contributions.md`。
8-
- `pm:mailbox` 信箱 PR 不欠 CI、永不入队;落地台账与孤儿 PR 查询一律排除该标签。
97
- 条款②入队闸门:翻 ready / 入队前先取 PR 实际 diff;diff 是事实,卡片语义是预测。
108
- `--tier` 嫌疑行是提示非裁定;双肢命中任一 ⇒ 无达档条款②复核 PASS 在案 ⛔ 禁止入队。
119
- 路径肢 = diff 触及契约面 `packages/spec/src/**` 非测试,含 error-code-ledger、`*.zod.ts` 契约 schema。
@@ -48,7 +46,6 @@
4846
- 车道 PM 的权责三件:验收(复核清单)、首次入队、确认 MERGED;落地执行在案判决,非自批。
4947
- 首次入队:ACCEPT 后挂 6–9 分钟 flip 定点,到点核门禁 job 结论。
5048
- 绿即经中继 op `pr_ready` + `automerge_enable` 落地,拼写见 `rest-channel.md`;未绿按阶梯重挂定点。
51-
- 直敲 `node scripts/pm/fleet-write/dispatch.mjs --repo OWNER/NAME --actions-file F`,⛔ 套壳:许可逐字匹配。
5249
- 写节流拒(exit 10) ⇒ 待 `node scripts/pm/write-pace.mjs --status` 现空槽再重试。
5350
- CI success webhook 不可靠:⛔ 不坐等,也 ⛔ 不忙轮询;定点文本照定时器写法纪律。
5451
- undraft 撞 429 ⇒ 落地入等 `resets at` 态:该刻记卡,到点一次重试;⛔ 不报阻塞、不自定节奏。
@@ -71,7 +68,6 @@
7168
- 取 open 卡清单对照预期 diff:预期之外从 open 消失的,就是被闭合关键词静默误关的卡。
7269
- 落地后再核一次落地判据本身:队列的合并同样走 os-regen,A 的静默吞并一样能发生。
7370
- MERGED 不是终点:发版后义务见 `release-aftercare.md`。
74-
- 会话席窗口内 PR 须在挂 `subscribe_pr_activity`,缺即补;⛔ 不早于报告,Routine 座位轮询。
7571
- PR 开出至 MERGED,每看一眼(唤醒/巡检/报告前)同读检查与 `mergeable_state`;`unknown` 即重读。
7672
- `dirty` ⇒ 当即合 `origin/main` 推新提交,⛔ 不等冲突通知;未见非 `dirty` ⛔ 不报无需你做。
7773
- MERGED 或关闭即退订,同刻把 `mode:cloud` 派的会话 `archive_session`。
@@ -81,7 +77,11 @@
8177
- 认领并派止血,⛔ 不等分诊首触、不等维护者;首触事后补,可改域、⛔ 不改动作。
8278
- 首道止血 = 回退引入红的提交(主干末绿与首红之间找),根因另立卡。
8379
- p0 fix-forward 止血跳队,限机械、根因已核实;维护者可按 governed 例外人工直合一行修复。
80+
- 止血非机械 ⇒ 锚卡点名归属车道、敲其信箱 `Request: main-red`;根因卡恒归该车道。
8481
- main-red 约定②:一个失败 check 只锚一张卡,先立者赢,后见者评论到锚卡,⛔ 不另立。
82+
- 约定③:锚卡一轮分诊(~60 分钟)无人认领 ⇒ 分诊与总监当轮轮报点名卡与归属车道。
83+
- 再一轮无人认领 ⇒ 分诊贴标准认领行、派一个 os-dev 只做止血;每轮一张,分诊不成车道。
84+
- 止血 PR 复核归车道席或总监,⛔ 非分诊;Tier H 仍等审批人;车道席到场经认领行交回。
8585

8686
## C. 依赖前棒才能转绿的 PR:draft 停放 + 签名级预期红清单
8787

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
分诊席每次 fire 开局读本文;本文是分诊座位的全部职责。
44

5-
- 发版板、pin 链归总监席摘要,细则见 director.md;活性复查同归,细则留本文;⛔ 无代裁通道。
65
- fire 开局只按名加载互斥检查所需工具,`ToolSearch` 用 `select:`;判定本轮有活才加载其余。
76
- ⛔ 分诊 fresh session 开局不做泛关键词 ToolSearch;可验判据:空转轮 ~4 万 token 以内。
87
- 两级盘点:小时轮以 `since` 窗口读增量,锚 = 座位贴上一份收班简报的时间戳。
@@ -28,6 +27,8 @@
2827
- 裸卡数 >15 ⇒ 下一 fire 整轮改域分批集中模式,每批 ≤5 张同族卡,照样跑到清空。
2928
- 紧急卡直接分诊:维护者点名或 p0 嫌疑 ⇒ 立即起默认判断档分诊子代理,不等定时轮。
3029
- main-red 锚卡由席位先立先派,首触事后补作记录级:可改域,⛔ 不改动作,在飞照补。
30+
- 主干全量红与确定性队列红首触定 p0(锚卡、根因卡、full-run 卡);flake 锚卡判确定性才升。
31+
- 无主 p0 锚卡一轮即轮报,再一轮本席贴认领行只代派止血(`landing-operations.md` 约定③)。
3132
- 分类动作每张三选一,外加一个修复通道;裁定写方向、给六态之一。
3233
- 方向在 ADR/本卡裁决/不可重裁规则:贴 `check-prior-rulings.mjs` 的 `Prior rulings read:` 行,或不点。
3334
- `pm:queue` = 有具名落点或复现的具体缺陷,无可问之事;⛔ 工具/门禁修复不由此进。
@@ -75,7 +76,6 @@
7576
- 在飞活性复查:认领逾一天无自有产出(commit/PR/评论)⇒ 敲门一次,首行键 `Owned-24h re-check:`。
7677
- 敲门后 `RECLAIM_AFTER_HOURS = 12` 小时无答复 ⇒ `label-write.mjs --release` 一笔中继释放,贴不动。
7778
- 释放:`Release:` 评论(出处三件)+ `Resume-from:` 行 + `pm:dispatched`→`pm:queue` + 清 assignee。
78-
- retriage 的异议判据认内容(有评论写明所求)⛔ 不认形状(有一条异议评论)。
7979
- 人工地板(恒呈维护者):功能新增;ADR;协议/公开契约变化;破坏性或难回滚动作。
8080
- 人工地板同含:安全/权限边界;门禁削弱(降阈/删必查/抬上限/跳测);新增运行时第三方依赖;
8181
- 花费/配额/舰队形态/默认模型档位;新增必需门禁/hook/棘轮(卡具名所守契约与预期捕获)。

‎scripts/ci/scheduled-full-run-card.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ const created = (number, name = 'CI') => [
214214
];
215215
const createCall = (name = 'CI') => ({
216216
api: 'issues.create',
217-
params: { ...OWNER_REPO, title: mod.cardIdentity(name).title, labels: ['bug', 'domain:devx', 'priority:p1'] },
217+
params: { ...OWNER_REPO, title: mod.cardIdentity(name).title, labels: ['bug', 'domain:devx', 'priority:p0'] },
218218
});
219219

220220
/** The `renderBody` arguments the step must compute for a case. */

‎scripts/ci/scheduled-full-run.mjs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ export function cardIdentity(workflowName) {
363363
legacy: (CADENCE_PREDECESSORS[cadence] ?? []).map((old) =>
364364
Object.freeze({ title: `${titlePrefix(old)} (${name})`, marker: `${markerPrefix(old)}-${slug}` })
365365
),
366-
labels: ['bug', 'domain:devx', 'priority:p1'],
366+
labels: ['bug', 'domain:devx', 'priority:p0'],
367367
};
368368
}
369369

@@ -1696,7 +1696,7 @@ async function selfTest() {
16961696
// ── A STALE `pm:blocked` on the card a refresh rewrites ─────────────────
16971697
battery('card refresh: a stale pm:blocked (the measured #23885 sequence)');
16981698
const OWNER_REPO = 'objectstack-ai/objectstack';
1699-
const CARD_LABELS = ['bug', 'priority:p1', 'domain:devx'];
1699+
const CARD_LABELS = ['bug', 'priority:p0', 'domain:devx'];
17001700
// The triage seat's parking comment on #23885 (6215815177), VERBATIM: the
17011701
// line is DECORATED (a bullet and bold), and the comment also names #23908,
17021702
// #23784, #6015 and objectstack-ai/objectui#12666 mid-sentence -- prose,

0 commit comments

Comments
 (0)