Skip to content

fix(pm): a present-but-unreadable Seat: declaration reads as null, never as seat 1 - #19423

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-19108-inline-seat-line-is-unreadable
Sep 20, 2026
Merged

os-steve merged 1 commit into
mainfrom
claude/issue-19108-inline-seat-line-is-unreadable

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes #19108

Clause-②: no

claimSeatNumber in scripts/pm/check-half-states.mjs returned 1 whenever its line-initial key regex found nothing, whatever else the body carried. A claim that declares its seat anywhere the key line cannot see — mid-sentence, inside a code span, inside a subscript tag, behind a > - prefix — therefore read as a real seat, so the misread never surfaced as an error: it surfaced as an accusation, and H38 rowed a different seat's post as stale over it.

A presence probe now runs only where the key line matched nothing. A body carrying the whole declared payload (Seat: then domain:LANE#N) somewhere the key line cannot see is PRESENT-but-unreadable and returns null — matching no seat, invisible to the seat filter, which is this file's documented under-reporting direction. A body with no declaration at all still returns 1, and the docblock now says why: that default is what keeps every claim written before the line existed attributed to the only seat those lanes had.

⛔ The probe deliberately does not widen CLAIM_SEAT_KEY_LINE. Accepting a malformed declaration would leave the writer no way to find out either — the same defect wearing the other costume — and the file already carries a maintainer ruling that the literal Claim: marker must not be widened. Refusing a malformed declaration at the writing path is a different instrument's job: #19152 is not addressed here.

Before / after — the card's measured table, module imported directly

Stored comment bodies fetched through the REST proxy; controls verbatim from the card. repro before the fix on 5a5e710, after on a62389c.

body before after
the three claims of one round (#19077 · #18728 · #18978) in the shape originally stored (inline code span; reconstructed by dropping the line-initial Seat: lines that the 2026-09-18 repair edit added) 1 null
the same three as stored today (repair edit's line-initial declaration present) 3 3
control — declaration beginning a line 3 3
control — no declaration at all 1 1
control — the same code span placed after other text on the line 1 null
control — bullet + bold key (- **Seat**: …) 3 3
control — declaration inside a subscript tag 1 null
control — prose 「the seat: domain:services#2 is busy」 (the file's deliberate pin) 1 1
control — a > - Seat: line 1 null ⚠️ see below
control — a present line naming no number null null

⚠️ Measurement limit, declared: the three claims were repaired on 2026-09-18, so their pre-edit bodies are not directly retrievable. The reconstruction above drops the line-initial Seat: lines the repair added and leaves the opening sentence's inline code span untouched — that sentence is unchanged prose in all three stored bodies, so what is measured is the original declaration shape, not a synthetic one. The card's other population — 「this seat's eight earlier claims of the same shift」 — was NOT MEASURED: enumerating them needs a board scan outside this run's read channel. The three as-stored bodies carry exactly that shape (line-initial Seat: domain:spec#3) and are measured instead, in the same direction.

⚠️ One existing pin changes its answer, on purpose

The pin that read

t('H38 seat: a `> - Seat:` line is not read (measured shape, same as `Branch:`)', claimSeatNumber('> - Seat: domain:services#4'), 1);

asserted 1. It now asserts null, and the case name and its comment say so: blockquote-then-bullet is still no line-initial match, but the declaration is present, so the honest answer is null. That row was itself an instance of the defect this card names — a present declaration silently answered with a real seat — so leaving it at 1 would have fixed the reported spelling and kept a measured sibling of it.

PM mechanism assumptions — each verified, two corrected

  1. claimSeatNumber is the only reader of the Seat: line. git grep 'claimSeatNumber' over the tree: 9 occurrences, all in scripts/pm/check-half-states.mjs (1 definition, 1 production caller, 7 self-test pins). Its only caller is newestLaneClaim (:9257), whose only production call site is the H38 loop (:23996); the two self-test call blocks are the rest. A tree-wide git grep 'Seat' over scripts/ and .github/ names no second reader — .claude/skills/pm-dispatch/SKILL.md carries the template text, not a reader. seatLane reads the seat number off the post title, a different key.
  2. The three misread claims carried the declaration inline as a code span after other text — confirmed on the stored bodies. All three open with the declaration inside the first sentence:
**Claim:** dispatched by the `domain:spec` execution seat. `Seat: domain:spec#3` · `session_019srGWGCBBCBHqcDoRZpQRh` · claimed 2026-09-18T19:31Z

Re-read through the fix ⇒ null, and the line-initial form still reads 3. See the measurement limit declared above on the eight-claim leg.
3. ✅ Decided: a null seat is simply filtered out — no new H38 sentence. Reasoning below.
4. ⚠️ Refuted: there is no existing H38 / claimSeatNumber battery to put cases in. The roster (SELF_TEST_BATTERIES, 8 entries) declares itself PARTIAL in its own header — 「the several hundred t(...) cases above and below are UNBATTERIED and stay that way here」 — and every claimSeatNumber pin is an unbatteried t(...). So the new cases are unbatteried t(...) beside them, SELF_TEST_BATTERY_FLOOR stays 8, and no battery was opened. Every one of the eight batteries still registers above its pin.

The H38 decision, on the four axes

The grading allowed either 「null or a named row」. Chosen: null, filtered out — H38 gains no sentence.

  • 实际业务需求 (real demand, measured). The party who needs to find out is the writer of the claim. H38 rows on a seat post, and the seat of an unreadable claim is by construction unknown — a named row would have to pick a post to accuse, which is the defect restated. A loud row here reaches the wrong reader.
  • 项目长远合理性 (long-term). H38's contract is one question: is THIS post stale against ITS OWN seat's claims. An unreadable-declaration row is a different question with a different subject; filing it inside H38 would make one row mean two things. The file already takes the declining posture for a lane it cannot count (H32's foreign reasoning, quoted in h38SeatPostStale). With null the claim contributes nothing and H38 declines by construction — the card's own shape 3, reached without new prose.
  • 防 AI 写错 (structurally harder to get wrong). Contract-first says tighten at the producer. The producer-side refusal is a separate instrument ([finding] a claim's keyed lines are exact-value fields enforced only AFTER the write — one seat, one shift, five half-state rows from three different keys #19152, held on the seat post); a diagnostic sentence on the consumer would be a consumer-side compensation for a producer-side gap, and it would also become the reason nobody lands the refusal. What this PR does do on the AI-error axis is remove a silent wrong answer that is a valid value — the shape that never reads as an error.
  • 创业阶段不扩散 (no surface sprawl). A new finding sentence is a new row family to word, to pin, to floor and to defend against false positives on multi-seat lanes. This diff instead makes an existing row correct. Net +35 lines against the PM's ≤ +40 budget.

⚠️ The cost of the quiet arm, stated rather than hidden: if an unreadable claim is a lane's only claim, H38 now says nothing at all where it previously said something wrong. That is the file's declared under-reporting direction, and it is strictly better than a confident accusation against a bystander.

Why the probe is case-sensitive and demands the whole payload

Off the line start there is no position left to tell a declaration from prose. The file pins 「the seat: domain:services#2 is busy」 ⇒ 1 as a deliberate control; a case-insensitive bare-key probe would flip it to null and start finding declarations in prose. So the probe requires a capital Seat, a colon, and the lane-and-number payload domain:LANE#N. It is pinned to the measured shape, and its docblock says which direction to move it: a variant it misses falls back to 1, which is the defect, so widen on evidence — ⛔ never narrow it.

Tests

  • node scripts/pm/check-half-states.mjs --self-testexit 0, 5124 cases pass (base 5a5e710: 5118 cases pass ⇒ the 6 new cases are counted; one existing case changed its expectation). All 8 batteries above their pins: 183/172 · 37/34 · 46/42 · 152/142 · 213/184 · 55/52 · 11/10 · 26/24.
  • Reverse verification (ablation), through scripts/ablation-replace.mjs so the mutation is proven on disk: replacing the one changed production line with its old if (!m) return 1;anchor 1 -> 0, blob 9a8ba4b32ba3 -> 665c53ffe8ee, self-test exit 1, 4 of 5124 case(s) failed — the mid-sentence case, the subscript-tag case, the flipped > - Seat: pin and the seat-1 leg of the end-to-end pair. Restore verified by the tool: blob == HEAD (9a8ba4b32ba3) and git diff HEAD empty. The other 3 new cases are controls that must stay green under that ablation (prose ⇒ 1, a lane token with no key ⇒ 1, no declaration ⇒ 1), and they did.
  • Derived gate union, node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at a62389c (no paths passed — the script derives the change set itself, three-dot vs merge base 5a5e710): 41 families, every one run with its exit captured before any pipe, 41/41 exit 0. Reconciled with --ran: 「41 derived, 41 run, 0 NOT-MEASURED, 0 UNRUN」.
  • Also run, because the derivation marks their rosters as sitting under a directory this diff is in and says their silence is not a clearance: check:pm-label-desc-cap, check:pm-governed-prose, check-published-list-mirrors, check:pm-settings-deny-roster, check:select-gate-families — all exit 0.
  • node scripts/pm/check-governed-merges.mjsNOT governed (scripts/pm/** is not on the register, 0 of 1 path hit it) — ordinary queue landing.
  • skip-changeset: scripts/pm/** publishes nothing from any released package.

Acceptance notes

Off-path observations, ⛔ not filed and ⛔ not fixed here (os-dev.md rule 3):

  • noted, not filed: the file's own H38 self-test fixtures are unbatteried, so the roster's eight batteries do not floor this row family at all; opening one is a diff of its own and the roster header already declares that gap deliberately. 承接者: the next card that adds an H38 row.
  • noted, not filed: the sibling boards were never measured for the same misread — the card's own evidence limit. 承接者: none today; it needs a cross-repo sweep nobody is holding.

Generated by Claude Code

…ever as seat 1

`claimSeatNumber` returned 1 whenever the line-initial key regex found
nothing, whatever else the body carried. A claim declaring its seat
mid-sentence — the same text, inside a code span, inside a subscript tag,
or behind a `> - ` prefix — therefore read as a REAL seat, so the misread
never surfaced as an error: it surfaced as an accusation, with H38 rowing
another seat's post as stale over three claims of one round.

A presence probe now runs only where the key line matched nothing: a body
that carries the whole declared payload (`Seat:` then `domain:<lane>#<n>`)
somewhere the key line cannot see is PRESENT-but-unreadable and returns
null, matching no seat and staying invisible to the seat filter — the
file's documented under-reporting direction. A body with no declaration at
all still returns 1, which is what keeps every claim written before the
line existed attributed to the only seat those lanes had.

The probe deliberately does NOT widen `CLAIM_SEAT_KEY_LINE`: accepting a
malformed declaration would leave the writer no way to find out either.
It is case-sensitive and demands the lane-and-number payload so the pinned
prose control keeps reading as seat 1.

Claude-Session: https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a62389c2e0b94ce72e6b9ca72ae900eaf422cc0f

① Derived judgments

  • Published accept set / public surface: none moves — one report-only PM patrol reader (scripts/pm/check-half-states.mjs, +49 / −14, net +35 against the ≤ +40 budget); no published package, no schema, no label written, no request added; SELF_TEST_BATTERY_FLOOR untouched at 8 (no battery opened — the claimSeatNumber pins were and stay unbatteried, as the roster header declares). check-governed-merges --pr 19423: NOT governed (0 of 1 paths) — ordinary queue landing. check-widening-tells --declaration no: 1 file NOT MEASURED — the no stands on the seat's reading. Judged correct.
  • The fix, read line by line: claimSeatNumber keeps the line-initial reader CLAIM_SEAT_KEY_LINE unchanged and, ONLY where it matched nothing, consults a presence probe CLAIM_SEAT_DECLARATION_ANYWHERE (case-sensitive, payload-complete: the Seat key then domain:<lane>#<n>) — present-but-unreadable ⇒ null (invisible to the seat filter, the file's documented under-reporting direction), truly absent ⇒ 1 (every pre-line claim keeps its seat-1 default, and the docblock now says why). ⛔ The key line is NOT widened: accepting a malformed declaration would leave the writer no way to find out — the card's own point. Seat-run live probe on this head: line-initial #3 → 3; absent → 1; the declaration as a code span mid-sentence → null; inside a <sub> tag → null; bullet + bold key → the number; a present line naming no number → null; the lowercase prose control 「the seat: domain:… lane」 → 1. The card's measured misread (three claims of one round read as seat 1 ⇒ a false H38 row on seat 1's post) is closed in the direction the grading allowed (「null or a named row」); the dev's four-axis choice of the quiet arm (H38 rows on a seat post, and an unreadable claim names no post to accuse; the loud refusal belongs at the producer — [finding] a claim's keyed lines are exact-value fields enforced only AFTER the write — one seat, one shift, five half-state rows from three different keys #19152, held) is sound. Judged correct.
  • One EXISTING pin flipped on purpose and disclosed: a > - Seat: line (blockquote-then-bullet) read 1 and now reads null — an instance of the very defect (a present declaration answered with a real seat), so flipped rather than special-cased. Judged correct.
  • Evidence, seat-run on this head in a detached worktree: check-half-states.mjs --self-test exit 0 — 5124 cases (5118 at the base: 6 new, 1 flipped), all 8 batteries above their pins; merge-tree vs origin/main a0e62e6: 0 conflicts; CI on a62389c: 36 check runs, 2 still running, 0 red. The dev's proof the pins can fail: an on-disk ablation of the one changed production line (blob-hash proven both ways) reds exactly 4 of 5124 — the two measured shapes, the flipped pin and the seat-1 leg of the end-to-end pair — while the 3 controls stay green; the 41-family derived union 41 / 41 exit 0 with --ran reconciled (a DERIVED zero), check:pm-dispatch-gates included; the only caller of claimSeatNumber is newestLaneClaim → the H38 loop (grep-verified, 9 occurrences all in this file).

② Semver level

None — nothing published; skip-changeset on the PR is the correct declaration. Judged correct.

③ Boundary flags

open_questions empty. Deviations read and accepted: the derivation ran in the no-path form (os-dev.md forbids a hand-made change set — the seat's route line was the slip, the dev's reading is right); the eight earlier claims of the affected seat are NOT MEASURED (the search API answered 403 through the proxy) and the three as-stored bodies were measured in the same direction instead — declared, not approximated. Boundary the dev pinned and the seat accepts: the probe is pinned to the measured shapes (payload-complete, capital key); an unmeasured variant it misses still falls back to 1, the defect — to be widened on evidence, never narrowed. Two noted, not filed items with carriers (the H38 fixtures are unbatteried by the roster's own declaration; sibling boards unmeasured for the same misread).

Implemented-by: claude/issue-19108-inline-seat-line-is-unreadable
Reviewed-by: session_017ETYWqMQD4qMtZzAGovWNi

VERDICT: PASS


Generated by Claude Code

@os-steve
os-steve marked this pull request as ready for review September 20, 2026 19:07
@os-steve
os-steve added this pull request to the merge queue Sep 20, 2026
Merged via the queue into main with commit 50bfe13 Sep 20, 2026
39 checks passed
@os-steve
os-steve deleted the claude/issue-19108-inline-seat-line-is-unreadable branch September 20, 2026 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants