Repository navigation
Commit 8cfa07a
feat(pm): the Prior rulings read: line covers the card’s own thread — tool corpus half and charter :339 in one batch (#19021)
Fixes #18993
Clause-②: no
`scripts/pm/check-prior-rulings.mjs` answered the `Prior rulings read:`
line out of three tree corpora (`docs/adr/**`, `AGENTS.md`,
`packages/spec/src/**`) while `SKILL.md:339` required it to cover 「已裁卡」
— a card's own thread, where the cheapest ruling lives, was in no
corpus. Measured consequence on #17518: a director-seat ruling
(5651572469, 2026-09-13) was followed by a retriage that re-presented
the card as unruled, a second maintainer ruling with a different
prescription (5716259259, 2026-09-17), and a dispatch round that ended
with zero diff. This PR makes the card's own thread a corpus half of the
tool and rewrites the charter line in the same batch, so the requirement
and the tool never diverge again.
## What changed
**`scripts/pm/check-prior-rulings.mjs`** (report-only, no write path,
unchanged exit register)
- Every `--card` run now reads the card's comment thread over the board
route the file already uses (its own proxy re-exec guard, taken once,
never on `--self-test`), one paged `GET
…/issues/{n}/comments?per_page=100&page=P` walked by page number to a
short page, bounded at `MAX_THREAD_PAGES` (50), reconciled against the
card's `comments` field.
- A ruling comment is recognised by the **shape of its first content
line**, three measured shapes: the charter's `Ruling: batch #n item k …`
(`ruling-line`), the pre-charter `## Ruling recorded — …` heading
(`ruling-recorded`, the shape 5651572469 is written in), and `Ruling
addendum …` (`ruling-addendum`). Every hit prints its id, author,
timestamp and shape.
- Two measured near-misses are pinned OUT and listed uncounted
(`**Ruling C landing step** — …`, `## Ruling A's patch is falsified …`).
- The paste line gains a `thread:` clause with three spellings kept
apart: `thread: 2 ruling(s) (id, id)`; `thread: none` (read, no ruling);
`thread: not read (no --card)` (a `--terms`-only run). A thread that
could not be read is the WHOLE line `unresolved`, exit 3, exactly as an
unreadable corpus — never `none`.
- The report prints the read's cost (pages, bytes, ms) and, when a
ruling stands, the 〈升级与决策〉③ warning; with two or more rulings it says to
read every one and to return a disagreement to the director seat.
- Self-test: 99 → 144 cases (the three shapes, the near-misses,
first-content-line anchoring, the fixture thread with two rulings
oldest-first, `none` / `not read` / `unresolved`, and an offline paged
board: two pages, one page, a 403, no token, the page bound, a network
failure, a malformed page; plus a structural pin that no `Link` header
is followed). `check-half-states.mjs`'s source-text pin on the line's
writer (H62) still holds: 4963 cases pass.
**`.claude/skills/pm-dispatch/SKILL.md` :339** — rewritten in place, net
0, 120 bytes → 120 bytes, 812 / 812 lines before and after:
> before: `- 方向落在 ADR/已裁卡/不可重裁规则:贴 `check-prior-rulings.mjs` 的 `Prior
rulings read:` 行,或不点。`
> after: `- 方向在 ADR/本卡裁决/不可重裁规则:贴 `check-prior-rulings.mjs` 的 `Prior
rulings read:` 行,或不点。`
The three places a direction may already be ruled now read exactly as
the tool's line covers them: an ADR, a ruling on this card, a
non-re-rulable rule.
**`.claude/skills/pm-dispatch/references/decision-analysis.md` :45** —
the human-facing statement of the same line shape (the tool's
`formatPasteLine` docblock names it as the twin), rewritten in place,
113 → 120 bytes, 50 / 50 lines:
```
before: - 四棱后一行:`Prior rulings read: TERMS → N hits; ADR-xxxx Dn|none`;未搜成整行写 unresolved。
after: - 四棱后:`Prior rulings read: TERMS → N hits; ADR Dn|none; thread: K|none`;未搜成整行写 unresolved。
```
(The file spells the placeholders in angle brackets; they are written as
capitals here because the platform mutates angle-bracket fragments in a
body.)
This file was not in the dispatch's named surface; it is included
because it states the line's shape and would otherwise be the next 「a
requirement and a tool that cannot answer it」 pair. No open PR touches
it (file lists of the 14 skills-lane PRs read 2026-09-18T11:3xZ).
## Hypotheses measured
1. **Ruling shape is stable; author is not.** Census over 1,100 comments
(REST, reads only): 2026-09-11T06Z–09-13T21Z (600): `## Ruling recorded
— …` 22 (os-tesla 19, claude[bot] 3), `Ruling: …` 4 (os-tesla 2,
claude[bot] 2); 2026-09-18T04Z–11Z (500): `Ruling: …` 26 (hotlong 26),
`Ruling addendum` 2 (hotlong). The charter line (`director.md`:
「裁决评论首行固定一条:`Ruling: batch #N item K · letter X · maintainer 「VERBATIM」
UTC`」 — placeholders capitalised here, the file spells them in angle
brackets) landed 32a3214 2026-09-13T11:27Z; the heading shape is the
same seat's earlier spelling. The fallback the dispatch named — 「every
comment by a `GOVERNED_APPROVERS` login」 — is falsified: it would have
missed every 2026-09-13 ruling, 5651572469 included. A quoted
「同意」/「agreed」 predicate hits 63 of 500 recent comments (claims, ledgers,
dev reports) and names nothing.
2. **Cost, the tool's own meter** (node `fetch` on the re-exec'd route,
pages serial, 2026-09-18T11:3xZ):
| thread | comments | pages | bytes | thread read | whole run | rulings
/ near-misses |
|---|---:|---:|---:|---:|---:|---|
| #17518 (the incident) | 17 | 1 | 141,623 | 347–369 ms | 2,353 ms
(before: 2,001 ms) | 2 / 0 |
| #12708 (director seat post) | 372 | 4 | 2,212,674 | 1,984 ms | 3,669
ms | 0 / 0 |
| #6015 (longest thread on the board) | 962 | 10 | 7,365,556 | 4,431 ms
| 6,254 ms | 0 / 0 |
`curl` on the same pages reads ~10% more bytes (156,874 on #17518):
GitHub pretty-prints for a `curl/*` User-Agent and minifies for any
other, measured directly; the tool reports what its own route pays. The
fixed cost per dedupe on the incident card is one page and about a third
of a second.
3. **The line, before / after, on `--card 17518` through the proxy**
(exit 0 both):
```
before: Prior rulings read: spec,assembledpackagebodyschema,declares,callable,custom,branches,inert-json,artifact,hold,schema,embedding,loses (+1 more) → 117 hits; ADR-0087 D4, ADR-0130 D4, ADR-0029 D6, ADR-0039 D7, ADR-0056 D5, ADR-0076 D3, ADR-0076 D9, ADR-0087 D1, ADR-0087 D3, ADR-0087 D7
after: Prior rulings read: spec,assembledpackagebodyschema,declares,callable,custom,branches,inert-json,artifact,hold,schema,embedding,loses (+1 more) → 117 hits; ADR-0087 D4, ADR-0130 D4, ADR-0029 D6, ADR-0039 D7, ADR-0056 D5, ADR-0076 D3, ADR-0076 D9, ADR-0087 D1, ADR-0087 D3, ADR-0087 D7; thread: 2 ruling(s) (5651572469, 5716259259)
```
and the report's thread block:
```
the card's own comment thread (ruling comments, by first-line shape): 2 ruling(s) of 17 comment(s) — read in 1 page(s), 141,623 bytes, 347 ms
5651572469 os-tesla 2026-09-13T06:10:22Z [ruling-recorded] Ruling recorded — B: `packages/spec` declares the inert-JSON artifact stage; the read A…
5716259259 hotlong 2026-09-17T14:41:18Z [ruling-line] Ruling: batch #149 item 1 · letter B (artifact-stage JSON-only variants of `FlowFunctio…
```
4. `--terms single,posture,tenant` (no card): exit 0, line ends `;
thread: not read (no --card)`.
## Gates
Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` from the worktree (three-dot change set: the
three files above): 40 families, every one run, exit codes captured
redirect-then-`$?`, reconciled with `--ran` (`40 derived, 40 run` after
the one PREREQUISITE NOT MET was cleared by its declared build:
`check:doc-formula-expressions` exited 3 for an unbuilt
`@objectstack/formula`; built under the verify lock, re-run, exit 0).
`check:pm-dispatch-gates` exceeds the foreground cap and ran detached
(`tail --pid`): 779 s, `1849 cases pass`, exit 0. Also: `node
scripts/pm/check-prior-rulings.mjs --self-test` 144 pass;
`check:pm-skill-ratchet` ✓ (812 / 812, 50 / 50, widest rows unchanged);
`check:pm-skill-id-lint` ✓ 27 files clean; `check:nul-bytes` ✓;
`check-governed-merges.mjs --test` over the three paths: GOVERNED (2 of
3 on the register — expected, `.claude/**`); eslint on the edited
script: 1 file, 0 errors, 0 warnings (`--no-inline-config`, the root
flat config's `**/*.mjs` block; no type-aware rules, so the diff moves
no untouched file's verdict). Readings taken at `cba931995`, the branch
head. The same derivation from a throwaway worktree at `origin/main`
64de4c5 (five commits past this branch's base, four of them touching
family-defining files) yields the identical 40 families — none new, none
gone. This branch is deliberately not merged with `main` here: the
incoming range carries four `merge=os-regen` artifacts whose driver debt
would hold the push on a PR that touches no spec file; the seat's
`update-branch` is the right instrument if a refresh is wanted before
landing.
No published package moves (`scripts/pm/**` and `.claude/**` ship in no
`files[]`): `skip-changeset`.
## Acceptance notes
- noted, not filed: the file's self-test keeps a flat case list with the
verdict handshake and no battery-name floor (the AGENTS.md `--self-test`
section's floor rule is stated for writing a new one); the 2026-09
self-test shape census owns that class — carrier: whoever works that
census; not this card.
- noted, not filed: `readCard` still uses the global `fetch` while
`readThread` takes an injectable one; symmetry only, no behaviour —
carrier: none.
- Boundary kept: no merge with #18963 / PR #18981 (grading-time dedupe
by file) — same class, different mechanism; #18963 is not addressed
here.
## 维护者速读(草稿)
**改了什么**:查重工具 `check-prior-rulings.mjs` 多读一处 ——
被查那张卡自己评论线上的裁决评论;贴进卡面的那一行 `Prior rulings read:` 多出一段 `thread:
…`,写明本卡已有几条裁决、评论 id 是哪几条;读不到线程时整行仍写 `unresolved`,绝不写成「没有」。章程
`SKILL.md:339` 与 `decision-analysis.md:45` 各原位改一行,行数不变,同一批落地。
**为什么改**:#17518 上四天内出现两条互相矛盾的维护者裁决,一整轮派发白跑 ——
原因是重新分诊的席位没看到卡上四天前的那条裁决,而工具即使跑了也查不到:卡的评论线不在它任何语料里。这次把「已裁卡」这半边做成机械面,并把章程那句话改成工具真能答的范围。
**风险与代价(含回滚)**:每次查重固定多一次线程读取 —— 事故卡 1 页、约 0.35 秒;全板最长线程(962 条评论)10 页、约
4.4 秒,已实测。识别靠裁决评论首行的固定写法(三种实测形态),不靠作者身份;写法漂移会以「近似项」在报告里显形而不是静默漏掉。回滚 =
revert 本 PR 三个文件,无发布物、无数据迁移。
**席位意见**:(留空)
**你要做的**:确认三种裁决写法的识别边界可接受;若同意,按受管面流程合并。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent e15870f commit 8cfa07a
3 files changed
Lines changed: 432 additions & 48 deletions
File tree
- .claude/skills/pm-dispatch
- references
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
337 | 337 | | |
338 | 338 | | |
339 | 339 | | |
340 | | - | |
| 340 | + | |
341 | 341 | | |
342 | 342 | | |
343 | 343 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | | - | |
| 45 | + | |
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| |||
0 commit comments