Commit f62787c
docs(pm-dispatch): add the witness-grep leg to the in-flight overlap interception (#17956)
Fixes #17935
The in-flight overlap interception asked exactly one question — do the
file faces intersect — and
the card measures a coupling that question cannot reach: two PRs, each
green on its own, red only
once both sit on one tree, because one PR's assertion NAMES a row the
other deletes. This lands the
second leg the card's one-line criterion asks for.
## The rule as landed
`.claude/skills/pm-dispatch/references/dispatch-runbook.md`, section
〈派发词构造细则〉, the new line
sitting directly under the file-face leg it extends:
```text
- 在飞重叠每轮拦截:main 新落 PR 与在飞申报文件面求交,相交即发四句警告。
- 另一腿:本轮删/改名标识符 grep 每个在飞 PR 的 diff,命中同发,⛔ 不因文件面不相交跳过。
- 四句:合 main 重跑测试矩阵、读对方 diff 重划边界、只补它没覆盖的、被完全覆盖就停下。
```
Measured against the card's criterion (quoted verbatim, untranslated):
> 在飞重叠的检查项里,除文件清单外出现一条:核对是否有任一在飞 PR 的**断言点名了本轮 diff 删除或改名的
> 符号 / 行 / 标识符**;⛔ 且该条不得以文件面互不相交为由跳过。
- it greps against each in-flight PR's **diff**, not its file list — the
cheapest of the two shapes
the card suggests, and it needs no new tool;
- 「命中同发」 gives a hit the same disposition an intersection gets: the four
sentences, defined on
the next line, so the two legs converge on one action;
- 「⛔ 不因文件面不相交跳过」 puts the card's prohibition in the rule's own text, so
the skip is
refused where a seat reads it, not only in the card that filed it.
## Line budget — 241 / 241, net 0
| | lines | bytes | note |
|:--|--:|--:|:--|
| ceiling (`check:pm-skill-ratchet`) | 241 | — | headroom 0 |
| before | 241 | 17542 | |
| after | 241 | 17554 | +12 bytes, 0 lines |
| added | +1 | 118 | the new line, under the 120-byte cap |
| deleted | -1 | 106 | the density payment, below |
The payment is a deletion inside the same section, not a re-wrap: the
diff is exactly one removed
line and one added line, and no other line was re-flowed. The line
deleted is
```text
- ⛔ 不在消费者侧打补丁;只写一个路径名,是要求 dev 在守约与修对之间二选一。
```
and both of its clauses survive without it:
- 「⛔ 不在消费者侧打补丁」 is already stated **in `SKILL.md` line 549** —
「文件面写两句:预期落点;生产者在别包时修生产者侧并报落点,⛔ 不在消费者侧打补丁。」 — and again in
AGENTS.md Prime Directive 12 ("never add a lenient alias or `??`
fallback in a consumer").
- 「只写一个路径名,是要求 dev 在守约与修对之间二选一」 is a rationale tail for the rule stated
two
lines above it (「文件面两句原文照抄」), which already requires both sentences to
be copied.
Those are the two categories this file's own ceiling comment names as
removable, quoted from
`scripts/pm/check-skill-line-ratchet.mjs` at the `dispatch-runbook.md`
entry: "provenance
narratives, incident post-mortems and rationale tails leave the corpus;
one rule per ≤120-byte
line, no rule already stated in SKILL.md".
## Acceptance greps — both directions
Run in the worktree at this branch's head against `origin/main`:
| check | before | after |
|:--|:--|:--|
| `不因文件面不相交跳过` in the runbook | 0 (exit 1) | 1 (exit 0) |
| literal control `在飞重叠` in the runbook | 1 | 1 |
| `git diff --stat origin/main -- .claude/skills/pm-dispatch/SKILL.md` |
— | empty (SKILL.md untouched) |
| `wc -l` on the runbook | 241 | 241 |
| `LC_ALL=C awk 'length($0)>120'` over the runbook | prints nothing |
prints nothing |
| control-character self-scan over the runbook | clean | clean |
SKILL.md is deliberately untouched: it is serial with another in-flight
card this wave, and a
pointer there, if it is ever wanted, rides that file's next PR.
## The new rule, applied to this PR
The leg this PR adds is cheap enough to run on the PR that adds it. This
diff deletes exactly one
line; its distinctive identifiers were grepped against the diff — not
the file list — of all 26
open PRs in this repository:
| grepped identifier | hits across 26 open PR diffs |
|:--|:--|
| `只写一个路径名` | 0 |
| `二选一` | 0 |
| `不在消费者侧打补丁` | 0 |
| positive control `⛔` (CJK rule text, same corpus, same grep) | 21 of
26 |
The control is what makes the three zeros a reading rather than a broken
grep. Same pass re-checked
the serial constraint the claim comment declared: no open PR touches
`dispatch-runbook.md`.
## The second shape — recorded, NOT built
The card offers a stronger shape that this PR deliberately does not
implement:
> 更强的形状:凡编辑「在缩小的表」(ledger / 豁免表 / drift 表)的 PR,在派发令里互相点名,因为这类表的
> 对照**必然**点名其成员。
It is recorded here as an option, not a deliverable. It costs a standing
classification (which
tables count as shrinking tables) that the grep leg does not need, and
the grep leg already covers
the measured instance. If the grep leg proves to be run inconsistently,
this is the escalation.
## Adjacent rules that do NOT already cover this
`SKILL.md` line 479 — 「本卡 pin 断言兄弟卡在改的行为 ⇒ 派发令注明」 — is the mirror
direction (this
card asserts on a sibling's behaviour) and fires only when triage
already knows the pair. Line 450
— 「文件面不相交只保证文本可合并;跨文件语义耦合由队列 CI 逮住,⛔ 不读作不可能冲突」 — names the
coupling but routes it to queue CI, which is precisely the expensive
half the card measured: the
queue does catch it, on the second PR, as a red. Neither is a mechanical
pre-check, so this is not a
duplicate rule.
## Gates
Derived from the tree with `node scripts/pm/dispatch-gates.mjs
--commands --repo
objectstack-ai/objectstack` (no paths passed; derived at commit
`da4eee77`), all run, reconciled
with `--ran`: **14 derived, 14 run, 0 NOT-MEASURED, 0 UNRUN** — a
derived zero, every family
carrying a recorded exit code.
```text
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
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-governed-merges :: 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-governed-prose :: exit 0 (named by the dispatch, outside the derived 14)
```
`check:doc-formula-expressions` first returned exit 3 — PREREQUISITE NOT
MET, the gate's own
"nothing was measured" code, because `@objectstack/formula` and
`@objectstack/lint` were unbuilt in
a fresh worktree. Built through the shared verify lock
(`scripts/pm/os-verify-lock.sh`, slot `issue-17935`, command exit 0) and
re-run to a real exit 0.
The repo-wide sweep (`pnpm lint` and the whole gate farm) belongs to CI,
not to this worktree.
No changeset: `.claude/**` ships nothing — the path is not inside any
package's published `files[]`
— so this carries the `skip-changeset` label.
## 维护者速读(草稿)
**改了什么** —— PM 派发手册里「在飞重叠」的拦截从一问变成两问:除了文件面求交,还要把本轮 diff 删除或
改名的标识符,拿去 grep 每一个在飞 PR 的 diff(不是它的文件清单),命中与相交同样发那四句警告,并且
⛔ 不许以「文件面互不相交」为由跳过这一问。全文只动这一个文件的两行(加一行、删一行)。
**为什么改** —— 实测到一次两个 PR 各自全绿、合到一棵树才红:一个 PR 的测试点名断言某一行存在,另一个 PR
正好把那一行删了。两个席位都按现行规则查了文件面,结论都对 —— 规则问的问题本身答不到这个耦合。这个形状
不罕见:凡 ledger、豁免表、drift 表这类「在缩小的表」,它们的对照**必须**点名成员,否则对照没有意义。
**风险与代价(含回滚)** —— 代价是每轮派发多一次 grep,没有新工具、没有新门禁、没有新脚本。规则面净增
零行:新增那一行由同节内一行的删除买单,被删那行的禁令原文仍在 `SKILL.md` 第 549 行与 AGENTS.md 第 12 条
里,余下半句是上两行已有规则的理由尾巴。回滚 = 还原这一个文件的两行,不牵动任何代码、门禁或产物。
**席位意见** ——
**你要做的** —— 只需判断两件事:① 这一问是否值得每轮都问(代价是一次 grep);② 那条被删的行,它的禁令
落在 `SKILL.md` 与 AGENTS.md 上是否够。若都同意,人工合入即可 —— 这是受管面(`.claude/**`),按
Prime Directive 14 由维护者手工确认合并,本 PR 全程保持 draft,席位不翻 ready、不排队、不开自动合并。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 45b90b6 commit f62787c
1 file changed
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
210 | 210 | | |
211 | 211 | | |
212 | 212 | | |
213 | | - | |
214 | 213 | | |
215 | 214 | | |
216 | 215 | | |
217 | 216 | | |
218 | 217 | | |
219 | 218 | | |
220 | 219 | | |
| 220 | + | |
221 | 221 | | |
222 | 222 | | |
223 | 223 | | |
| |||
0 commit comments