Skip to content

Commit 3230308

Browse files
docs(pm-dispatch): a fork PR closes in the same stroke that closes its card (#19895)
Fixes #19883 Clause-②: no ## 维护者速读(草稿) **改了什么**:三份 PM 协议细则文件,共 +5 / −3 行。① 采纳了 fork PR 的卡,正文恒带一行行首 `Adopts: #M`(M = 那个 fork PR 的号),立卡那一笔就写;② 落地收口清单(确认 MERGED、给卡收口的那一笔)加一行:卡带这一行,就在同一动作里给 fork PR 贴致谢 + 落地链接并关闭它;需求被拒(卡关 not_planned)时,关卡那一笔同样贴致谢与谢绝理由并关闭;③ 分诊席每次读的职责文件加一行:每次 fire 扫开着的 fork PR,没卡就立卡,卡已关而 fork PR 还开着就补关。两份有行数上限的文件各加一行、各退一行同文件里已原样写着的重复内容,上限不动。 **为什么改**:维护者原话「这种pr应该写个评论直接关闭。」「"这种 PR"都应该这样处理,开一个 skills 卡片」。外部贡献者的 PR 在需求已由内部 PR 落地后被晾了两天没人关:这条规则只写在一份落地时没有席位会读的文件里,落地收口清单里没有这一步,卡上指向 fork PR 的只是一段散文。另外核实发现,「分诊每次 fire 扫 fork PR」这一步本身也不在分诊席每次读的职责文件里,只在那份没人读的文件里。 **风险与代价(含回滚)**:纯文本改动:不加门禁、不加脚本、不加巡查行、不动行数上限。代价是分诊每次 fire 多一次「列出开着的 PR」的读取(当前 23 个开放 PR,0 个 fork),以及卡正文多一种行首拼写 `Adopts:`。退掉的两行,其每一句在同一文件里都还在(逐条对照见下文表格)。回滚 = revert 这一个 squash commit。 **席位意见**: **你要做的**:无需点击。本 PR 只碰 `.claude/**`,属 Tier S,由 skills 席在席内契约复核 PASS 后走队列落地。若不希望分诊每次 fire 都扫 fork PR(只保留落地那一笔),在本 PR 评论一句即可,删掉 triage 那一行。 ## What this PR does Three files under `.claude/skills/pm-dispatch/references/`, +5 / −3. 1. **`external-contributions.md`** (the fork-PR playbook) - Step ①: the card behind a fork PR always carries a line-start `Adopts: #M` (M = the fork PR's number). It is written in the stroke that files the card, or that attaches the fork PR to a card that already exists. The landing closeout and the triage catch-up read only this line. - Step ③: the line "after the internal PR lands, close the fork PR with thanks and the landing link" becomes: the stroke that closes the card also closes the fork PR its `Adopts:` line names. On `completed` the closing comment carries thanks and the landing link. On `not_planned` it carries thanks and the reason the need was declined. The declined case had no text before. - The sentence that limits a seat's comments on a fork PR is rewritten, not worked around. It used to say "only these two kinds" after naming review comments and the close. It now names them: apart from step ①'s fixed comment, a seat writes review comments and the closing comment, and nothing else. The declined form is the closing comment's second form, not a third kind. 2. **`landing-operations.md` §B, the MERGED closeout**, the home of "the landing stroke": one line directly after the `Part of` / `Fixes` closeout line. A card closing `completed` with an `Adopts:` line means the same action posts thanks and the landing link on that fork PR and closes it. 3. **`triage-duties.md`**: one line at the end of the backlog-sweep block. Every fire also scans open fork PRs. No card: file one per step ①. Card already closed: close the fork PR per step ③. ## Why this shape — the four axes The card offers two candidates, A (at the landing step) and B (in the triage sweep), and leaves the shape to the seat. `SKILL.md`'s order for a failure fix is: remove the construct that allows the error, make the right form the only spelling, and only then add a check. - **Remove the construct.** The miss was allowed because the close was a separate, later act. It was written only in `external-contributions.md`, and no seat reads that file at MERGED. The landing seat reads `landing-operations.md`, whose closeout list had no fork-PR item. The card named its fork PR only in free prose: an "Adoption of PR" section and a promise to close it "in the landing stroke". Shape A removes the construct: the close is now an item of the closeout list itself, in the same action that closes the card. - **One spelling.** `Adopts: #M`, line-start and undecorated, the same form as `Blocked-by:`. It has three named readers: the landing closeout, the triage catch-up, and step ①'s "does this fork PR already have a card". Measured on the one case: the fork PR's timeline carries **10** cross-references from other issues and PRs, one of them its card. The line is what picks the card out of the ten. - **Check.** None is added: no gate, no half-state row, no ratchet. Neither ruling sentence names one, and a new gate defaults to no. B is one prose duty line on a scan the triage seat already owes by step ①. It is not a patrol row. - **Why B at all, with A in place.** The declined case has no other reader. A `not_planned` close happens in several seats' flows: a triage first-touch close, a decision-box answer, a falsified premise. The one file every seat reads is `SKILL.md`, which sits at 319 / 319 and is held by PR #19890. The triage fire is the one reader that sees every closed card's fork PR, and it is also the backstop if a landing stroke misses one. - **Real business need (measured).** Fork PRs are rare: 1 among the 500 most recent closed PRs (created 2026-09-17T18:37Z .. 2026-09-23T14:41Z). That one is the fork PR that was missed. 0 of 23 open PRs are forks at 2026-09-23T15:29:57Z. The maintainer's word covers every such PR, and the one miss cost an outside contributor two days of silence. - **Startup focus.** +2 lines net across the corpus. Both ceilinged files stay at their ceilings: `landing-operations.md` 101 / 101, `triage-duties.md` 120 / 120. `external-contributions.md` goes 16 → 18; that file is not in the ceiling map. - **Rejected alternative.** Teach `scripts/pm/close-cards.mjs` to read `Adopts:` and refuse a card close that does not also close the fork PR. That is structurally the strongest shape, but it is a new mechanical check that no maintainer word names, and it is outside this card's claimed file surface. ## Premise check, on `origin/main` at `dabf8d7` - **Confirmed.** The rule's only carrier was `external-contributions.md` (the old line 13), as prose. Fork PR #19342 stayed open from the internal landing to 2026-09-23T14:42:37Z. Card #19489 is `closed` / `completed` (2026-09-21T10:32:18Z). PR #19534 merged 2026-09-21T10:32:16Z as `2aac821a8c`. - **Correction to one reading in the dispatch.** `triage-duties.md` had **no** fork-PR scan line. `git grep -c -i fork dabf8d7 -- …/triage-duties.md` exits 1 (zero hits). The control on the same tree and term, `…/external-contributions.md`, hits 8 and exits 0. Step ①'s scan lived only in `external-contributions.md`, and `SKILL.md`'s phase-file index names no reader for that file. The pre-relocation `SKILL.md` (`f151ef2^`) pointed at it only from the enqueue-and-landing section. So candidate B's premise, "step ① already scans open PRs on every fire", held on paper only. The new triage line is what gives step ① a reader. - The dispatch was cut at `e9eb2244d5`; this worktree starts at `dabf8d7`. `git diff --stat e9eb224 dabf8d7 -- .claude/skills/pm-dispatch/` is empty. ## Retired lines: every clause is still stated in the same file | retired | where each clause still stands | |---|---| | `landing-operations.md` old :19, "follow to MERGED; after enqueue the watch belongs to the lane PM's landing window: every round read the queue branch and `origin/main`" | §B heading 「跟到 MERGED 为止;入队后的看护归车道 PM 落地窗口」 and §B 「确认 MERGED 要两个读数:每轮同时读队列分支与 `origin/main`。」 | | `triage-duties.md` old :8, "the tool-loading discipline binds only the triage fresh session's opening; execution seats and devs are not bound" | line 3: the file is the triage seat's alone. Line 6 「fire 开局只按名加载…判定本轮有活才加载其余」 and line 7 「⛔ 分诊 fresh session 开局不做泛关键词 ToolSearch」 are both scoped to the opening already. | Neither retired line is quoted anywhere else in the tree (`git grep` over both texts: the only hits are the lines themselves). ## Gates, on `4a3242c` `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives 18 families from the 3 changed paths, merge base `dabf8d795`. All 18 were run, each exit captured before any pipe. `--ran` verdict: `✓ dispatch-gates --ran: 18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED (a DERIVED zero — all 18 recorded an exit code and none of them is 3).` - `pnpm check:pm-skill-ratchet`: exit 0. `landing-operations.md is 101 lines (ceiling 101; headroom 0)`; `triage-duties.md is 120 lines (ceiling 120; headroom 0)`. Every changed line is 120 bytes or less. - `pnpm check:pm-skill-id-lint`: exit 0, `34 file(s) clean (pattern /#[0-9]{3,}/g)`. No rule line cites a tracker number. - `pnpm check:pm-governed-prose` (named by the dispatch, outside the derivation): exit 0. - `pnpm check:nul-bytes`: exit 0, `no raw ASCII control bytes`. - `pnpm check:skill-frame-sync`: exit 0. - `pnpm check:pm-half-states`: exit 0, `4912 cases pass`. - `pnpm check:pm-governed-merges`: exit 0. - `pnpm --filter @objectstack/lint run check:doc-formula-expressions`: the first run exited 3 (PREREQUISITE NOT MET, `@objectstack/formula` and `@objectstack/lint` not built). After `turbo run build` for those two packages under the verify lock (VERDICT command-exit 0), the re-run exited 0. - The other 10 derived families (closing-keyword parity ×2, comment-mask corpus, harness-current, agent-test spelling, cross-package test inputs, doc authoring, driver-memory census, gitlink, refd-timer probe, watch-hint literal): exit 0. No package is touched, so there is no build closure and no package test to run. Nothing here is published (`.claude/**`), so this PR carries `skip-changeset`. ## Acceptance notes - `SKILL.md`'s phase-file index names no reader for `references/external-contributions.md`. After this PR both executing seats' files point at it (`landing-operations.md` line 7, `triage-duties.md`), so no index row is owed. Carrier: none. - `references/external-contributions.md` is not in the line ratchet's ceiling map, while the other `pm-dispatch` reference files are. Stated as a fact, not a gap. Carrier: none. - A fork-PR card that closes as `duplicate` is not covered: its `Adopts:` line would need to move to the surviving card. There are 0 instances. Carrier: none. - `check-half-states.mjs`'s H65 message quotes `triage-duties.md` as 「选层按 fire 时刻,⛔ 不用计数器;简报写明本轮跑的层」, but the file says 「选层按 fire 时刻 ⛔ 不用计数器,简报写明层」. This is report-only message text. Carrier: none. --- _Generated by [Claude Code](https://claude.ai/code/session_01A22sUB3mUWs6M36VgfijBq)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 029d8a4 commit 3230308

3 files changed

Lines changed: 5 additions & 3 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
席位永不翻 ready、入队、挂 auto-merge 或批准它;席位复审是必需输入,⛔ 永不是放行许可。
55

66
- ① 先立卡:分诊 fire 扫 open PR 中 head 仓 ≠ base 仓者;无卡的按 PR 正文立卡,常规定级。
7+
- 该卡正文恒带行首 `Adopts: #M`(M = fork PR 号),立卡或挂接那一笔写;收口与补关只认这一行。
78
- 无卡的 fork PR 在板上不存在;PR 上只贴一条固定评论,原文:
89
`this repository works card-first; filed as #N; this PR stays a draft until the card is graded`
910
- ② 人裁的是问题不是代码:普通可复现缺陷路由到席位,不经维护者。
1011
- 地板项(安全/权限边界、契约、功能)作业务问题进决策箱,与任何卡同批裁。
1112
- ③ 席位采纳 diff,永不落地 fork PR:内部分支 cherry-pick 或重写,`Co-authored-by:` 贡献者。
1213
- 门禁推导、达档复核与队列同内部工作;对贡献者的要求只作 fork PR 上的 review 评论。
13-
- 内部 PR 落地后关闭 fork PR,附致谢与落地链接;席位在 fork PR 上只写这两种评论。
14+
- 关卡那一笔同时关 `Adopts:` 所指 fork PR,⛔ 不拆到事后:completed 附致谢与落地链接。
15+
- not_planned 附致谢与谢绝理由;除 ① 的固定评论,席位在 fork PR 上只写 review 与关闭评论。
1416
- ④ fork 的 CI 席位永不代批准运行;head 上 0 条 check run 读作 NOT MEASURED,⛔ 永不读作绿。
1517
- 机器面:`check-governed-merges.mjs --pr <n>` 对 head 仓 ≠ base 仓走 H(人合)出口;head.repo 为空同。
1618
- 供应链条款(fork 带来的依赖、lockfile、workflow、scripts)延后:第一张此类 fork PR 出现时再立。

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
- 外部评审链降为可选事后审计,非放行前提。
1717
- 契约复核的适用面、资格与归属见 `references/contract-review.md`。
1818
- 碰生成物的 PR 入队前先同步 + 整体重生成:四步序 `bash scripts/pm/os-regen-merge.sh`。
19-
- 跟到 MERGED 为止,入队后的看护归车道 PM 的落地窗口:每轮同时读队列分支与 `origin/main`。
2019
- 默认分支 push 即触发对外部署的仓,只在验证层存在时才合并;无验证层 ⛔ 不合并。
2120
- 验证层 = 部署以 CI 为闸、发布后探测线上面、失败自动回滚并立卡。
2221
- 该类仓的落地判据是已发布且探测通过,⛔ 不是 MERGED;跟到发布为止。
@@ -66,6 +65,7 @@
6665
- ② enable-auto-merge 调用根本没落地,重发与效果验证序列见 `platform-readings.md`。
6766
- ③ PR 碰 `.github/workflows/**` 而 token 缺 workflows 权限。
6867
- 确认 MERGED 同一动作里给 `Part of` 卡收口、`Fixes` 卡代关但标也须摘,⛔ 不拆到下轮巡检。
68+
- 卡以 completed 关闭且带 `Adopts:` 行 ⇒ 同一动作在所指 fork PR 贴致谢与落地链接并关闭它。
6969
- `Part of` 卡开着不摘 `pm:dispatched`,无在飞物的卡就永远算进 `label:pm:dispatched is:open`。
7070
- 摘标换回 `pm:queue` 或按剩余物定级,加一条写清交付了什么、剩下归谁的评论。
7171
- 同刻读相关卡 `closed_by_pull_requests`,确认没有卡被正文闭合关键词误关。

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
- 发版板(无板仓跳过)、查重/shadow 检查与代裁通道全仓照跑。
66
- fire 开局只按名加载互斥检查所需工具,`ToolSearch` 用 `select:`;判定本轮有活才加载其余。
77
- ⛔ 分诊 fresh session 开局不做泛关键词 ToolSearch;可验判据:空转轮 ~4 万 token 以内。
8-
- 工具加载纪律只约束分诊 fresh session 的开局;执行座位与 dev 不受约束。
98
- 两级盘点:小时轮以 `since` 窗口读增量,锚 = 座位贴上一份收班简报的时间戳。
109
- 查重缓存住席内:新会话全量拉,其后 `since` 增量;⛔ 不建分支、不共享、不依赖备份。
1110
- 每日一 fire 跑全仓全量对账并归集日频职责;选层按 fire 时刻 ⛔ 不用计数器,简报写明层。
@@ -21,6 +20,7 @@
2120
- 窗口耗尽未跑完的,下一 fire 从标签状态断点续跑。
2221
- 排序:队列每轮清空时最新优先;清不空的那一刻起改最老优先。
2322
- 裸卡数 >15 ⇒ 下一 fire 整轮改域分批集中模式,每批 ≤5 张同族卡,照样跑到清空。
23+
- 每 fire 同扫 open fork PR:无卡按 `external-contributions.md` ① 立卡;卡已关的按其 ③ 补关。
2424
- 紧急卡直接分诊:维护者点名或 p0 嫌疑 ⇒ 立即起默认判断档分诊子代理,不等定时轮。
2525
- 紧急分诊的授权面 = 分诊本身,⛔ 不写码不认领;产出落卡,与定时轮分诊同格式同效力。
2626
- 跨仓 pin 链的窗口级兜底也在 sweep,⛔ 只立单不执行 bump。

0 commit comments

Comments
 (0)