Skip to content

Commit 226970b

Browse files
claude[bot]claude
andauthored
feat(pm): two report-only patrol rows — the decision card's four-facet block, and finding outliving its grade (#18033)
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`. ⚠️ No live specimen carries the marker, so the clean direction has no lit control on today's board; its controls are the self-test's fixtures, in both spellings. ## Gates Derived on the merged head `1d52dd97` with `dispatch-gates --commands --repo objectstack-ai/objectstack` (no paths): **74 families, 74 run, every one exit 0**. Reconciled with `--ran` carrying a recorded exit code per family: 「74 derived famil(ies) accounted for — 74 run, 0 NOT-MEASURED (a DERIVED zero — all 74 recorded an exit code and none of them is 3)」. Five build-reading families first answered `PREREQUISITE NOT MET` (exit 3) in a fresh worktree — recorded as NOT MEASURED, never as findings — and all five exit 0 after `pnpm build` under the shared verify lock (`VERDICT command-exit 0 · held 391s · waited 0s`, 73/73 tasks). One of the five, `check:type-check-debt`, refused a second time on a heap ceiling my own `NODE_OPTIONS` imposed (its own line says so: 「tsc runs under --max-old-space-size=4096 MB — the caller's NODE_OPTIONS, which is tighter」) and exits 0 at the CI-shaped 6144. `pnpm lint` run repo-wide rather than narrowed: `eslint --no-inline-config . --format json` exit 0, **6723 files linted, 0 with findings** — no narrowing claimed. Control-character sweep over every touched file: no match. ⚠️ `origin/main` moved again after this merge; the derivation says the tree is 1 commit behind on 3 files it derives from. The merge queue rebuilds and re-runs on that generation, which is where that half is measured. ## Deviations, declared 1. **One file beyond the claim's surface.** `scripts/pm/sweep-stale-finding.mjs` is edited to hand the screen down, because ruling ② mandates reuse by import and the only import direction that runs points the other way (measured; the crash is reproduced in the docblock). Its public API, its behaviour and its 91-case self-test are unchanged. 2. **No header-roster entry.** The header roster stops at H28; every row since carries its own banner block above its predicate instead. These two follow that convention — adding two entries to a list 33 consecutive rows are absent from would make the header lie about its own coverage. 3. **Two more commits, both derivation bookkeeping the wiring forced, neither weakening a self-test.** - Wiring the sweep into `package.json` makes it a declaring family for `dispatch-gates`, and `check:declared-population-live` reds on the module-level fixture's repo slug. Fixed by the gate's own remedy: the `no-path-population` marker (true — the tool's whole input is the GitHub API) plus moving the fixture into the self-test body, where the module-body mask reaches it. - `dispatch-gates --self-test` then red on one case of 1682 (CI run 34759389219, `Lint & Repo Gates` step 32): 「listed but no longer a member: check:pm-half-states」. The patrol's two sibling source-text pins make the family placeable **by path** (`dispatch-gates scripts/pm/sweep-stale-finding.mjs` ⇢ 「program text read by scripts/pm/check-half-states.mjs」), so it left the root-walk residue population and its `ROOT_WALK_RESIDUE_LEDGER` row went stale — which that ledger reds on by its own contract. The row is removed with a note; the placement is true and is the opposite of the #15753 one the row was written about. 4. **`origin/main` merged** (`1d52dd97`) because the derivation warned the tree was stale on a file it derives from — and it was load-bearing: the merged `check-clause2-carriers.mjs` adds `check:pm-clause2-carriers` to this card's family list (73 → 74). `skip-changeset`: `scripts/pm/**`, a root `package.json` script and a workflow publish nothing from any released package. Authored in Claude Code session `session_01DAcomhvR9kKizeYgg89Vo8`. --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 60b9955 commit 226970b

5 files changed

Lines changed: 772 additions & 92 deletions

File tree

.github/workflows/lint.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,38 @@ jobs:
12321232
- name: Board-snapshot archiver self-test
12331233
run: pnpm check:pm-board-snapshot
12341234

1235+
# Stale-`finding` sweep self-test (#16904) — same family and the same
1236+
# split as every step around it, and the split matters more here than for
1237+
# most: this tool WRITES. Its live path removes a label from real cards on
1238+
# a shared board, so a production run from CI would relabel the board
1239+
# because a pull request happened to run — the offline half is the only
1240+
# half CI can hold, and the sweep itself is run once per board, by hand,
1241+
# by the seat that owns that board.
1242+
#
1243+
# Until this step existed the instrument was UNFIRED: the tool shipped a
1244+
# 91-case `--self-test` that nothing in CI ran, which is the second half
1245+
# of the pair `check:self-test-wired` exists for (a script is IN its
1246+
# population only once a workflow names it, so an unwired tool's self-test
1247+
# is not a hole the gate can see — it is a hole only this step closes).
1248+
#
1249+
# What the self-test instruments is the set of rules a clean tree cannot
1250+
# exercise: the screen's ungraded verdict, which is the ONE thing the
1251+
# sweep must never strip (「`finding` ALONE is NOT stale」), the `pm:seat`
1252+
# refusal, the four-step write's read-back and its refusal on a raced
1253+
# label set, the batch caps and the resume cursor a 403 leaves, and the
1254+
# exit register that keeps "could not read the board" apart from "the
1255+
# board is clean". Weakening any of them leaves an ordinary run just as
1256+
# green — it would simply strip fewer cards, or more — and a wrong strip
1257+
# here is a write against a shared board, not a report.
1258+
#
1259+
# Since #16904's D2 the screen itself lives in `check-half-states.mjs` and
1260+
# this tool imports it, so the patrol row that REPORTS the population and
1261+
# the sweep that STRIPS it read one rule; the patrol's own self-test pins
1262+
# that this file still aliases that definition rather than re-growing a
1263+
# copy of it.
1264+
- name: Stale-`finding` sweep self-test
1265+
run: pnpm check:pm-stale-finding
1266+
12351267
# Changeset-deadline census self-test (#16850) — same family and the same
12361268
# split as every step around it. The LIVE census reads the shared board
12371269
# over the API and is REPORT-ONLY by ruling: #16850 takes option 1 (detect

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"check:pm-widening-tells": "node scripts/pm/check-widening-tells.mjs --self-test",
8282
"check:pm-prior-rulings": "node scripts/pm/check-prior-rulings.mjs --self-test",
8383
"check:pm-closed-card-sweep": "node scripts/pm/sweep-closed-cards.mjs --self-test",
84+
"check:pm-stale-finding": "node scripts/pm/sweep-stale-finding.mjs --self-test",
8485
"check:pm-post-stamped": "node scripts/pm/post-stamped.mjs --self-test",
8586
"check:pm-board-snapshot": "node scripts/pm/board-snapshot.mjs --self-test",
8687
"check:pm-changeset-deadline-census": "node scripts/pm/changeset-deadline-census.mjs --self-test",

0 commit comments

Comments
 (0)