Skip to content

pm-dispatch: the spec floor rides its first consumer's PR; bump cards read the hold index - #24315

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-24276-spec-floor-rider-hold-index
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-24276-spec-floor-rider-hold-index

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24276
Clause-②: no

Two maintainer-ordered rules land in .claude/skills/pm-dispatch/references/**. Each is paid for in its own file, so the net line change is zero per file. There is no new gate, no ceiling raise, no CROSS_FILE_MOVES entry and no edit to scripts/pm/check-skill-line-ratchet.mjs. The PR stays a draft. This is Tier S: the seat lands it after its in-seat contract review.

The maintainer's words, verbatim from the card: 「下限提到 next.3」, then 「下限提到 next.3我记得之前有一个升级任务,为什么没升级完整,以后如何避免。」, then 「立卡吧,A 和 B 都要」.

What lands

A: dispatch-runbook.md 〈派发词构造细则〉, right after the file-surface lines

- 卡的代码首读 spec 发布 R 首发的键、类型成员或运行时行为,即 R 的首个消费者:
- 同 PR 把消费包 `@objectstack/spec` 下限抬到 R,认领文件面点名 manifest 与 lockfile specifier。
- 「不动 pin」只禁 lockfile 解析版本与 `.objectui-sha`/console pin 的移动,⛔ 永不禁下限。

How the lines map to the ruling text:

  • Trigger. "A card whose code first reads a key, type member or runtime behaviour that spec release R first publishes" is line 1.
  • Duty. "raises the consuming package's @objectstack/spec floor to R in the same PR" is line 2, first clause.
  • File surface. "The claim's file surface names the manifest and the lockfile specifier when this applies" is line 2, second clause. The condition is line 1.
  • Boundary. "'No pin bump' bans only the lockfile move and the .objectui-sha / console pin move — ⛔ it never bans the floor" is line 3.
  • One word made explicit. Line 3 says the lockfile resolution (解析版本). Raising a floor also rewrites the lockfile's specifier: line, and line 2 tells the claim to name that line. If line 3 said only "lockfile", the two lines would seem to contradict each other. This is the distinction the ruling already draws, not a new one.

B: triage-duties.md, right after the bump-card cap (bump 单单张封顶)

- bump 单的等待卡清单读自 hold 索引,查询式与命中数落正文;⛔ 凭记忆写的不是该清单。
- hold 索引 = `Restart-when:` 或 `Blocked-by:` 行点名该发布、版本或上游 commit 的全部 open 卡。

The second line is the ruling's own definition of the hold index ("every open card whose Restart-when: or Blocked-by: line names that release, version or upstream commit"). The first line carries the rest: "read from" the index, "the query and its hit count go in the card body", and "⛔ a list written from memory is not that list".

Where each rule sits, and why

A goes in the runbook, not beside cross-repo-coordination.md L33. I measured both placements against the 120-byte cap.

  • Size. A is 329 bytes across three lines. Its tightest one-line trigger-plus-duty form is still 116 to 129 bytes before it names the file surface. So one line beside L33 can hold only the boundary (line 3, 110 B), never the duty.
  • Splitting. Splitting A would put the duty and its boundary in two files that are read at different moments. cross-repo-coordination.md is read at routing and at cross-repo claim or transfer time. The runbook section is read while an order is composed: execution-duties.md 〈派发〉 sends the composer there for 构造细则与条款原文.
  • Where the miss happened. The incident's miss was in an order: its fence said "⛔ Do not bump a pin in this PR", and that kept the floor out of the PR. The runbook section already holds the file-surface sentences the order copies (文件面两句原文照抄). Putting A beside them means the seat writes the file surface and the fence in one read.
  • Budget. cross-repo-coordination.md (51/51) had the fewest payable lines of the candidates.

B goes in triage-duties.md. The per-next.N consumer bump card is filed by the triage seat. The incident's own card (objectstack-ai/objectui#12873) says "Filed by the triage seat" in its first paragraph. So triage-duties.md is the file that card's writer reads, and B sits beside the bump-card cap as the rule for every bump card's held-card list.

Payment ledger: every removed or merged clause, and where it survives

All line numbers are at base 21070a2.

File Base line(s) Change Clause removed Where it survives
dispatch-runbook.md L202 line deleted (−1 line, −73 B) 点名单当天现取仍会漏,dev 对实际改动路径重取补跑 execution-duties.md L133 「点名单是线索不是规格,dev 对实际改动重取补跑」. That file is read by the same composing seat on every fire. The dev side is in os-dev.md 〈本地验证范围〉: derive with no paths, and the script takes the change set from the merge base. This is a dev duty restated in a section whose first line (L188) says dev duties are single-sourced in the os-dev definition and ⛔ not restated.
dispatch-runbook.md L232 + L233 folded into one line (−1 line, −27 B) 「⛔ 静默略过」 The mandated sentence two lines up (L231, 「每一面都必须在 PR 正文有一个结论」) and execution-duties.md L142 「编译面清单逐面申报,⛔ 静默略过」. The failure-mode text stays in the merged line: 评审把没提到的面读作漏掉.
dispatch-runbook.md L235 line deleted (−1 line, −108 B) 只读读数 dev 不建闭包、不跑测试、不开 PR;只读树与记录,读数贴决策卡或立卡 os-dev.md L355 「派发词标只读读数 ⇒ 不建闭包、不跑测试、不开 PR;报 status: "reading",读数贴决策卡」, which is the dev's own binding text. 「或立卡」 survives as os-dev.md L63 (three-class findings go into the report and the seat files them). This is a second os-dev duty restated under L188. The PM-side line it followed (L234, the measurement-first dispatch choice) stays.
triage-duties.md L39 + L40 folded into one line (−1 line, −88 B) 落卡必带四棱卡面块…「维护者速读」,⛔ 不留待有人接手再补 filing-gate.md L17 「② 只有维护者能做的决定,落卡即带「维护者速读」与四棱块」. The triage seat reads that file at filing time (triage-duties.md L41 points there). The inbox backfill is L52 (决策箱勤务:入箱校验/补全四棱块、速读). Kept in the merged line: 速读 sits above the block (其上的 → 居四棱块之上), 同题, business language, and the one-word closing question.
triage-duties.md L55 + L57 folded into one line (−1 line, −74 B) 是分诊固定产出,分诊席唯一权威生产 cross-repo-coordination.md L37 「分诊座位唯一生产:定级/路由/type/查重/shadow/duplicate_of」 and L7 (中央分诊是 domain:*/type/定级的唯一生产者). Both are read by the triage seat before routing on every fire. The mandatory-output sense survives as 新卡即时打. Kept: 立单者可预填、分诊校正, 新卡即时打, 存量卡 ⛔ 不回填、下次碰到补.

