Commit 226970b
feat(pm): two report-only patrol rows — the decision card's four-facet block, and
Fixes #17009
Fixes #16904
Both cards' D2 halves. #17009's D1 landed as `ea1da645` (PR #17515) and
#16904's as `ae1b2588` (PR #17994, plus the seat's one-time sweep,
readings 5652947318); these two rows are the last deliverable on each
card, so merging this closes both.
Two report-only rows in `scripts/pm/check-half-states.mjs`, one per
card, plus the CI wiring #16904's D1 report left unfired. No `--write`
path anywhere, no label written, no gate added.
## H62 (#17009) — a decision card whose face carries no four-facet block
An open `needs-user-decision` card whose **body** carries no
`os-decision-facets` marker, in either spelling (plain-text line or the
HTML-comment form). Presence is a literal containment test over the raw
body, which is the extraction rule the shape names for itself (「提取按字面
grep」); code-stripping and a positional read are refused **by name** in
the docblock because each produces 「注释形读不到永不读作无四棱块」, the one direction
of error the shape forbids. Body only: 「落卡即带」 makes the block part of
the card face, so a marker in a comment is not the shape, and the row
buys no thread.
The `Prior rulings read:` line is a **second reading on the same row** —
`present` / `unresolved` / `absent`, in the row text, never a second
row. Its writer is `check-prior-rulings.mjs`, coupled here by **source
text**: both import directions were measured and neither runs (static is
a TDZ cycle; dynamic deadlocks against this module's own top-level
await). The docblock records both measurements so nobody retries them.
## H63 (#16904) — a graded card still wearing `finding`
An open card carrying `finding` beside a grading state or any
`priority:*`. The rule is **reused by import, never copied** — but the
arrow is forced, not chosen: the sweep already imports `PM_STATE_LABELS`
from the patrol and derives module-level constants from it, so the
patrol importing the sweep throws `ReferenceError: Cannot access
'PM_STATE_LABELS' before initialization` the moment the patrol is the
entry point, which it is on every run and every self-test. So the screen
moved **down** into the module that owns the vocabulary, and
`sweep-stale-finding.mjs` re-exports it under the names its callers and
its 91-case self-test already use. The patrol's own self-test pins by
source text that the sweep still aliases that definition and has not
re-grown a copy.
The row does not contradict the pins it sits beside, and the docblock
names each: H13's set carries `finding` (which is exactly why H13 is
silent on a graded carrier — the card is visible, to the wrong reader);
H22 drops it and this row is open-only; H29 drops it as a card KIND
rather than a position, and this row explicitly does not claim
otherwise.
**Wiring:** `check:pm-stale-finding` in `package.json` and a self-test
step in `lint.yml`. `check-self-test-wired` population **208 → 209**,
green in both directions — the tool shipped a 91-case self-test that
nothing in CI ran.
## Acceptance greps, both directions
| reading | before | after |
|:--|--:|--:|
| `H62 (#17009)` / `H63 (#16904)` in `check-half-states.mjs` | 0 / 0 | 3
/ 3 |
| lit control `Decision-box dependency flags (instruction ④)` | 4 | 4 |
| `os-decision-facets` reader in `check-half-states.mjs` | 0 | 3 |
| `check:pm-stale-finding` in `package.json` | 0 | 1 |
| `check:pm-stale-finding` in `lint.yml` | 0 | 1 |
| `check-half-states --self-test` cases | 3806 | 3915 |
| `sweep-stale-finding --self-test` cases | 91 | 91 |
| `check-self-test-wired` scripts CI runs | 208 | 209 |
| `check:declared-population-live` declaring families with an
unreachable population | 1 | 0 |
**Untouched, proved by md5 of the whole exported function body** — H4
`1d13e919…`, H13 `fa79039c…`, H14 `1e88cafd…`, H22 `a45048e0…`, H29
`aaa6ea92…`: identical before and after. The instruction-④ section,
`.claude/**` and the write-path half of `finding` stripping are
untouched.
## Live readings, read-only, taken from this tree
H63: the landed sweep's own dry run against this board — **33 open cards
carry `finding`, 0 stale, 33 genuinely ungraded, 0 unjudged**; 1
listing, 0 writes, `rate_limit` core 14747 → 14746. Zero is the expected
reading right after the seat's one-time sweep (19 stripped here, 36 on
objectui), and it is what the row exists to watch refill.
H62: the decision inbox is **1 open card** today, not the 27 the D1
measured — it fires on that one, second reading `absent`. finding outliving its grade (#18033)1 parent 60b9955 commit 226970b
5 files changed
Lines changed: 772 additions & 92 deletions
File tree
- .github/workflows
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1232 | 1232 | | |
1233 | 1233 | | |
1234 | 1234 | | |
| 1235 | + | |
| 1236 | + | |
| 1237 | + | |
| 1238 | + | |
| 1239 | + | |
| 1240 | + | |
| 1241 | + | |
| 1242 | + | |
| 1243 | + | |
| 1244 | + | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
| 1252 | + | |
| 1253 | + | |
| 1254 | + | |
| 1255 | + | |
| 1256 | + | |
| 1257 | + | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
1235 | 1267 | | |
1236 | 1268 | | |
1237 | 1269 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
81 | 81 | | |
82 | 82 | | |
83 | 83 | | |
| 84 | + | |
84 | 85 | | |
85 | 86 | | |
86 | 87 | | |
| |||
0 commit comments