Skip to content

tooling(pm): the partial-dispatch landing is a release — one SKILL.md rule, one report-only sweep row (H49) - #16040

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-16003-partial-dispatch-release
Sep 5, 2026
Merged

tooling(pm): the partial-dispatch landing is a release — one SKILL.md rule, one report-only sweep row (H49)#16040
os-zhuang merged 1 commit into
mainfrom
claude/issue-16003-partial-dispatch-release

Conversation

@os-steve

@os-steve os-steve commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Fixes #16003

Two files, nothing else: .claude/skills/pm-dispatch/SKILL.md (one rule, two lines, line-neutral) and scripts/pm/check-half-states.mjs (one report-only row, H49, plus the two tidiness items from #15899's flight). Governed (.claude/** hit): draft, human merge, skip-changeset — this diff publishes nothing from any package.

The rule (SKILL.md, 认领 family, beside the release line)

A dispatch may cover ONE item of a card whose other items belong to another lane or a governed surface. Before today the state model had no transition for the moment that PR lands: the card stays open, pm:dispatched, assigned, with no dev — and two seats improvised the exit in one shift, one of them parking a whole card behind a decision only one item depended on. The rule, quoted unbroken:

部分落地(PR 带 Refs #N (item k),⛔ 不 Fixes)即释放:合入同笔回 pm:queue + 清 assignee。
同笔 Release: 行点名已落项与余项去向;余项需换道/拆分加 pm:retriage,自队列重新认领。

Rules-only register, 111 and 118 bytes. Paid for by two folds in the 状态模型 prose, both semantically one rule already: the not-planned closure pair (「无机制可唤醒的卡 ⛔ 不 hold…;重开免费,维护者可否决」, 119 bytes) and the unreleased-upstream hold pair (「该 hold 评论预写唤醒后的派发形状;pin 消费的仓不适用…」, 120 bytes). No table row touched; the four-axis frame block and the 升级与决策 section are untouched.

Same-file caution, proved. PR #15946 (3ba4752b31, claude/issue-15929-governing-text-step) edits SKILL.md at 712–733. This branch's hunks sit at 130, 146 and 466. Fetched into a ref this session owns and merged in memory at head 023dd73f3e:

git merge-tree --write-tree --name-only refs/pmwt/issue-15929 HEAD
exit 0
b8c43941c15aea0ccac1c18e1cdb82ae60b797c3

One line of output — the merged tree's id — and no conflicting path listed.

The row (check-half-states.mjs, H49, band state, report-only)

An OPEN card carrying pm:dispatched with a non-empty assignee (and no pm:queue) whose newest Refs-linked PR is MERGED while no Release: or Claim: comment on the card is newer than that merge — a partial dispatch landed and the card still reads TAKEN; the release is owed. The finding names the PR and the item (Refs #N (item k); a bare Refs #N fires too and says no item is named), quotes the partial-landing line and RELEASE_ACT_RULE unbroken, and states the remedy as one write: pm:dispatched to pm:queue (adding pm:retriage when a remaining item needs a re-lane or a split), assignee cleared, the Release: line naming the landed item and where the remaining items go; the remainder re-dispatched from the queue as a fresh claim.

  • Extractor. refsTargets joins H7's partOfTargets/closingKeywordTargets, with H7's strictness: word-bound, whitespace then #, no colon, code-stripped on the body surface, fresh regex per call, Ref/References deliberately not read. It captures the (item k) annotation only when it directly follows the number.
  • Link relation. refsOnlyLinksFor counts a PR as a Refs link only when its body does NOT also deliver the card (Part of, closing keyword) — those are H8's, bound per card number as H7 binds.
  • PR side. H8's merged window plus the open-PR listing the sweep already holds; no new fetch. An OPEN Refs PR on the card is the newest word and the row declines.
  • Thread side. latestMarkedComment — H47's resolution, reused. A marker whose stamp does not parse cannot be placed against a merge, so the row declines rather than accuse.
  • Cost. Nothing. The thread comes out of commentCache, which H2's claim read fills for exactly this population (an assigned pm-tracked card), so the coverage pair partialCandidates/partialJudged is complete by construction; a shortfall is a thread H2 could not read.
  • Adjacency, pinned. On a Refs-only merged PR whose head is not named for the card, H8 is silent and H49 fires (the gap this row closes). On a head named for the card both fire; after a Claim: newer than the merge H49 stands down while H8 cannot read the thread — filed as check-half-states: H8's branch-name fallback reads a merged Refs #N (item k) PR on a card-named branch as a full delivery, so it prescribes dropping pm:dispatched on a lawfully re-dispatched remainder #16036, not addressed here.

The two tidiness items (same file)

Verification — all readings at head 023dd73f3e

Self-test. node scripts/pm/check-half-states.mjs --self-test: 2427 → 2535 cases pass. Case-name sets diffed: 0 names left, 108 arrived, none dropped. The self-test's own PARTIAL_RULE_LINE is an independent transcription of the SKILL.md line, compared against the row's output rather than against the constant it pins.

Ablation, on a throwaway copy placed in scripts/pm/ so every sibling resolves (the control proves the governed register loads), copy blob proved equal to the HEAD blob before each leg, each mutation confirmed on disk by grep counts of the injected marker and the deleted text, restore proved by blob hash:

leg mutation result
control none (copy blob b5b98548ae… = HEAD blob) 2535/2535 green
A h49PartialLandingUnreleased returns null after its population check (marker 1 on the copy, 0 in the tree) 29 red — every firing-direction H49 case; the clean-direction cases stay green, correctly
B the findings.push([issue, 'H49', owed]) line replaced (deleted text 0, marker 1) 4 red — H49's two registry cases, plus H48's and the #13947 invariant "no band names a family the sweep never emits"
C the H49 summary clause removed (deleted text 0, marker 1) 9 red — 4 generic summaryClause invariants + 5 H49 summary cases

Tree file blob identical before and after the whole run (b5b98548ae906e97327a191e6a799b45bd4eab49), git diff HEAD empty, no copy left behind. No rebuild leg applies: node loads this .mjs source directly.

Live sweeps, read-only, before (the merge-base blob 3161d0b325… extracted and hash-checked) and after, both boards:

board before after H49 rows H49 coverage (judged of candidates)
objectstack 362 365 0 49 of 49
objectui 161 161 0 10 of 10

Per-family: objectstack H2 22 → 23 and H24 1 → 3; objectui H20 0 → 1 and H44 15 → 14 — drift between two sweeps minutes apart on live boards worked by parallel seats; every other family identical, H49 contributing nothing to either side. Zero is the expected reading, not a quiet row: no pm:dispatched + assigned card on either board has a merged Refs-only PR inside H8's 8-day window that no later Claim: or Release: answers — and the coverage pair says every candidate had its thread in hand, as the docblock predicts from H2's read. The self-test carries the shapes.

Gates. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack with no paths (the changed set derived by the tool from the merge base): 40 commands, identical to the explicit two-path derivation. All 40 run, every exit code captured before any pipe; --ran reconciles: 40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN. check:pm-dispatch-gates was run detached per its header and read to completion (1511 cases pass). check:doc-formula-expressions first answered exit 3 PREREQUISITE NOT MET (@objectstack/formula not built in a fresh worktree); after building @objectstack/formula... and @objectstack/lint... through the shared lock it ran and passed — measured, not skipped.

  • check-skill-line-ratchet: self-test 155 cases; live SKILL.md is 811 lines (ceiling 811; headroom 0), widest table row is 342 bytes (pin 342; headroom 0).
  • check-skill-id-lint: 24 files clean. check-skill-frame-sync: 4 axes, binding sentence in both copies. check-governed-prose: 2 surfaces name all 5 registered governed surfaces.
  • check-half-states --self-test: 2535 (2427 before, no case dropped).
  • check-governed-merges --test on the file list: exit 3 GOVERNED — .claude/** ×1.
  • Whole-repo pnpm lint through scripts/pm/os-verify-lock.sh on slot issue-16003: VERDICT command-exit 0 (57s hold). Not a narrowed run — the full farm.

维护者速读(草稿)

一句话:给「一张卡只落了一项」这件事补一条状态转换规则,并让巡查机器能看见没走这条规则的卡。

业务背景:并行开发常常只做卡上的一项——其余项属于别的车道或受管面。以前 PR 合入后,卡仍然显示「已派发、有人认领」,但实际上没有人在做;同一天两个席位各自即兴处理,其中一次把整张卡停在一个只有一项依赖的决定后面,剩余项白等。

规则内容:只落一项的 PR 写 Refs #N (item k)、不写 Fixes(GitHub 就不会关卡);合入即释放——同一笔写入把卡放回队列、清掉 assignee、留一行 Release: 点名已落项和余项去向;余项需要换车道或拆卡时加 pm:retriage 交分诊;余项从队列重新认领。文字与既有释放规则并列,行数不增。

机器侧:巡查脚本新增一行只报告、不改标签的 H49——卡还显示「已派发、有人」,而它最新的 Refs PR 已合入、线程上没有更新的 Release:Claim:,就点名 PR 和已落项,提示欠一次释放。不新增任何 API 读取;今天两块板都读作 0 行,是因为还没有卡处于这种形状,不是没读到。

风险与代价:规则文字压在既有行数上限内,靠折叠两行状态模型散文换来;巡查行是报告态,误报只会多一条提示、不会写标签。已顺手发现 H8 对同名分支上的 Refs PR 会再报一次(#16036,未在本 PR 处理)。

席位意见:(留空)

🤖 Generated with Claude Code

https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox

Generated by Claude Code


Generated by Claude Code

… rule, one report-only sweep row (H49)

SKILL.md (認领 family, line-neutral 811/811): a PR that lands ONE item of a card carries
`Refs #N (item k)` and never `Fixes`; its landing releases the card in the landed form —
same write back to `pm:queue` (+ `pm:retriage` when a remaining item needs a re-lane or a
split), assignee cleared, a `Release:` line naming the landed item and where the remaining
items go; the remainder is re-claimed from the queue. Paid for by two folds in the 状态模型
prose (not-planned closure; the pin-consumer exemption).

check-half-states.mjs: H49 (band `state`, report-only) — an open `pm:dispatched` + assigned
card whose newest `Refs`-linked PR is MERGED while no `Release:` or `Claim:` on the thread is
newer than that merge. `refsTargets` joins H7's extractors with H7's strictness; the PR side
is H8's merged window plus the open listing; the thread is H2's read. Coverage pair, summary
clause, registry band, 108 self-test cases (2427 → 2535, none dropped).

Tidiness, same file: H24's remedy and H47's two legs now read `RELEASE_ACT_RULE`; the two
docblocks that cited SKILL.md line numbers keep their quotes and drop the numbers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Sep 5, 2026
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 5, 2026

os-steve commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

ACCEPT — lands #16003 as ruled. Governed (mixed diff, .claude/** ×1, predicate exit 3), so this PR stays draft at the governed terminal: both approvers requested; needs-user-decision goes on the PR with the final 「维护者速读」 posted beneath this verdict.

What the seat verified, at head 023dd73f3e, in its own compare worktree (merge base 6615a024c3)

  • SKILL.md +4/−4, line-neutral at 811/811 (ratchet exit 0, pin 342 untouched): the two new lines sit beside the release line in the 认领 family — a one-item landing carries Refs #N (item k) and never Fixes; the merge is a release in the landed form (same write back to pm:queue, assignee cleared, a Release: line naming the landed item and the remainder's destination, pm:retriage when a remaining item needs a re-lane or a split, the remainder re-claimed from the queue). Paid by two folds the seat read for lost content — the not-planned closure pair keeps "no mechanism can wake it ⇒ close, reason and source in the closing comment, reopen is free, maintainer may veto"; the hold-comment pair keeps "the hold comment pre-writes the wake-up dispatch shape" and the pin-consumer exemption. Widest lines 119 / 120 bytes; the four-axis frame block is byte-identical to main at 733–752; check-skill-id-lint clean; check-governed-prose green.
  • Same-file proof: git merge-tree --write-tree --name-only of PR docs(pm-dispatch): governing-text step and protocol-baseline rule in the decision frame #15946's head against this head exits 0 with a tree id only — the two SKILL.md flights merge cleanly.
  • check-half-states.mjs +425/−10: H49 report-only in band state, over the PR side H8 already lists (merged window + open listing) and the card threads commentCache already holds — no new fetch class; refsTargets beside H7's extractor with H7's strictness; the finding quotes the new rule line and RELEASE_ACT_RULE unbroken; coverage pair + summary anchor + registry band present. Self-test 2427 → 2535 (+108, 0 dropped) in the seat's run. The two tidiness items landed: H24's remedy and H47's legs read RELEASE_ACT_RULE; the two ruled docblocks lost their drifted line numbers, quotes kept.
  • CI, seat's read 20:5xZ: 35 check runs, 24 success, 11 skipped, none failing, none pending.
  • Body line 1 Fixes #16003; skip-changeset present; the 草稿 section is in the body.

Open question answered: A. The ruling asked for H7's strictness, and a lowercase refs #N is the same relation, not a dialect.

Follow-ups graded: #16036 (H8's branch-name fallback reads a merged Refs PR as a full delivery) and #16037 (three more SKILL.md line-number citations) — same file, held behind this PR; #16037 rides as tidiness in #16036's flight.

Implemented-by: os-dev executor, flight #16003, branch claude/issue-16003-partial-dispatch-release
Reviewed-by: pm-dispatch skills seat, https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox


Generated by Claude Code

@claude
claude Bot requested review from hotlong and os-zhuang September 5, 2026 21:56

os-steve commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

维护者速读

改了什么:PM 手册补两行规则:一张卡只落其中一项时,PR 写 Refs #N (item k)、不写 Fixes(GitHub 不关卡);合入即按今天落地的「释放是显式动作」处理——同一笔把卡放回队列、清掉 assignee、留一行 Release: 点名已落项与余项去向;余项要换车道或拆卡就加 pm:retriage 交分诊,再从队列重新认领。行数不增(811/811),靠把状态模型里两对相邻规则各并成一句付账,席位逐句核过内容未丢。巡查脚本新增一条只报告不写标签的行(H49):卡仍显示「已派发、有人」,而它最新的 Refs PR 已合入、线程上没有更新的释放或认领,就点名 PR 和已落项。顺带把上一趟留下的两处内联拼写改为引用同一常量、删掉两处漂移的行号。

为什么改:spec 席位交班实测:一天里两个席位各自即兴处理「只落了一项」的卡,其中一次把整张卡停在一个只有一项依赖的决定后面,余项白等。规则本身就是已落地的释放动作,只是没人写下「合入一项也算离手」。

风险与代价(含回滚):纯规则文字 + 一条报告态巡查行,不新增任何 API 读取;40 个派生门禁全绿,巡查自测 2427 → 2535,两块板实测 0 条新报告(今日没有卡处于这种形状,不是没读到)。与同在等你的 PR #15946 同改一个文件但不同区域,merge-tree 证明无冲突。回滚 = revert 一个 commit。

席位意见:建议批准。四轴:业务——实测两次即兴处理、一次处理错;长远——复用已落地的释放动作,不新造机制;防 AI 错——把「合入一项」这个以前无规则的时刻变成有明确写法、且机器能看见没照做的卡;创业阶段——不加行、不加抓取。

你要做的:批准并入队,或人工直合。一字:是/否。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 5, 2026 22:38
@os-zhuang
os-zhuang enabled auto-merge September 5, 2026 22:38
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit 3116d24 Sep 5, 2026
41 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-16003-partial-dispatch-release branch September 5, 2026 23:50
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 needs-user-decision size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants