Skip to content

fix(scripts/pm): H59 reads state_reason so a deliberately REOPENED card is not called a platform failure - #19427

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-19177-h59-reopened-is-not-false-open
Sep 20, 2026
Merged

os-steve merged 1 commit into
mainfrom
claude/issue-19177-h59-reopened-is-not-false-open

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes #19177

Clause-②: no

H59's FALSE OPEN row asserted a cause the payload it already holds contradicts: for a card that
is open carrying state_reason: reopened, it printed the sentence naming a platform failure.
The discriminator rides the same open-listing row the sweep already has, so the arm costs zero
requests. Region H59 only; SELF_TEST_BATTERY_FLOOR untouched (8). Net +29 lines (29 added,
0 removed, 1 file) against the PM's ceiling of +30.

Before / after, on the filing card's live shape

Probe: a merged PR binding a closing keyword to a card that is open with state_reason: reopened.
Both quotes are fenced so no keyword in this body binds anything.

Before (identical bytes for reopened and for null — the row is blind to the field):

FALSE OPEN: merged PR #9936 binds `Fixes #9874` in its body, and the card is STILL OPEN after
that merge — GitHub did not perform the closure the PR declared. ...

After, for state_reason: reopened only:

FALSE OPEN (reopened): merged PR #9936 binds `Fixes #9874` in its body and the card is open
carrying `state_reason: reopened` — it was CLOSED at some point and REOPENED since, a deliberate
act and ⛔ never the platform failure the plain row names. ⚠️ This row buys no timeline, so it
does not PLACE that reopen against the merge: whether the declared closure fired and was
reversed, or never fired on an already-reopened card, is unread here — what the payload settles
is that a REOPEN happened, and that is the act to read. Remedy, and it belongs to the SEAT THAT
OWNS the card: read the reopen's own reason — the comment that made it, or the decision-box
label it carries — and ⛔ do not close a card somebody reopened on purpose. ⛔ This row closes
nothing and writes no label.

null, completed and not_planned keep today's sentence, byte for byte.

The disposition, on the four axes

The card named two dispositions and picked neither: suppress the row on reopened, or keep
it and change the sentence
. This PR keeps it and changes the sentence.

  • 实际业务需求 — measured, not assumed. The live instance cost a full investigation on two
    consecutive sweeps and could be cleared by no legal act on the card. The population is live
    today: scanning the same state=open listing this row already holds, objectstack#17147 and
    objectui#7844 / objectui#6596 carry state_reason: reopened right now (920 open issues read
    across the two boards in this act). So the case is real. And the audit still wants the row: a
    merged PR whose body declares a closure that is not in force reads as done to everyone who
    finds it, whichever way the card got back to open. Suppression deletes that from the board.
  • 项目长远合理性 — H59's whole architecture is "narrow before you buy, count every narrowing,
    ⛔ never call an unjudged thing clean" (its own clause prints "Rows are a LOWER BOUND").
    Suppression would add the one silent drop in this row that no counter reports. The defect is a
    sentence asserting a cause its data does not establish; the fix belongs at the producer of that
    sentence, not in a consumer that learns to ignore the row.
  • 防 AI 写错 — the decisive axis. This row's reader is an AI seat on patrol, and today's
    sentence sends it to look for a platform failure that did not happen and hands it a remedy
    ("close it if the work landed") that is wrong for a deliberate reopen: a seat that follows it
    closes a card somebody reopened on purpose. Suppression removes the false instruction but also
    removes the true information, and a verifier that silently degrades is worse than no verifier.
    The new arm removes the false accusation, declares the limit of what it read, and redirects the
    remedy at the reopen's own reason — declared, loud, and correct.
  • 创业阶段不扩散需求 — one field read, one branch in an existing sentence. ⛔ No new request,
    no new constant, no new row, no new battery, no new count key. This is the narrowest arm that
    fixes the defect, and it is a correction of an existing sentence rather than an expansion.

No axis conflict: axis 4 mildly prefers the smaller change and the chosen arm is not larger than
suppression on any measure. Suppression is refused on axes 1 and 3.

⚠️ One honesty limit, and it is stated on the row rather than papered over: state_reason: reopened proves the card was closed and reopened; it does ⛔ not prove WHICH closure, so it
cannot rule out "GitHub never fired on an already-reopened card". Placing the reopen against the
merge needs a timeline page, and direction (b) is the half that costs no request — buying one
here would change what this arm is. So the row reports what the payload settles and names what it
did not read, which is the same discipline direction (a) already applies to its band leg.

The fixture extension

The direction (b) fixture card59Open is a plain object literal, ⛔ not a helper, so the cases
build the variants with a one-line factory over a spread of it and the literal is left untouched
— every existing (b) assertion still reads the plain arm. Nine cases added beside the existing
ones (⛔ never at selfTest()'s tail): the platform-failure sentence absent, the field quoted,
the closed-and-reopened reading, the no-timeline declaration, both halves of the remedy, the row
still firing, the angle-bracket pin, and one case holding null / undefined / completed /
not_planned on today's sentence.

Verification

node scripts/pm/check-half-states.mjs --self-test5118 cases at base a0e62e6, 5127 after,
exit 0 both times. Batteries unchanged, SELF_TEST_BATTERY_FLOOR still 8.

Reverse verification (ablation), run from the committed state through
scripts/ablation-replace.mjs
so the mutation is proven on disk and the restore is proven
against HEAD. Predicted direction before running: 转红. Mutation: the new predicate neutered to
if (false && ...).

ablation-replace: ok mutation landed: anchor 1 -> 0, blob 0fe677fe4ed9 -> afec5bdfc7fe
✗ check-half-states self-test: 7 of 5127 case(s) failed.
ablation-replace: ok restored: blob == HEAD (0fe677fe4ed9) and `git diff HEAD` is empty

7 of the 9 new cases go red. The 2 survivors are exactly the 2 that are ⛔ not about the reopened
sentence — the angle-bracket pin, and the control case that holds every OTHER state_reason on
today's sentence, which reads the plain arm either way. That asymmetry is the evidence the cases
bind to this predicate rather than to the row in general.

Derived gate unionnode scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at head 30caf75, no paths passed (the tool derives its own change
set: 1 path, 29 changed lines, merge base a0e62e6). It printed 41 commands; 40 ran green in the
foreground, each exit captured before any pipe
; pnpm check:pm-dispatch-gates ran detached with
its own exit captured. Reconciled with --ran. The tool's own NOT MEASURED classes (8 families
taking a value from the workflow, 1 CI-measured-only, 10 pending a changeset that this card does
not write, 46 artifact rosters, 11 wide-population families, 1 path-scheduled CI job) are CI's
and are named as unmeasured here rather than read as clearances.

