Skip to content

Commit c337079

Browse files
os-steveclaude
andauthored
fix(scripts/pm): H59 reads state_reason so a deliberately REOPENED card is not called a platform failure (#19427)
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-test` — **5118 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 0fe677f -> afec5bdfc7fe ✗ check-half-states self-test: 7 of 5127 case(s) failed. ablation-replace: ok restored: blob == HEAD (0fe677f) 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 union** — `node 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 N` — `scripts/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](https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 488f4f5 commit c337079

1 file changed

Lines changed: 29 additions & 0 deletions

File tree

‎scripts/pm/check-half-states.mjs‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14511,8 +14511,24 @@ export function h59FalseCloseRow({ pr, card, attribution, closedEvent, closedAtM
1451114511
*
1451214512
* The card's presence in the open listing IS the reading, so a truncated
1451314513
* listing can only lose a row and never invent one.
14514+
*
14515+
* ⭐ The row also carries `state_reason`, the whole discriminator for the
14516+
* REOPENED arm: a card open because somebody REOPENED it is a deliberate act,
14517+
* ⛔ never GitHub failing to close it, so the arm changes the SENTENCE only.
1451414518
*/
1451514519
export function h59FalseOpenRow({ pr, card, keyword }) {
14520+
if (String(card?.state_reason ?? '') === 'reopened') {
14521+
return (
14522+
`FALSE OPEN (reopened): merged PR #${pr?.number} binds \`${keyword} #${card?.number}\` in its body and ` +
14523+
'the card is open carrying `state_reason: reopened` — it was CLOSED at some point and REOPENED since, a ' +
14524+
'deliberate act and ⛔ never the platform failure the plain row names. ⚠️ This row buys no timeline, so it ' +
14525+
'does not PLACE that reopen against the merge: whether the declared closure fired and was reversed, or ' +
14526+
'never fired on an already-reopened card, is unread here — what the payload settles is that a REOPEN ' +
14527+
'happened, and that is the act to read. Remedy, and it belongs to the SEAT THAT OWNS the card: read the ' +
14528+
'reopen\'s own reason — the comment that made it, or the decision-box label it carries — and ⛔ do not ' +
14529+
'close a card somebody reopened on purpose. ⛔ This row closes nothing and writes no label.'
14530+
);
14531+
}
1451614532
return (
1451714533
`FALSE OPEN: merged PR #${pr?.number} binds \`${keyword} #${card?.number}\` in its body, and the card ` +
1451814534
'is STILL OPEN after that merge — GitHub did not perform the closure the PR declared. ' +
@@ -34780,6 +34796,19 @@ Doubles as the fire's **write self-check** (step 0). \`201\` is not the reading.
3478034796
t('H59 (b): ⛔ no angle-bracket placeholder reaches the row body', /[<>]/.test(row59b), false);
3478134797
t('H59 (b): not a loud finding', isLoudFinding(row59b), false);
3478234798

34799+
// ⭐ Direction (b), REOPENED — the discriminator rides the same open listing row, so the arm buys nothing.
34800+
const row59bBy = (state_reason) => h59FalseOpenRow({ pr: pr59FalseOpen, card: { ...card59Open, state_reason }, keyword: 'Fixes' });
34801+
const row59bReopened = row59bBy('reopened');
34802+
t('H59 (b) reopened: ⛔ the platform-failure sentence is NOT printed at all', row59bReopened.includes('GitHub did not perform the closure'), false);
34803+
t('H59 (b) reopened: …it quotes the field it read off the payload it already held', row59bReopened.includes('`state_reason: reopened`'), true);
34804+
t('H59 (b) reopened: it says the card was closed and REOPENED since, a deliberate act', row59bReopened.includes('CLOSED at some point and REOPENED since'), true);
34805+
t('H59 (b) reopened: ⛔ it refuses to PLACE the reopen against the merge, buying no timeline', row59bReopened.includes('buys no timeline'), true);
34806+
t('H59 (b) reopened: the remedy points at the REOPEN\'s own reason', row59bReopened.includes('read the reopen\'s own reason'), true);
34807+
t('H59 (b) reopened: …and ⛔ warns off closing a card somebody reopened on purpose', row59bReopened.includes('do not close a card somebody reopened on purpose'), true);
34808+
t('H59 (b) reopened: the row still FIRES — this changed the SENTENCE, ⛔ not the population', row59bReopened.startsWith('FALSE OPEN (reopened):'), true);
34809+
t('H59 (b) reopened: ⛔ no angle-bracket placeholder reaches this one either', /[<>]/.test(row59bReopened), false);
34810+
t('H59 (b) reopened: ⛔ every OTHER state_reason keeps today\'s sentence, null included', [null, undefined, 'completed', 'not_planned'].every((r) => row59bBy(r).includes('GitHub did not perform the closure')), true);
34811+
3478334812
// The two directions are DISTINCT sentences — neither restates the other.
3478434813
t('H59 directions: the two rows do not share an opening', row59a.slice(0, 20) === row59b.slice(0, 20), false);
3478534814

0 commit comments

Comments
 (0)