Repository navigation
Commit 9ee8e35
skills(pm-dispatch): dedupe at grading by file/mechanism over the open board of both repos — same finding ⇒
Fixes #18963
Clause-②: no
Governed rules layer (`.claude/skills/pm-dispatch/SKILL.md`) — draft PR,
four-piece terminal; the skills seat's contract-tier review, then the
maintainer's approval lands it. `skip-changeset`: nothing published
moves (`.claude/**` is internal agent tooling, in no package's
`files[]`).
## What changed — three rows of the 分诊 band, 812 / 812 held, no line
added, no re-wrap
The triage band's shadow rows read as a cross-repo keyword search only,
and the dedupe step that would have caught two cards on one file and one
defect — graded two days apart into two lanes by the same seat, both
dispatched, the second PR superseded — did not exist in the rules text:
a dedupe by FILE / MECHANISM against the OPEN board, own repo included,
before keywords, with the finding (not the card) as the unit. Governing
triage reading, verbatim: 「缺的不是勤勉,是一条按『文件/机制』而不是按『关键词』去重的步骤」 ·
「去重的单位是『发现』,⛔ 不是『卡』」.
| line | before (bytes) | after (bytes) |
|---|---|---|
| :366 | 「跨仓查重(shadow 检查):跟跨仓引用 + 关键词搜各姊妹仓。」 (75 B) | 「查重/shadow
检查先按文件/机制查本仓与姊妹仓 open 卡(含 `pm:dispatched`),再跟引用与关键词。」 (120 B) |
| :367 | 「shadow 命中在飞 ⇒ `Blocked-by:` 不派;open 未认领 ⇒ 先收敛成一个派发入口。」 (99 B)
| 「同文件同缺陷 = 同一发现,不分车道:证据搬到先卡,后卡关 `duplicate_of`,⛔ 不并排派发。」 (115 B) |
| :368 | 「shadow 命中已完成 ⇒ 卡可能过期。」 (47 B) | 「其余在飞 ⇒ `Blocked-by:` 不派;open
未认领 ⇒ 先并成一个派发入口;已完成 ⇒ 卡可能过期。」 (119 B) |
Band total 221 B → 354 B (+133 B) at +0 lines; every row is at or under
the 120-byte cap (120 / 115 / 119). Lines :223
(「分诊座位唯一生产:定级/路由/type/查重/shadow/`duplicate_of`…」) and every line outside
:366–:368 are byte-identical to `origin/main` — the diff is 3 insertions
/ 3 deletions on one file. The parallel-draft bands of PR #18903
(:509–:670) and PR #18921 (:162–:183) are untouched.
## What the new rows say (measured against the card's requirements)
- **By file / mechanism, before keywords, over the OPEN board of both
repos, `pm:dispatched` included** — :366: 「先按文件/机制查本仓与姊妹仓 open 卡(含
`pm:dispatched`),再跟引用与关键词」. The population is stated once, in the same
row (the PM's mechanism assumption 2, confirmed: 「跨仓」 became 「本仓与姊妹仓」 in
place).
- **The unit is the finding, not the card** — :367: 「同文件同缺陷 =
同一发现,不分车道」; the later card's evidence moves to the earlier card
(「证据搬到先卡」), the later card shuts as `duplicate_of` (「后卡关
`duplicate_of`」, the state the :136 row already binds to the `duplicate`
reason), and it is never dispatched beside the earlier one (「⛔ 不并排派发」).
- **Every hit that is NOT the same finding keeps the old trichotomy** —
:368: in flight ⇒ `Blocked-by:`, not dispatched; open and unclaimed ⇒
fold into ONE dispatch entry first; completed ⇒ the card may be stale.
「其余」 scopes the row to what :367 did not already settle, so a
same-finding sibling in flight lands on `duplicate_of`, not on
`Blocked-by:`.
## Retired clauses and their survivors (nothing retired without one)
| retired spelling | survivor |
|---|---|
| :366 「跨仓查重(shadow 检查)」 | :366 「查重/shadow 检查 … 本仓与姊妹仓」 — same name (the
:310 spelling 「查重/shadow 检查」), population widened to both repos |
| :366 「跟跨仓引用 + 关键词搜各姊妹仓」 | :366 「再跟引用与关键词」 — same two methods, now
second to file / mechanism; 「各姊妹仓」 is carried by 「本仓与姊妹仓」 earlier in the
row |
| :367 「shadow 命中在飞 ⇒ `Blocked-by:` 不派」 | :368 「其余在飞 ⇒ `Blocked-by:` 不派」
— same mechanism, scoped to hits that are not the same finding |
| :367 「open 未认领 ⇒ 先收敛成一个派发入口」 | :368 「open 未认领 ⇒ 先并成一个派发入口」 — 「收敛成」 →
「并成」 (3 B) is the only compression; 「先」, 「open 未认领」 and 「一个派发入口」 all
kept |
| :368 「shadow 命中已完成 ⇒ 卡可能过期」 | :368 「已完成 ⇒ 卡可能过期」 — folded into the
trichotomy row |
The 「shadow 命中」 subject of the old :367/:368 is now carried by :366
naming the check and :368's 「其余」 reading against :367; no row outside
the band was touched to make that binding.
`references/core-rules.md` (151 / 151): its only shadow mention is :56
「跨仓查重与 shadow 检查恒归中央 ⛔ 不下放」 — a rule about WHO runs the check (central
triage), not about its population or order, and it stays true under the
new rows (:55 already makes same-repo dedupe central too). No rule moved
there, so no twin is owed (the PM's mechanism assumption 3, confirmed by
`git grep -i shadow` on that file: 1 hit, :56).
## Reader test — the second grading, replayed under the new rows
At 2026-09-18T00:06Z the triage seat grades #18844
(`check-single-claim-paths.mjs` 401s because node `fetch` ignores
`HTTPS_PROXY`; remedy: the proxy re-exec the sibling scripts already
carry). Platform state of #18314 at that instant, from its label events:
open, `pm:queue` + `domain:spec` + `priority:p2` since
2026-09-17T10:10Z, unclaimed (`pm:dispatched` and the assignee arrived
at 05:49Z, five hours later — so at grading it was a queued sibling, not
yet an in-flight one; the dispatch text's 「`pm:dispatched` when #18844
was graded」 describes the later claim-time instant, which is the seat's
own half, #18964).
- :366 — search the open board of this repo by FILE:
`check-single-claim-paths.mjs` ⇒ #18314's title carries the file name
verbatim; it is on the open board (`pm:queue`; had it already been
`pm:dispatched`, 「含 `pm:dispatched`」 keeps it in the population).
- :367 — same file, same defect (`fetch` vs `HTTPS_PROXY`, the re-exec
remedy), a different lane (`domain:spec` vs the skills seat's filing) ⇒
「同一发现,不分车道」 ⇒ #18844's evidence (the seven-file census) moves onto
#18314; #18844 shuts as `duplicate_of` #18314; ⛔ no second dispatch, no
PR #18945.
- :368 — not reached: the hit was settled as the same finding, so
neither `Blocked-by:` nor 「先并成一个派发入口」 applies.
Outcome: one card (#18314, p2, `domain:spec`), one dispatch, one PR. The
old :366 could not reach this: its population was 「各姊妹仓」 only, and its
method was keywords.
## Gates — all at `ea2521bd9` (the final commit; the tree did not move
after these runs)
Derived from the worktree's own changeset with `node
scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (stderr: 「gate list derived from the tree of
'objectstack-ai/objectstack' at commit ea2521b」; change set: 1 path vs
merge base `26c73fb4e`): 20 commands, 20 run, every exit code captured
by redirect-then-`$?`, never through a pipe. `--ran` reconciliation: 「20
derived famil(ies) accounted for — 20 run, 0 NOT-MEASURED, 0 UNRUN」
(exit 0).
| command | exit | verdict line |
|---|---|---|
| `pnpm check:pm-skill-ratchet` | 0 | ✓
`.claude/skills/pm-dispatch/SKILL.md is 812 lines (ceiling 812; headroom
0)` · ✓ `references/core-rules.md is 151 lines (ceiling 151; headroom
0)` · widest table row 342 (pin 342) |
| `pnpm check:pm-skill-id-lint` | 0 | ✓ 27 file(s) clean (pattern
`/#[0-9]{3,}/g`) |
| `pnpm check:skill-frame-sync` | 0 | ✓ the one declared copy is
internally coherent; 4 axes; 74 markdown files scanned — four-axis block
:733–:754 md5 `3327d02c56f8a0eca88569dad2270f32` before and after |
| `pnpm check:pm-governed-prose` | 0 | ✓ 2 instruction surface(s) name
all 5 registered governed surfaces and claim no others |
| `pnpm check:nul-bytes` | 0 | OK (8891 text files, no raw ASCII control
bytes); control-byte grep of the edited file: 0 hits |
| `pnpm --filter @objectstack/lint run check:doc-formula-expressions` |
0 | ✓ self-test 58 cases · 22 record-scoped examples across 438 files /
1378 TS blocks clean · 9 TSDoc examples clean · 14 `*When` predicates
clean — after `pnpm --filter "@objectstack/lint..." build` under
`scripts/pm/os-verify-lock.sh` (VERDICT command-exit 0, 30 s held; first
attempt without the lint build exited 3 PREREQUISITE NOT MET, which is a
refusal, not a measurement) |
| `pnpm check:agent-test-spelling` | 0 | ✓ 0 violations — 515 files |
| `pnpm check:cross-package-test-inputs` | 0 | OK: 29 package(s) read
outside themselves, all declared; 255 self-test cases |
| `pnpm check:doc-authoring` | 0 | ✓ 821 pinned sites across 231 files
hold the baseline |
| `pnpm check:driver-memory-census` | 0 | OK — every declaration
ledgered, every entry live |
| `pnpm check:pm-expected-skips` | 0 | ✓ self-test 99 cases |
| `pnpm check:pm-governed-merges` | 0 | ✓ 328 assertions |
| `pnpm check:pm-half-states` | 0 | ✓ self-test 4963 cases |
| `pnpm check:refd-timer-probe` | 0 | ✓ 11 cases, negative controls
included |
| `pnpm check:watch-hint-literal` | 0 | ✓ 71 declarations across 4
rostered names |
| `node scripts/check-closing-keyword-parity.mjs` | 0 | OK (3 parsers
agree on all 9 keywords; 5 files carrying the grammar, all registered) |
| `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 | ✓ 40
assertions, 5 mutations driven to red |
| `node scripts/check-comment-mask-corpus.mjs` | 0 | ✓ 6857 files, 0
disagree, 0 unparseable |
| `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 | ✓
253 cases |
| `node scripts/pm/check-harness-current.mjs --self-test` | 0 | all 26
cases passed |
Builds were run with `OS_SKIP_DTS=1` (the formula gate imports compiled
JS, not declarations); no gate that reads `.d.ts` is in this derivation.
Repo-wide scans (`pnpm lint`) are CI's run, not owed here — this diff
touches no lintable source.
## Line budget
- `SKILL.md`: 812 before / 812 after / ceiling 812 — paid in-band by
rewriting the three rows (:366 75 → 120 B, :367 99 → 115 B, :368 47 →
119 B); the one compression is 「收敛成」 → 「并成」 in the :368 survivor.
- `references/core-rules.md`: 151 / 151 / 151 — untouched, no twin owed.
## Acceptance notes
- `origin/main` moved from `26c73fb4e` (`BASE`) to `873e0e8e2` while
this ran; `git log BASE..origin/main -- .claude/skills/pm-dispatch/` is
empty, and neither PR #18903 nor PR #18921 has landed, so no merge of
`origin/main` was performed — the branch is one commit on `BASE` and its
three-dot diff against `main` is exactly the table above. Declared here
rather than done: a merge commit would carry no content for this file.
- noted, not filed: the seat's own half (the claim-time stem scan in
`references/dispatch-runbook.md`) is #18964 and was not touched; the
runbook carries no spelling of the shadow check to drift against these
rows (`git grep -i shadow` on `references/`: only `core-rules.md:56`).
承接者: #18964.
- noted, not filed: `check-closing-keyword-parity` reports it skipped
`packages/spec/CHANGELOG.md` (6,080,453 B over its 2 MiB cutoff for
UNREGISTERED files) — the gate's declared, printed behaviour, not a
defect; 承接者:无.
- noted, not filed: `dispatch-gates --ran` accepts an exit code per
recorded line and flags a bare record as 「CLAIMED」; the record here was
re-run in that form (see the report). 承接者:无.
## 维护者速读(草稿)
**改了什么** — 分诊规则里的三行「shadow
检查」被改写为一条真正的去重步骤:定级时先按**文件/机制**、再按关键词,对**本仓与姊妹仓**全部 open
卡(含已派发的)查重;同文件同缺陷视为**同一个发现**,后卡的证据搬到先卡、后卡以 `duplicate_of` 关闭,不再并排派发。行数
812/812 不变,只在原三行内改写,没有折行凑数。
**为什么改** — 同一个文件、同一个缺陷的两张卡,被同一个分诊席在两天里定进两个车道,两边都派了 dev,第二个 PR 作废:浪费了一轮
dev。原规则只说「跨仓 + 关键词」,本仓已有的卡根本不在检查范围里。
**风险与代价(含回滚)** — 纯规则文本,不碰代码与发布包;风险是分诊多做一次按文件名的板面检索(成本很小)。回滚 = revert 这一个
commit,三行恢复原文。
**席位意见** — (留空,席位定稿成评论)
**你要做的** — 一个动作:APPROVE(受管面,需你的批准后由席位落地)。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF)_
Co-authored-by: Claude <noreply@anthropic.com>duplicate_of, never a second dispatch (#18981)1 parent 64de4c5 commit 9ee8e35
1 file changed
Lines changed: 3 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
363 | 363 | | |
364 | 364 | | |
365 | 365 | | |
366 | | - | |
367 | | - | |
368 | | - | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
369 | 369 | | |
370 | 370 | | |
371 | 371 | | |
| |||
0 commit comments