No payment is a re-wrap. Each one deletes bytes that are stated elsewhere. Three are whole-line deletions; two are folds that drop a restated clause. Moving whitespace pays for nothing here.

line_budget

File Lines before → after Ceiling Headroom after Bytes before → after
references/dispatch-runbook.md 241 → 241 241 0 17,950 → 18,071 (+121: +329 rule, −208 payment)
references/triage-duties.md 120 → 120 120 0 12,261 → 12,329 (+68: +230 rule, −162 payment)

Every touched line is ≤ 120 bytes. The widest is 120 B, on the folded type line.

Worked example for B (an example only, not rule text)

The triage seat's reading on the card (comment 6248125841) names three objectui cards that wait on the next release: objectstack-ai/objectui#12932, objectstack-ai/objectui#12929 and objectstack-ai/objectui#12484. Each is pm:blocked. I read each card on its own through REST on 2026-10-18 at about 11:05Z. Each body carries a line-anchored Blocked-by: objectstack-ai/objectstack#24168, the Version Packages PR that publishes next.4, plus the upstream commit the card waits for (586705d85d, 1e001c7df, e15897c0f1).

So the next objectui consumer bump card should get its held-card list from one query over objectui's open cards. That query matches Restart-when: / Blocked-by: lines that name next.4, objectstack-ai/objectstack#24168 or one of those commits. Today it should find at least these three. The bump card's body records that query and its hit count. I did not run the enumeration myself, because the os-dev definition keeps the dev off open-issue scans. The bump-card writer runs it.

Verification (at head 6cfc7839, after the merge of origin/main 0b44840d)

The change set is 2 paths, +8/−8. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 19 commands, the same 19 the seat derived. I ran all 19 locally. I also ran pnpm check:pm-settings-deny-roster, which the derivation flags because its roster lives under .claude, and the local-only pnpm check:skill-frame-freshness. Each exit code was captured before any pipe. --ran reconciles 19 derived / 19 run / 0 NOT-MEASURED / 0 UNRUN.

node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/pm/check-harness-current.mjs --self-test :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-half-states :: exit 0
pnpm check:pm-label-write :: exit 0
pnpm check:pm-skill-id-lint :: exit 0
pnpm check:pm-skill-ratchet :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:skill-frame-sync :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:pm-settings-deny-roster :: exit 0
pnpm check:skill-frame-freshness :: exit 0

pnpm --filter @objectstack/lint run check:doc-formula-expressions first exited 3 (PREREQUISITE NOT MET: @objectstack/formula and @objectstack/lint were not built). I built that closure under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0) and re-ran the gate. After the merge I rebuilt the closure again. origin/main moved once more while the gates ran, to 52ff308b (a REST route refactor). That commit touches scripts/check-route-envelope.mjs and one scripts/adr-anchors/ file, which are none of this card's 19 families and no path under .claude/**. I did not merge it again; the merge queue rebuilds onto the current main.

The families CI runs (Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard and the rest) are left to this PR's check-runs. I did not re-run them locally. No package is touched, so there is no build closure, package test or typecheck to owe.

Acceptance notes

  • Not filed: the objectui consumer bump card has no line of its own. triage-duties.md names the console bump card and the cloud .objectstack-sha bump card as cases, but not the per-next.N objectui consumer bump card that the triage seat files by standing practice. B is written to cover every bump card, so it covers that card too. This is an observation, not a defect, a contract violation or a metadata trap, so it is not filed. Carrier: none (承接者:无).
  • The fence's source sentences are unchanged. cross-repo-coordination.md L33 (⛔ pin bump 不做 rider) and lanes/ui.md L20 (console pin bump 不做 rider) still read as before. A's boundary sits where the order is composed, not beside them (see placement above).

维护者速读(草稿)

改了什么:PM 技能参考文件新增两条规则。A:哪张卡的代码第一次用到 spec 某个新版本才有的东西,就在同一个 PR 里把该包对 @objectstack/spec 的版本下限抬到那个版本;派发令里的「不动 pin」只管 lockfile 解析版本和 console pin,不管下限。B:每张版本 bump 单列「等这个版本的卡」时,必须按 Restart-when:/Blocked-by: 行查出来,并把查询和命中数写进正文,不许凭记忆列。

为什么改:next.3 那次,一张卡已经用上 next.3 才有的类型,包的下限却还停在 next.2(派发令写了「不动 pin」,把抬下限也挡在外面);同时 bump 单凭记忆列等待卡,漏了一张正在等 next.3 的卡。这两条规则把这两个漏洞都补上。

风险与代价(含回滚):只改给 PM 席位读的规则文本,不碰代码、门禁和发布物;两个文件行数都不增加,新增内容靠删掉别处已写明的重复句子付账,每一处删除及其存活位置都列在正文的表里。回滚即 revert 本 PR。

席位意见:

你要做的:无需操作。席位复核 PASS 后由席位入队落地(Tier S)。


Generated by Claude Code

… hold index

Two maintainer-ordered rules, each paid in its own file at net zero lines:

- dispatch-runbook.md (dispatch-order composition, beside the file-surface
  lines): a card whose code first reads a key, type member or runtime
  behaviour that spec release R first publishes raises the consuming
  package's @objectstack/spec floor to R in the same PR, and its file surface
  names the manifest and the lockfile specifier; "no pin bump" bans only the
  lockfile resolution and the .objectui-sha / console pin move, never the
  floor. Paid by deleting two os-dev duties the section says it does not
  restate (stated in execution-duties.md and os-dev.md) and folding a
  restated silent-skip ban into its conclusion line.
- triage-duties.md (beside the bump-card cap): a bump card's waiting-card
  list is read from the hold index (every open card whose Restart-when: or
  Blocked-by: line names that release, version or upstream commit), with
  the query and hit count in the body. Paid by folding two clauses restated
  in filing-gate.md and cross-repo-coordination.md.

Claude-Session: https://claude.ai/code/session_01V1upgJbYCZ5xed9pEQF4Tz
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 18, 2026
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Oct 18, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 6cfc783910a3bbdd0d94ec3750e8e051d6f3a8e6
Local-runs: none
Scope: .claude/skills/pm-dispatch/references/dispatch-runbook.md, .claude/skills/pm-dispatch/references/triage-duties.md + .claude/skills/pm-dispatch/references/execution-duties.md (L133, L142), .claude/agents/os-dev.md (L355), .claude/skills/pm-dispatch/references/filing-gate.md (L17), .claude/skills/pm-dispatch/references/cross-repo-coordination.md (L7, L33, L37), .claude/skills/pm-dispatch/references/state-machine.md (the Restart-when: and Blocked-by: line contracts B names) — all read on origin/main 52ff308b

