Skip to content

Commit a09725d

Browse files
claude[bot]claude
andauthored
fix(pm): the half-state patrol's seat legs read the triage post and the newest page — H44 reaches a vacant or Routine seat, H32 / H38 take their marker from the last page (#18332)
Two defects in one file, `scripts/pm/check-half-states.mjs`, both of the same shape: a seat leg reading a seat post through the wrong population or the wrong window while the file already declares the right one. Each is fixed by pointing the leg at that declaration. ## 1 — H44's seat leg could not reach the triage post it was written for `h44NeedsSeatComments` was `pm:seat` + `seatIsHeld`, and `seatIsHeld` excludes a `🟢 Routine` holder by name and every non-🟢 status. The triage seat runs as a Routine or sits vacant most of the time — it read `[PM seat] triage (objectstack-wide) — 🔴 空缺 …` throughout this work — so H44, H56 and H64 read NOTHING on the one post H44's own header names as its worked example (「carrier of the round-open marker that is this card's worked example」), while H65 read it every run through `h65IsTriageSeatPost`, which carries no held gate. The gate now admits the triage post through that same predicate, **beside** the held leg rather than instead of it, so the file keeps ONE reading of 「is this the triage post」. **Measured cost: zero additional requests.** H65 already buys exactly that page every run through the shared `seatPostRowsFor` memo, so the widening only moves which row asks first. **⛔ No vacant-seat posture is owed, and that is measured rather than assumed.** All three rows accuse a COMMENT and name its author; none names the post's current holder, so 「nobody is on the clock」 changes nothing about whether an artefact on the thread states a reading with no time on it. On the vacant triage post's newest page (18 comments, 2026-09-10T23:26:55Z → 2026-09-15T15:54:40Z) H44 files one carrier row naming 10 offending comments, H56 files 0 and H64 files 0 — confirmed offline before the change and reproduced exactly by the live run after it. ### The design choice, on the four axes Option A was to widen the gate to the triage post; option B was to keep the held gate and rewrite the header to say the seat leg reaches only HELD posts. **A, on all four axes** — the dispatching seat's reading is confirmed, not refuted. - **实际业务需求** — measured, not inferred: the triage post carries 10 comments H44's own grammar flags, 0 of them visible to the row today, on the busiest artefact producer on the board (818 comments, 18 of them in the last five days). B leaves a report-only row structurally unable to observe its own subject, which is dead letter. - **项目长远合理性** — the file already declares both halves this needs (`h65IsTriageSeatPost` for the population, `seatPostRowsFor` for the window). A hands the leg those declarations; B is the workaround shape, correcting prose to match behaviour nobody wants. - **防 AI 写代码犯错** — these three rows are exactly the ones that catch AI-written artefacts stating a reading with no time on it or naming no session, and the triage post is where AI seats write most. Declaring a purpose in the header that the runtime does not honour is the anti-pattern this axis names. The held gate is an argument about ACCUSING A NAMED HOLDER of idleness — H32's subject, not this row's; inheriting it was the category error. - **创业阶段不扩散需求** — not a capability expansion: no new row, no new rule, no new request, one new finding on the live board. It makes an existing declared row reach the population its own header already claims, in the narrow spelling already in the file. 声明而未兑现是 实现缺口,补实现或退役 — the implementation is supplied. Immediate, no staged window. ## 2 — H32 and H38 read a seat post's OLDEST comment page The `seatMarkers` gather captured `commentRowsFor`'s page-less FIRST page, and GitHub serves issue comments oldest-first, so `latestSeatMarker` returned the newest row of page 1. That row is H32's idle clock and carries H32's wait exemption, and it is H38's `T_seat`. Measured on the live board: | seat post | comments | page-1 newest | real newest | stale by | |---|---|---|---|---| | #6023 | 225 | 2026-08-27T16:38:35Z | 2026-09-14T11:41:59Z | 18 days | | #6367 | 137 | 2026-09-04T07:56:39Z | 2026-09-10T17:55:24Z | 6 days | | #7623 | 63 | 2026-09-15T09:00:39Z | 2026-09-15T09:00:39Z | single page, identical | On #6023 the wait exemption was being judged on an August comment. The gather now takes the window through `seatPostRowsFor`. It buys nothing new — this population is a **subset** of H44's seat leg, the memo is shared, and asking here only makes the gather loop the first asker; the page-less fetch it replaces was itself wasted, because H44's leg overwrote that cache entry a pass below. `latestSeatMarker`'s contract is unchanged, and a null page is left out of `seatMarkers` exactly as the throw was. **⚠️ H38's live verdicts do not move, and the reason is pinned rather than left to be discovered:** `seatPostLastEventMs` maxes the comment stamps against the post's own `updated_at`, and GitHub bumps that field whenever a comment is added, so the post's own field already dominates. The page choice decides `T_seat` exactly where `updated_at` cannot — a comment EDITED after the post's last bump, which is the one 「seat writing home」 signal that lives only in the rows. Two self-test cases pin both halves of that. ## Reverse verification **Ablation, per card, from the committed state** (mutation proven on disk by blob hash before each reading; restored with `git checkout HEAD -- <path>`, restore proven by an empty `git diff HEAD` and a blob hash equal to HEAD's): - **Card 1** — revert the predicate leg, keep its cases ⇒ **3 cases red by name**: `H44 gate: ⭐ a VACANT triage post IS fetched — the held leg alone made this row dead letter on it`, `H44 gate: ⭐ …and a 🟢 Routine triage post too, which seatIsHeld excludes BY NAME`, `H65 scope: ⭐ H44's seat leg now REACHES the Routine triage post, through this very predicate`. - **Card 2** — revert the capture to the page-less read, keep its cases ⇒ **1 case red by name**: `seat window: …and taken through the memo by more than one row, which is what makes it shared` (got 2, want 3). ⚠️ Stated precisely: that source-text pin is the ONLY case that can observe this, and the file says why in `seatWindowAudit`'s own header — the window lives inside `sweepInto`, which takes no injectable transport, so the property a spy would prove has to be proved on the text. The other 16 cases of this card pin the CONTRACT (which marker and which `T_seat` each window yields) at function level and stay green under the ablation by construction. That is the designed instrument, not a gap discovered late. **`seatWindowAudit`, before → after:** `pagelessPaths` 1 → 1 · `commentPaths` 4 → 4 · `purchaseSites` 1 → 1 · `readers` **2 → 3**. Only the reader count moves, and its pin is rewritten in place with the superseded statement quoted above it. **Live patrol, `origin/main` `de9c319a` vs this head, both exit 0**, same corpus both runs (408 open pm-/p0-labeled, 507 unscoped, 13 open PRs, 683 recently-merged PRs): | family | origin/main | this head | |---|---|---| | H32 | 0 | 0 | | H38 | 1 | 1 | | H44 | 29 | **30** | | H56 | 6 | 6 | | H64 | 10 | 10 | | H65 | 1 | 1 | Total half-states 476 → 477. **The one row that moved is `H44 #6015`** — the vacant triage post — naming comment `5626818520` as the oldest of 10 offending comments on it, which is the offline prediction reproduced exactly. No row was lost. Coverage clause: the seat leg reads `4 of 4` on `origin/main` and `5 of 5` here; H44's corpus 1488 comments across 398 threads → 1506 across 399, the 18 added being exactly the triage post's newest page; H32's `3 of 3` and H65's `1 of 1` are unchanged. ⚠️ A third commit was needed for that coverage clause: the live run printed `5 of 5 HELD seat post(s)` while one of the five was the vacant triage post. Both readings are legal English, so only a live run could catch it. The clause now names both legs; the pin that asserted the HELD wording is rewritten in place, not deleted. **Self-test:** 4235 on the old base → 4245 after card 1 → 4261 after card 2 → 4269 after taking `main` in (PR #18326's 8 cases) → **4270** after the coverage-clause pin. Three pins were rewritten in place rather than deleted, each with its superseded statement quoted above it: the H65 scope case that asserted H44's gate reads `false` on a Routine triage post, the audit's reader count, and the H44 coverage clause's HELD wording. ## Gates All 41 commands derived by `node scripts/pm/dispatch-gates.mjs --commands scripts/pm/check-half-states.mjs` were run at this head and reconciled with `--ran`; the battery `pnpm check:pm-dispatch-gates` is among them. ESLint on the file is clean, and the file carries no control bytes. ## Acceptance notes Out of scope, noted and not filed: the seat-post window is now correct for every reader, but the property is only observable from source text, because the window lives inside `sweepInto` which takes no injectable transport — a spy-able seam would let a case assert the gather's window directly instead of counting call sites. Successor: whoever next edits `sweepInto`'s transport; nobody is queued on it today, and inventing the seam under these two cards would have widened the surface well past them. Non-governed (`scripts/pm/**`) — left DRAFT for the owning seat to land in-seat after review. No reviewers requested, no auto-merge armed, no ccr route touched. `skip-changeset`: nothing published moves — `scripts/pm/**` is not in any package's `files[]`. Fixes #18324 Fixes #18325 --- _Generated by [Claude Code](https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent de9c319 commit a09725d

1 file changed

Lines changed: 224 additions & 33 deletions

File tree

0 commit comments

Comments
 (0)