node scripts/pm/check-governed-merges.mjs --pr Nscripts/pm/** is ⛔ not a governed surface.

Acceptance notes

Off-path observations from this region, ⛔ not filed and ⛔ not fixed here:

  • The census clause (h59LinkageClause) counts a reopened row as an ordinary declared-closure row
    and does not break the two arms out. Nothing it prints is false — it says a declared target
    still on the open listing is a FALSE OPEN row, which stays true — so this is an enhancement,
    ⛔ not one of the three filable classes. It would also need a new SWEEP_COUNT_KEYS entry and
    the clause's own pins, well past this card's ceiling. 承接者: whoever next widens H59's census.
  • The claim's line pointers for the H22 lit control read :25702:25718; at a0e62e6 those
    fixtures are at :25902:25918, and a fifth spelling of the field lives at :29968 in H24's
    battery. All five are self-test fixtures, so the file had no production reader of state_reason
    before this PR. Drift in a dispatch pointer, ⛔ not a defect in the file. 承接者: none.

Generated by Claude Code

…card is not called a platform failure

H59's FALSE OPEN row said 「GitHub did not perform the closure the PR declared」
for every open card a merged PR bound a closing keyword to — including a card
that was closed and then REOPENED on purpose. The discriminator is already on
the open-listing payload the row holds (`state_reason: 'reopened'`), so the arm
costs no request.

Keeps the row (a declared closure that is not in force is still worth reading)
and changes the sentence for that one case: it names the reopen as the act to
read, declares that it buys no timeline and therefore does not place the reopen
against the merge, and points the remedy at the reopen's own reason rather than
at GitHub. Every other `state_reason`, null included, keeps today's sentence.

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: 30caf7520985efa0eb9aa389e0e68d43f7c96ba4

① Derived judgments

  • Published accept set / public surface: none moves — one report-only PM patrol reader (scripts/pm/check-half-states.mjs, +29 / −0 against the ≤ +30 budget); no published package, no schema, no label written, zero new requests (the discriminator is a field of the raw REST issue object the open listing already stores); SELF_TEST_BATTERY_FLOOR untouched at 8, no new count key or constant. check-governed-merges --pr 19427: NOT governed — 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: inside H59's FALSE OPEN row, one guard String(card?.state_reason ?? '') === 'reopened' returns a distinct sentence — 「FALSE OPEN (reopened): … the card is open carrying state_reason: reopened — it was CLOSED at some point and REOPENED since, a deliberate act and ⛔ never the platform failure the plain row names」 — that refuses to place the reopen against the merge (the row buys no timeline), points the remedy at the reopen's own reason (its comment or decision-box label) and warns off closing a card somebody reopened on purpose; every other state_reason, null included, keeps today's sentence byte for byte (pinned). The POPULATION is unchanged — this card is about the sentence, and the chosen disposition (keep the row, change the sentence; not suppression, which would hide a real reopen from the audit the row exists for) is the one the seat read and the dev argued on the four axes. Judged correct.
  • Evidence, seat-run on this head in a detached worktree: check-half-states.mjs --self-test exit 0 — 5127 cases (5118 at the base, +9: seven on the reopened arm, one placeholder pin, one control that every other state_reason prints today's sentence); merge-tree vs origin/main 576d5df: 0 conflicts, and vs the head of this seat's queued PR fix(pm): a present-but-unreadable Seat: declaration reads as null, never as seat 1 #19423 (claimSeatNumber region, same file): 0 conflicts; CI on 30caf75: 34 check runs, 3 still running, 0 red. The dev's proof the pins can fail: an on-disk ablation of the guard (blob-hash-proven both ways) reds exactly 7 of 5127 — the seven reopened-arm cases — while the placeholder pin and the other-reasons control stay green; the 41-family derived union 41 / 41 exit 0 with --ran reconciled (a DERIVED zero), check:pm-dispatch-gates included.

② Semver level

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

③ Boundary flags

open_questions empty. Mechanism assumptions measured rather than followed: (3) refuted — the H59 (b) fixture is a plain literal, extended by a one-line factory over a spread so every existing assertion still reads the plain arm; (2) amended — state_reason sat at five fixture sites (H22 ×4, H24 ×1), so H59 is the file's FIRST production reader of the field; (1) and (4) confirmed (the open listing stores raw REST rows with the field live — three open cards carry reopened today; the phrase has no census pin to move). The card's live instance (objectui#9874) has since closed completed — the premise is about the code and holds. The seat's claim pointers for the H22 control had drifted ~200 lines (a dispatch comment, not repo content). One noted, not filed enhancement with a carrier: h59LinkageClause's census does not break the reopened arm out (nothing it prints is false; a new SWEEP_COUNT_KEYS entry past this ceiling).

Implemented-by: claude/issue-19177-h59-reopened-is-not-false-open
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:52
@os-steve
os-steve added this pull request to the merge queue Sep 20, 2026
This was referenced Sep 20, 2026
Merged via the queue into main with commit c337079 Sep 20, 2026
39 checks passed
@os-steve
os-steve deleted the claude/issue-19177-h59-reopened-is-not-false-open branch September 20, 2026 20:17
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