Face: governed rules text (.claude/**, Tier S); rendered in seat — get_session last_served_model equals the value of CONTRACT_REVIEW_TIER. Inputs: the card (body and five comments, the post-dispatch note 6248946273 included), the PR diff read through git (origin/main...refs/pm/24276, +8/−8 over 2 files), the PR body, the head's 37 check-runs. ⛔ Not the dispatch order's conclusions; the dev's report was read for its machine fields only.

① Derived judgments

  • A (dispatch-runbook.md L211–L213, three lines of 106, 113 and 110 bytes): trigger — a card's code first reads a key, type member or runtime behaviour that spec release R first publishes; duty — the same PR raises the consuming package's @objectstack/spec floor to R and the claim's file surface names the manifest and the lockfile specifier; boundary — 「不动 pin」 bans only the lockfile's resolved version and the .objectui-sha/console pin move, ⛔ never the floor. Each clause of the card's A maps to one line. 「lockfile 解析版本」 for the card's 「lockfile move」 is the only sharpening and it is forced: raising a floor rewrites the lockfile's specifier: line, which line 2 orders, so an unqualified lockfile ban would contradict line 2. Correct.
  • A's placement: the section the dispatching seat reads while composing the file-surface sentences (〈派发词构造细则〉, two lines below 「文件面两句原文照抄」), not beside cross-repo-coordination.md L33 「⛔ pin bump 不做 rider」. Measured by the dev: one line beside L33 carries only the 110-byte boundary, never the duty. L33 and lanes/ui.md L20 are unchanged and neither contradicts A (they ban the pin as a rider; A is about the floor). Correct.
  • B (triage-duties.md L30–L31, 115 bytes each): the bump card's waiting-card list is read from the hold index, the query and its hit count go in the body, a list from memory is not that list; hold index = every open card whose Restart-when: or Blocked-by: line names that release, version or upstream commit. Verbatim to the card's B; placed after the bump-card cap, where every bump card's writer reads. 「上游 commit」 keeps B valid under the objectui pin model ruled B on decision: objectui 按提交钉 objectstack(.objectstack-sha),开发与 CI 从该提交的源码构建 @objectstack/*,npm 版本范围与下限只在 objectui 发版时由提交算出 —— 前端不再逐个 PR 等后端发版 objectui#12955 (note 6248946273). Correct.
  • Payments, five, each verified on origin/main 52ff308b as stated elsewhere: runbook L202 (the dev re-derives over its real paths) survives at execution-duties.md L133 and in os-dev.md 〈本地验证范围〉 ③; the runbook L232+L233 fold drops 「⛔ 静默略过」, which survives at execution-duties.md L142 「编译面清单逐面申报,⛔ 静默略过」 while the merged line keeps the failure mode 「评审把没提到的面读作漏掉」; runbook L235 (what a read-only dev does) survives at os-dev.md L355; the triage L39+L40 fold drops 「落卡必带四棱块…⛔ 不留待有人接手再补」, which survives at filing-gate.md L17 「落卡即带「维护者速读」与四棱块」, read at filing (triage-duties L41 points there); the triage L55+L57 fold drops 「分诊固定产出,分诊席唯一权威生产」, which survives at cross-repo-coordination.md L7 and L37. No payment is a re-wrap: three whole-line deletions and two folds that each drop a clause stated elsewhere. Ratchet 241/241 and 120/120 (Lint · PM dispatch-gates and Lint & Repo Gates success on the head); every touched line ≤ 120 bytes (widest 120, the type line).
  • No issue or PR number in any rule line; no new gate, no ceiling raise, no CROSS_FILE_MOVES entry, no scripts/pm/** path. The incident stays on the card.
  • Accept set and public surface: none touched. .claude/** is unpublished; Clause-②: no on the body's line 2 is right.

② Semver level

No published package is touched; skip-changeset is the correct declaration (read back on the PR's labels). No semver level applies.

③ Boundary flags

Implemented-by: claude/issue-24276-spec-floor-rider-hold-index
Reviewed-by: session_01V1upgJbYCZ5xed9pEQF4Tz

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

改了什么:PM 技能的两份参考文件各加一条规则,行数不变。A(dispatch-runbook.md 派发词构造细则):哪张卡的代码第一次用到 spec 某个新版本才有的键、类型或运行时行为,就在同一个 PR 里把该消费包对 @objectstack/spec 的下限抬到那个版本,认领的文件面点名 manifest 与 lockfile specifier;派发令里的「不动 pin」只管 lockfile 解析版本和 .objectui-sha/console pin,永不管下限。B(triage-duties.md bump 单段):bump 单的等待卡清单必须按 Restart-when:/Blocked-by: 行查出来,查询式与命中数写进正文,凭记忆列的不算。

为什么改:您在 objectui 席会话里的原话「下限提到 next.3我记得之前有一个升级任务,为什么没升级完整,以后如何避免。」与「立卡吧,A 和 B 都要」。next.3 那次两处漏:用上 next.3 类型的 PR 被「不动 pin」挡住没抬下限;bump 单凭记忆漏了一张等 next.3 的卡。

风险与代价(含回滚):只改席位读的协议文本,不碰代码、门禁、发布物;两文件都在棘轮天花板,新增 5 行由同文件删掉 5 行别处已写明的重复句付账,每处删除与存活位置列在 PR 正文表里,席位逐条在 origin/main 上核过。回滚 = revert 本 PR。

席位意见:ACCEPT,席内达档复核 PASS(见上一条评论)。一点需知:您在 objectui#12955 裁 B(objectui 按 commit 消费 @objectstack/*)后,objectui 席在卡上留注:objectui#12958(发布时按 pin 算下限)落地后,A 对 objectui 变为冗余,此前 A 仍成立。A 是对任何按 npm 区间消费 spec 的包的通则,您的「A 和 B 都要」未被撤,故照裁落地;本席记一条跨车道备忘,objectui#12958 关闭时重读 A 决定退役或收窄。B 不受影响(「上游 commit」已覆盖 pin 模型)。

你要做的:无需操作。Tier S,本席经中继 ready + auto-merge 入队。

席位核对

ACCEPT — PR #24315, head 6cfc7839, reviewed by session_01V1upgJbYCZ5xed9pEQF4Tz (os-steve, domain:skills#1) against GitHub and origin/main 52ff308b, not against the report.

  1. 形态: draft, base main, same-repo head; body line 1 Fixes #24276, line 2 Clause-②: no; the only closing keyword in the stored body is that Fixes; files = 2, both .claude/skills/pm-dispatch/references/**, +8/−8; labels skip-changeset (correct: .claude/** publishes nothing), documentation, size/s; assignee os-steve; body carries ## 维护者速读(草稿) with 席位意见 empty and ends with one session-URL footer; commit bfec7efd carries the model-free trailer pair; merge commit 6cfc7839 over origin/main 0b44840d (one merge before the PR opened, AGENTS.md §10).
  2. 前提实测: both probes re-run by the seat at ef65646 and by the dev at 21070a2 — A and B absent (0 hits each, control Restart-when 9 hits in 5 files); the card's ten referenced issues and PRs read live before dispatch (the incident closed by deps(app-shell): raise @object-ui/app-shell's @objectstack/spec floor from ^18.0.0-next.2 to ^18.0.0-next.3, the first release that declares and serves the package row's source the Packages page now requires objectui#12943; the three objectui cards triage named are open pm:blocked with a line-anchored Blocked-by: objectstack-ai/objectstack#24168). premise_still_valid: true holds; the note 6248946273 narrows A's future for objectui, not its validity today (速读 席位意见 above).
  3. 门禁 check-runs on 6cfc7839: 37 runs — 25 success, 12 skipped, 0 failure; Lint & Repo Gates, TypeScript Type Check, Test Core, Governed Surface Queue Guard success; Build Core skipped; check-expected-skips.mjs --pr 24315 OK, all 12 skips in the roster. Dev side: 19 derived / 19 run / 0 NOT-MEASURED at the same head, exit codes captured before any pipe (listed in the PR body); the seat re-ran no gate locally.
  4. 接受的偏差: the report JSON sits in a fenced block under the plain-text os-dev-report line (marker readable; formatting only); three git pushes (probe, commit, merge); attribution follows AGENTS.md over the harness reminder (correct); mechanism assumption (d)'s L27+L28 fold refused by measurement (123 bytes) and the payment taken from the runbook instead — the order allowed either.
  5. 范围外去向: one acceptance note (no line names the per-release objectui consumer bump card; B covers every bump card) — carried on this seat's post, not filed; carrier: none is right for an observation. Cross-lane memo recorded on the seat post: re-read A when tooling: objectui's publish computes each @objectstack/* floor from .objectstack-sha — the first published release that contains the pinned commit — and stops when none does (ruling B on objectui#12955, step 2) objectui#12958 closes.
  6. 落地条款: Tier S (check-governed-merges.mjs --pr 24315: 2 of 2 paths under .claude/**, 16 changed lines); contract review of record PASS on this head (the previous comment); every check green or an expected skip; mergeable true; no generated artifact on the surface (no merge=os-regen path); no Restart-touch: line on any open pm:on-hold card names either file (15 cards scanned, 1 such line, disjoint). The seat lands it now through the relay (pr_ready, then automerge_enable) and follows it to MERGED.

mcp_calls 0 · api_writes 2 relay strokes (pr_create with assignee and label, the report comment) · line_budget 241→241 and 120→120, headroom 0 — machine fields read from the report and checked against the PR object and its labels.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants