Skip to content

pm-dispatch: four gaps measured in one repo:cloud term — Part-of label leak, changeset-vs-published-package, PM gate lists as leads, and stale rulings #8000

Description

@hotlong

Filed by the repo:cloud whole-repo seat (#6026, session session_017V6EP1vMCtG26ThdcQ8bW6) at the maintainer's direct instruction (2026-08-12: 「更新最新的项目经理skills」). Routed to domain:skills per the 2026-08-11 ruling that skills get their own PM; ⛔ this seat is not dispatching it — that would put two producers in one lane.

Two binding constraints for whoever takes this:

  • model: claude-fable-5 is mandatory for any card touching .claude/skills/pm-dispatch/** (Model tiering, no downward discretion).
  • Skills updates are ADR-class: human review, human merge. ⛔ No auto-merge, no merge queue. The PM reviews and leaves the PR visibly pending, and lists it as「awaiting a human merge」in its round report.

All four items below were measured in a single term (2026-08-12, cloud rounds 1–5, 11 cards / 9 PRs merged), not recalled. Each names the specimen.


1. A Part of PR does not close its card — so pm:dispatched leaks onto an open card

Specimen: cloud#771. PR #1249 correctly used Part of (items 2 and 3 remained maintainer-owned). It merged, the card stayed open — correctly — and pm:dispatched stayed on it. The in-flight query label:pm:dispatched is:open then counted a card with no dev, no branch, and nothing in flight.

The protocol already teaches Fixes vs Part of (step 7) and already teaches that labels are the state machine. What it never says is that these two rules interact: the Fixes path self-cleans via GitHub's auto-close, the Part of path does not, and the difference is invisible until the in-flight count is wrong.

Proposed change — add to step 7's Fixes/Part of paragraph, and to the Label discipline block:

⚠️ Part of 收口的卡不会自动关,pm:dispatched 必须手工摘。 Fixes 卡由 GitHub 关闭时标签随卡一起离开在飞视图;Part of 卡合并后仍然开着,标签留在原地,于是 label:pm:dispatched is:open 把一张没有 dev、没有分支、没有任何在飞物的卡算进在飞。⇒ PR MERGED 的同一动作里pm:dispatched 换回 pm:queue(或按剩余物定级),并留一条评论写明已交付什么、还剩什么、剩下的归谁

2. The changeset requirement needs the private: false test, in the dispatch template and the ACCEPT checklist

Specimens: cloud#1243 (PR #1247) and cloud#670 (PR #1246) — both missing a changeset, in the same round. Both packages are published (@objectstack/service-cloud v4.0.5, @objectstack/service-tenant v4.1.0, both private: false), and every comparable PR in recent history carries one — including #1242, the直接前身. Without it the fix merges and is never released.

The skill mentions changesets twice, both times as prose ("a .changeset/*.md is present for anything user-visible"). "User-visible" is a judgment call a dev under time pressure resolves optimistically. The mechanical test is not in the text.

Proposed change — two places:

  • step 5 dispatch template, as a card-specific clause with an explicit判据: 「本单改动的包若 package.jsonprivate 不为 true(即已发布包),用户可见行为改动必须.changeset/*.md。判据是包的发布状态,不是改动大小。」
  • step 7 ACCEPT checklist, as its own line: 「改动触及的每个包,private: false 即已发布 ⇒ 核 changeset 在不在。⛔ 缺了不入队 —— 合进 main 却永不发布,是比不合更糟的终局:它看起来像修好了。」

Both were caught at review here, but only because the reviewer happened to check package.json. That should not be a happenstance.

3. A PM-supplied gate list is a lead, not a spec — the dev must re-derive it

Specimen: cloud#1248 (PR #1250). The dispatch named check:dts-gating, check:ee-plugin-wiring, check:ee-feature-consumers. It omitted check:route-envelope, and the change genuinely broke that gate. The dev found it only because the same dispatch also said 「Take the gate list from the tree yourself — ⛔ do not trust mine」.

The skill currently states the opposite allocation outright: 「⛔ 取数的是 PM,不是 dev」, with the reasoning that a dev must not enumerate the whole farm locally. That reasoning is sound and should stay — but it produced a single point of failure, and the skill's own text acknowledges the risk two paragraphs later (「点名的准确性从此是 PM 独担的,凭记忆点名等于把那一族漏进 CI」). This term it was not memory that failed; it was a fresh, careful read that was simply incomplete.

Proposed change — keep PM naming the families, add a cheap dev-side check:

PM 仍在派发那一刻从树上取数并点名该跑的门禁族(⛔ 不是让 dev 枚举全 farm)。但 dev 在跑完被点名的族之后,自己用同一条命令复核一次(grep -rn 'pnpm.*check:' .github/workflows/*.yml),发现 PM 清单漏掉、而自己改动确实触及的门禁族,补跑并在报告里点名。这是 O(1) 的一次 grep,换掉「PM 独担点名准确性」这个单点。实测:#1250check:route-envelope 正是这样被抓到的,而 PM 的清单是当天现取的、不是凭记忆。

4. The stale-premise check must cover rulings, not just issue bodies

Specimen: cloud#987. A maintainer ruling of 2026-08-10 moved the card to pm:queue on the grounds that 「the disable-with-tombstone is the dispatchable piece」. That piece had already shipped on 2026-08-04 via cloud#1119 / PR #1120 — six days before the ruling. Dispatching as written would have returned premise_still_valid: false and no PR.

The skill's stale-premise machinery is aimed squarely at issue bodies ("Issues describe the repo as of their filing date"). Rulings feel authoritative and recent, so they are read as current. They are not: a ruling is a judgment made against a repo state, and main moves ~18 merges a day underneath it too.

Proposed change — extend the step-1 stale-premise paragraph:

裁决同样有前提,而且比正文更容易被当成现成事实。 裁决写得权威、日期又近,于是「照裁决执行」感觉上不需要复查 —— 但裁决是针对某个仓库状态下的判断,而 main 在它下面照样一天走 ~18 个合并。⇒ 派发前把裁决点名要做的那件事拿到 origin/main 上核一遍它还没被做掉。判据与正文那条一样:成本是几分钟,不核的成本是一整次 agent 运行。标本 cloud#987 —— 08-10 的裁决要求的动作,08-04 就已经落地了。


Also worth considering (lower confidence, listed for the skills seat to judge)

  • Measurement-first cards worked three times in a row (cloud#1232 → chore(deps)(deps): bump mongodb from 6.21.0 to 7.2.0 #1239chore(deps)(deps-dev): bump the development-dependencies group across 1 directory with 16 updates #1243): a card that asks a question, with the fix conditionally authorised by the measurement's result, and ⛔ no fix written before the measurement exists. The third one found a probe the card never listed (cross-tenant CREATE, which needs no row id) precisely because the dev was measuring rather than implementing. This may deserve a named pattern in step 5.
  • Positive controls as a first-class ACCEPT line. Best specimen this term is a negative result: cloud#1243's sys_environment comparison came back 405 — and its positive control failed too, so the dev recorded it as INCONCLUSIVE instead of banking a green as a tenant wall. That is the case that justifies the rule, and it is a better teaching example than the passing ones.

Source: repo:cloud seat post #6026. Happy to supply the full round-by-round evidence for any item.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions