From 9180b909788bca8dc202b16c2b82dbfff454a2be Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 18:13:37 +0000 Subject: [PATCH 1/3] =?UTF-8?q?fix(devx):=20H26=20claims=20no=20SCHEDULED?= =?UTF-8?q?=20release,=20not=20"no=20mechanism"=20=E2=80=94=20counted,=20n?= =?UTF-8?q?ot=20asserted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit H26 asserted that a `pm:blocked` card whose `Blocked-by:` target carries `pm:on-hold` / `needs-user-decision` has "NO MECHANISM THAT WILL EVER RELEASE IT". That half was never counted, and counting refutes it: on this board 2026-09-18, 14 closed cards carry `pm:on-hold` and 5 carry `needs-user-decision`, 9 of the 19 closing as `completed` — with `pm:blocked` (20 closed) as a control that fires, so the zeros would have been visible had the closed enumerator been broken. The row is miscalibrated, not wrong, so it stays. All six sites now say what is actually true — nothing is SCHEDULED to close these targets — and carry the provenance: the counted population with its control, plus this file's own `PM_RESIDUE_LABELS` / H22, which already presuppose these labels land on closed cards. H26's useful final sentence is preserved verbatim. The self-test pins the NEW wording POSITIVELY, not merely the absence of the old: an `absent:`-only check would stay green if a later edit deleted the assertion along with the sentence it named. Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk Co-authored-by: Claude --- scripts/pm/check-half-states.mjs | 122 +++++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 30 deletions(-) diff --git a/scripts/pm/check-half-states.mjs b/scripts/pm/check-half-states.mjs index 968c210c8bc..bec2ee98531 100644 --- a/scripts/pm/check-half-states.mjs +++ b/scripts/pm/check-half-states.mjs @@ -586,13 +586,20 @@ * state-model row (its protocol face) and applying the label to the * specimen card, which is a seat's write. * - * ## H26 — the block that nothing can ever release + * ## H26 — the block nothing is SCHEDULED to release * * H26 an open `pm:blocked` card whose resolvable `Blocked-by:` target is OPEN - * and parked in a state that can never close — `pm:on-hold` or - * `needs-user-decision`, both by definition states a card sits in WHILE - * OPEN. The unlock predicate is "the target closed", so such a block is - * structurally indefinite and nothing reported it: the waiting card is + * and parked in a state nothing is scheduled to end — `pm:on-hold` or + * `needs-user-decision`, which lift when a person acts and not otherwise. + * ⛔ NOT a claim that such a target CANNOT close: that categorical half + * was asserted and never counted, and counting refutes it — cards do + * close out of both states, `completed` more often than not. The census + * and its control live in the row's own rationale block, which is also + * the file's one deliberate citation of the retired words, kept greppable + * for the copy objectui ports. The unlock predicate + * is "the target closed" and no timer, sweep or queue fires it, so the + * block has NO SCHEDULED RELEASE and nothing reported it: the waiting + * card is * perfectly well-formed (H4 clean, target resolves, target open, so H19 * clean, label correct), and H9 — the nearest neighbour — audits the HELD * card rather than the waiting one. Six measured instances, all found by @@ -607,7 +614,7 @@ * cycle. FREE: H19 already resolves every distinct target, and a resolved * target's labels rode in on a payload this sweep had already paid for. * ⛔ Not a judgement that the block is wrong — waiting on a deferred card - * is sometimes right; the row says the wait has no releasing mechanism, + * is sometimes right; the row says nobody is SCHEDULED to end the wait, * which is a fact a human should be handed rather than discover. * NOT reported BY THIS ROW: a target labelled `pm:queue` while titled * `[Decision]` (one of the six). The reason is unchanged and this row is @@ -6756,12 +6763,44 @@ export function h31ContractReviewCarrierSplit(issue, openPrs) { } // --------------------------------------------------------------------------- -// H26 — a block whose target can never CLOSE, and the stale chain (#11219). +// H26 — a block nothing is SCHEDULED to close, and the stale chain (#11219). // // The unlock predicate is "the `Blocked-by:` target CLOSED". `pm:on-hold` and -// `needs-user-decision` are, by definition, states a card sits in WHILE OPEN. -// A block naming such a target is therefore structurally indefinite: nothing in -// the machinery can ever fire it, and until this row nothing said so. +// `needs-user-decision` end only when a person acts on the target: no timer, no +// sweep and no queue in this loop closes either one. A block naming such a +// target therefore has NO SCHEDULED RELEASE, and until this row nothing said so. +// +// ## Why this row no longer says "can never close" (#18017) +// +// It used to, and that half was an ASSERTION — never counted. Counting refutes +// it. Taken on this board 2026-09-18, REST +// `/issues?labels=LABEL&state=STATE&per_page=100`, pull requests excluded, the +// population being every ISSUE carrying the label: +// +// label open closed closed `state_reason` +// `pm:on-hold` 101 14 7 completed, 7 not_planned +// `needs-user-decision` 2 5 2 completed, 3 not_planned +// `pm:blocked` (control) 70 20 16 completed, 4 not_planned +// +// ⭐ The control has a known direction and it HIT: the same enumerator returned +// non-zero on all three CLOSED rows, so a `state=closed` leg that silently +// answered nothing would have read as three zeros, and it did not. ⇒ cards DO +// close out of both indefinite states, and 9 of those 19 closures are +// `completed` — the hold lifted and the work finished, which is precisely the +// release the retired wording said could not exist. +// +// This file presupposed it already, twice, in its own text: `PM_RESIDUE_LABELS` +// (H22) carries `pm:on-hold`, and a label is residue only on a CLOSED card; and +// H22's own note calls `needs-user-decision` one of the "perfectly good states +// for a closed card to have ended in" — the direct converse, one file away. +// +// ⚠️ What is KEPT is the half that survives the count: nothing is SCHEDULED to +// close these targets. The overstatement was "no one is scheduled to act" +// rendered as "no mechanism exists", and only the rendering changed. This +// sentence does not stay in a report body — it is quoted outward as a +// structural verdict, and other seats have cited it as grounds to abandon locks +// they would otherwise have opened, so what it claims has to be what the +// numbers support. // // ## Why every existing check passes on these cards // @@ -6805,16 +6844,19 @@ export function h31ContractReviewCarrierSplit(issue, openPrs) { // rows simply stopped throwing the labels away. // // Report-only, and pointedly not a judgement that the block is WRONG: waiting -// on a deferred card is sometimes exactly right. The row says this block has no -// mechanism that will ever release it, which is the thing a human should see +// on a deferred card is sometimes exactly right. The row says nobody is +// SCHEDULED to release this block, which is the thing a human should see // rather than discover in a hand sweep. // --------------------------------------------------------------------------- /** - * Target states that can never satisfy the unlock predicate, because they are - * states an OPEN card sits in. `pm:blocked` is deliberately not here — that is - * the chain leg below, and it says something different: the target CAN close, - * once its own blocker does. + * Target states that nothing is SCHEDULED to move a card out of, so nothing is + * scheduled to satisfy the unlock predicate either. ⛔ NOT states a card cannot + * leave — cards close out of both, counted in the block above; they are states + * that lift when a person acts and not on any timer this loop owns. + * `pm:blocked` is deliberately not here — that is the chain leg below, and it + * says something different: the target has a named next step, its own blocker + * closing. */ export const INDEFINITE_TARGET_LABELS = ['pm:on-hold', 'needs-user-decision']; @@ -6865,15 +6907,21 @@ export function h26BlockOnIndefiniteTarget(issue, resolutions) { ? ` +${indefinite.length - H19_TARGET_LIST_CAP} more` : ''; parts.push( - `\`pm:blocked\` on ${indefinite.length} target(s) that can never CLOSE: ${named}${more}. ` + + `\`pm:blocked\` on ${indefinite.length} target(s) that nothing is SCHEDULED to close: ${named}${more}. ` + 'The unlock predicate is "the `Blocked-by:` target closed", and `pm:on-hold` / ' + - '`needs-user-decision` are by definition states a card sits in WHILE OPEN — so this ' + - 'block has NO MECHANISM THAT WILL EVER RELEASE IT. Every existing check passes on this ' + + '`needs-user-decision` lift only when a person acts — no timer, no sweep and no queue ' + + 'in this loop closes either — so this block has NO SCHEDULED RELEASE. ⛔ Deliberately ' + + 'weaker than the categorical claim this row used to make, which counting refutes: on ' + + 'this board 2026-09-18, 14 closed cards carry `pm:on-hold` and 5 carry ' + + '`needs-user-decision`, 9 of the 19 as `completed` (control: `pm:blocked` closed 20, so ' + + 'the closed enumerator fires) — these targets DO close. `PM_RESIDUE_LABELS` ' + + 'presupposes it too — ' + + 'H22 counts `pm:on-hold` as residue ON A CLOSED CARD. Every existing check passes on this ' + 'card (the line is present, the target resolves, the target is open, the label is ' + 'correct), which is why the measured instances were found by a human reading and by no ' + 'gauge; H9 asks the mirror question about the HELD card and nothing asked about the ' + 'WAITING one. ⚠️ Not a claim that the block is wrong — waiting on a deferred card is ' + - 'sometimes exactly right. It says the wait is indefinite BY CONSTRUCTION, so the release ' + + 'sometimes exactly right. It says nobody is SCHEDULED to end the wait, so the release ' + 'has to come from the target\'s own state changing (a ruling answered, a hold restarted) ' + 'and someone has to want that.', ); @@ -23968,11 +24016,12 @@ async function sweepInto(findings, seen, seenPrs, seenMerged, seenUnscoped, seen // waiting on one epic is the normal shape, and it costs one read. // // The map holds the ISSUE, not just its number, because H26 (#11219) asks a - // second question of the same target — is it parked in a state that can never - // close? — and the answer is a field the payload already carried. Free by - // construction: a locally-open target is answered from a listing in hand, and - // a fetched one arrives with its labels on the same response. Nothing here - // adds a request; the resolution rows simply stop discarding the labels. + // second question of the same target — is it parked in a state nothing is + // scheduled to end? — and the answer is a field the payload already carried. + // Free by construction: a locally-open target is answered from a listing in + // hand, and a fetched one arrives with its labels on the same response. + // Nothing here adds a request; the resolution rows simply stop discarding the + // labels. const openLocalIssues = new Map(); for (const [number, issue] of seenUnscoped) openLocalIssues.set(number, issue); for (const [number, issue] of seen) openLocalIssues.set(number, issue); @@ -30493,7 +30542,7 @@ async function selfTest() { t('#16393 summary: a bare line reports the divisor UNCHECKED, not agreed with', saidBy('h22Window', summaryLine({}, 0)).includes('RATE PREMISE UNOBSERVED'), true); t('#16393 summary: …and renders numbers, never `undefined`', saidBy('h22Window', summaryLine({}, 0)).includes('undefined'), false); - // -- H26: a block whose target can never close, + the stale chain (#11219) -- + // -- H26: a block nothing is scheduled to close, + the stale chain (#11219) - // The measured cards, by name, and both directions of every leg. const waiting = (number = 1119) => ({ number, @@ -30517,7 +30566,19 @@ async function selfTest() { ...extra, }); t('H26: target parked in pm:on-hold -> finding', typeof h26BlockOnIndefiniteTarget(waiting(), [tgt(987, ['pm:on-hold'])]), 'string'); - t('H26: …and the row says the block has no releasing mechanism', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('NO MECHANISM THAT WILL EVER RELEASE IT'), true); + // #18017 — the categorical half was RETIRED, and what replaced it is pinned + // POSITIVELY. ⭐ An `absent:`-only check on the retired phrase would stay + // green if a later edit deleted the assertion along with the sentence it + // named — which is the exact failure mode #18017 exists to describe — so + // every retirement below is paired with a present: on the replacement, and + // the row's USEFUL half is pinned verbatim in its own case. + t('H26: …and the row says nobody is SCHEDULED to release the block', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('NO SCHEDULED RELEASE'), true); + t('H26: …and the retired categorical claim is gone', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('NO MECHANISM THAT WILL EVER RELEASE IT'), false); + t('H26: …and the row no longer says the target can never close', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('can never close'), false); + t('H26: …and it carries the counted population that refuted it', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('14 closed cards carry `pm:on-hold`'), true); + t('H26: …with the control, so the count is a reading and not a bare number', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('control: `pm:blocked` closed 20'), true); + t('H26: …and this file\'s own corroboration, not the board count alone', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('`PM_RESIDUE_LABELS` presupposes it too'), true); + t('H26: ⭐ …and the USEFUL half survives verbatim', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('the release has to come from the target\'s own state changing (a ruling answered, a hold restarted) and someone has to want that'), true); t('H26: …and names the target and its state', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('`#987` (`pm:on-hold`)'), true); t('H26: target parked in needs-user-decision -> finding', typeof h26BlockOnIndefiniteTarget(waiting(75), [tgt(68, ['needs-user-decision'])]), 'string'); t('H26: a target carrying BOTH indefinite states names both', h26row(waiting(), [tgt(987, ['pm:on-hold', 'needs-user-decision'])]).includes('`pm:on-hold` + `needs-user-decision`'), true); @@ -30538,14 +30599,15 @@ async function selfTest() { // The chain leg. t('H26: a target that is itself pm:blocked -> the transitive row', typeof h26BlockOnIndefiniteTarget(waiting(1395), [tgt(10101, ['pm:blocked'])]), 'string'); t('H26: …and it says to look one level further', h26row(waiting(1395), [tgt(10101, ['pm:blocked'])]).includes('TRANSITIVE'), true); - t('H26: …and does not claim the block can never release', h26row(waiting(1395), [tgt(10101, ['pm:blocked'])]).includes('NO MECHANISM'), false); + t('H26: …and does not claim the block has no scheduled release', h26row(waiting(1395), [tgt(10101, ['pm:blocked'])]).includes('NO SCHEDULED RELEASE'), false); + t('H26: …nor the retired categorical claim it replaced', h26row(waiting(1395), [tgt(10101, ['pm:blocked'])]).includes('NO MECHANISM'), false); // Both legs at once, on two different targets, in one row. const bothLegs = String(h26BlockOnIndefiniteTarget(waiting(), [tgt(987, ['pm:on-hold']), tgt(10101, ['pm:blocked'])]) ?? ''); - t('H26: both legs report together', bothLegs.includes('NO MECHANISM THAT WILL EVER RELEASE IT') && bothLegs.includes('TRANSITIVE'), true); + t('H26: both legs report together', bothLegs.includes('NO SCHEDULED RELEASE') && bothLegs.includes('TRANSITIVE'), true); // A target that is BOTH parked and blocked is named ONCE, under the reading // that ends the wait forever rather than the one that merely lengthens it. const bothOnOne = String(h26BlockOnIndefiniteTarget(waiting(), [tgt(987, ['pm:on-hold', 'pm:blocked'])]) ?? ''); - t('H26: a parked AND blocked target is named once, as indefinite', bothOnOne.includes('NO MECHANISM THAT WILL EVER RELEASE IT'), true); + t('H26: a parked AND blocked target is named once, as indefinite', bothOnOne.includes('NO SCHEDULED RELEASE'), true); t('H26: …and not a second time as a chain', bothOnOne.includes('TRANSITIVE'), false); // A partially indefinite block still reports: one live blocker does not make // the indefinite one fireable. From 4cc90a6b7920d1536550d34fc93f1b25792e063d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 20 Sep 2026 19:16:18 +0000 Subject: [PATCH 2/3] =?UTF-8?q?fix(pm):=20H26=20claims=20no=20SCHEDULED=20?= =?UTF-8?q?or=20DECLARED=20release,=20not=20"no=20mechanism"=20=E2=80=94?= =?UTF-8?q?=20counted,=20not=20asserted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit H26 asserted that a `pm:blocked` card whose `Blocked-by:` target carries `pm:on-hold` / `needs-user-decision` has "NO MECHANISM THAT WILL EVER RELEASE IT", because those are "by definition states a card sits in WHILE OPEN". The categorical half was never counted, and counting refutes it. Re-run of the filing card's own instrument on this board, 2026-09-20T19:06Z, REST `/issues?labels=LABEL&state=STATE&per_page=100` walked to exhaustion with pull requests excluded: `pm:on-hold` 101 open / 14 closed (7 completed, 7 not_planned), `needs-user-decision` 8 / 15 (9, 6), and the control that fires, `pm:blocked`, 69 / 21 (17, 4). All three closed legs non-zero, so a silently empty `state=closed` leg would have shown as three zeros and did not. The row is miscalibrated, not wrong, so the POPULATION is untouched: every card it fired on before, it fires on now. What changed is the claim. Since #19255 a card can declare a label-transition exit the sweep fires, and H26 already stands down for a target that has one — so the row now says what it actually checked: nothing schedules these targets' close AND this card declared no exit for them. NO SCHEDULED OR DECLARED RELEASE. H26's useful final sentence is preserved verbatim, and the remedy paragraph #19255 added, which names the declarable exit, is untouched. The self-test pins the NEW wording POSITIVELY rather than only the absence of the old, and each retired phrase is pinned in the case the row really wrote it (`can never CLOSE`): a lower-cased paraphrase was already absent before this change and would have pinned nothing. Claude-Session: https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi Co-authored-by: Claude --- scripts/pm/check-half-states.mjs | 129 +++++++++++++++---------------- 1 file changed, 63 insertions(+), 66 deletions(-) diff --git a/scripts/pm/check-half-states.mjs b/scripts/pm/check-half-states.mjs index 802ee6c6b4f..034f6fdbad1 100644 --- a/scripts/pm/check-half-states.mjs +++ b/scripts/pm/check-half-states.mjs @@ -586,20 +586,17 @@ * state-model row (its protocol face) and applying the label to the * specimen card, which is a seat's write. * - * ## H26 — the block nothing is SCHEDULED to release + * ## H26 — the block nothing is SCHEDULED or DECLARED to release * * H26 an open `pm:blocked` card whose resolvable `Blocked-by:` target is OPEN - * and parked in a state nothing is scheduled to end — `pm:on-hold` or - * `needs-user-decision`, which lift when a person acts and not otherwise. - * ⛔ NOT a claim that such a target CANNOT close: that categorical half - * was asserted and never counted, and counting refutes it — cards do - * close out of both states, `completed` more often than not. The census - * and its control live in the row's own rationale block, which is also - * the file's one deliberate citation of the retired words, kept greppable - * for the copy objectui ports. The unlock predicate - * is "the target closed" and no timer, sweep or queue fires it, so the - * block has NO SCHEDULED RELEASE and nothing reported it: the waiting - * card is + * and parked in `pm:on-hold` or `needs-user-decision` with no exit this + * sweep can fire. ⛔ NOT a claim that such a target CANNOT close: that + * categorical half was asserted, never counted, and counting refutes it + * (#18017 — the census and its control live in the row's own rationale + * block). What survives is that no timer, sweep or queue in this loop + * closes either state and, since #19255, that the card declared no + * label-transition exit for this target — so the block has NO SCHEDULED + * OR DECLARED RELEASE and nothing reported it: the waiting card is * perfectly well-formed (H4 clean, target resolves, target open, so H19 * clean, label correct), and H9 — the nearest neighbour — audits the HELD * card rather than the waiting one. Six measured instances, all found by @@ -6887,44 +6884,47 @@ export function h19DeclaredExitFired(issue, resolutions, commentBodies, ownerRep } // --------------------------------------------------------------------------- -// H26 — a block nothing is SCHEDULED to close, and the stale chain (#11219). +// H26 — a block with no scheduled or declared release, and the stale chain (#11219). // -// The unlock predicate is "the `Blocked-by:` target CLOSED". `pm:on-hold` and -// `needs-user-decision` end only when a person acts on the target: no timer, no -// sweep and no queue in this loop closes either one. A block naming such a -// target therefore has NO SCHEDULED RELEASE, and until this row nothing said so. +// The unlock predicate is "the `Blocked-by:` target CLOSED", and since #19255 a +// card can declare a second one: a label-transition exit this sweep fires. This +// row is what is left when NEITHER is in reach — the target is parked in +// `pm:on-hold` or `needs-user-decision`, no timer, sweep or queue in this loop +// closes either state, and the card declared no exit for that target. So the +// block has NO SCHEDULED OR DECLARED RELEASE, and until this row nothing said so. // // ## Why this row no longer says "can never close" (#18017) // // It used to, and that half was an ASSERTION — never counted. Counting refutes -// it. Taken on this board 2026-09-18, REST -// `/issues?labels=LABEL&state=STATE&per_page=100`, pull requests excluded, the -// population being every ISSUE carrying the label: +// it. Taken on this board 2026-09-20T19:06Z, REST +// `/issues?labels=LABEL&state=STATE&per_page=100` walked to exhaustion, the +// population every ISSUE carrying the label — pull requests excluded, which is +// not tidying: 96 of the 111 rows the closed `needs-user-decision` leg returns +// are PRs, so dropping that filter measures another population. // // label open closed closed `state_reason` // `pm:on-hold` 101 14 7 completed, 7 not_planned -// `needs-user-decision` 2 5 2 completed, 3 not_planned -// `pm:blocked` (control) 70 20 16 completed, 4 not_planned +// `needs-user-decision` 8 15 9 completed, 6 not_planned +// `pm:blocked` (control) 69 21 17 completed, 4 not_planned // // ⭐ The control has a known direction and it HIT: the same enumerator returned -// non-zero on all three CLOSED rows, so a `state=closed` leg that silently -// answered nothing would have read as three zeros, and it did not. ⇒ cards DO -// close out of both indefinite states, and 9 of those 19 closures are -// `completed` — the hold lifted and the work finished, which is precisely the -// release the retired wording said could not exist. +// non-zero on all three CLOSED rows, so a `state=closed` leg answering nothing +// silently would have read as three zeros, and it did not. ⇒ cards DO close out +// of both indefinite states, 16 of those 29 closures `completed` — the hold +// lifted and the work finished, the very release the retired wording denied. // // This file presupposed it already, twice, in its own text: `PM_RESIDUE_LABELS` // (H22) carries `pm:on-hold`, and a label is residue only on a CLOSED card; and // H22's own note calls `needs-user-decision` one of the "perfectly good states // for a closed card to have ended in" — the direct converse, one file away. // -// ⚠️ What is KEPT is the half that survives the count: nothing is SCHEDULED to -// close these targets. The overstatement was "no one is scheduled to act" -// rendered as "no mechanism exists", and only the rendering changed. This -// sentence does not stay in a report body — it is quoted outward as a -// structural verdict, and other seats have cited it as grounds to abandon locks -// they would otherwise have opened, so what it claims has to be what the -// numbers support. +// ⚠️ What is KEPT is the half that survives the count, and the POPULATION is +// untouched: every card this row fired on before, it fires on now. The +// overstatement was "no one is scheduled to act" rendered as "no mechanism +// exists", and only the rendering changed. This sentence does not stay in a +// report body — it is quoted outward as a structural verdict, and seats have +// cited it as grounds to abandon locks they would otherwise have opened, so +// what it claims has to be what the numbers support. // // ## Why every existing check passes on these cards // @@ -6969,18 +6969,17 @@ export function h19DeclaredExitFired(issue, resolutions, commentBodies, ownerRep // // Report-only, and pointedly not a judgement that the block is WRONG: waiting // on a deferred card is sometimes exactly right. The row says nobody is -// SCHEDULED to release this block, which is the thing a human should see -// rather than discover in a hand sweep. +// SCHEDULED to release this block and the card declared no exit for it, which +// is the thing a human should see rather than discover in a hand sweep. // --------------------------------------------------------------------------- /** * Target states that nothing is SCHEDULED to move a card out of, so nothing is * scheduled to satisfy the unlock predicate either. ⛔ NOT states a card cannot - * leave — cards close out of both, counted in the block above; they are states - * that lift when a person acts and not on any timer this loop owns. - * `pm:blocked` is deliberately not here — that is the chain leg below, and it - * says something different: the target has a named next step, its own blocker - * closing. + * leave — cards close out of both, counted in the block above; they lift when + * a person acts and on no timer this loop owns. `pm:blocked` is deliberately + * not here — that is the chain leg below, saying something different: the + * target has a named next step, its own blocker closing. */ export const INDEFINITE_TARGET_LABELS = ['pm:on-hold', 'needs-user-decision']; @@ -7033,16 +7032,14 @@ export function h26BlockOnIndefiniteTarget(issue, resolutions, commentBodies, ow ? ` +${indefinite.length - H19_TARGET_LIST_CAP} more` : ''; parts.push( - `\`pm:blocked\` on ${indefinite.length} target(s) that nothing is SCHEDULED to close: ${named}${more}. ` + - 'The unlock predicate is "the `Blocked-by:` target closed", and `pm:on-hold` / ' + - '`needs-user-decision` lift only when a person acts — no timer, no sweep and no queue ' + - 'in this loop closes either — so this block has NO SCHEDULED RELEASE. ⛔ Deliberately ' + - 'weaker than the categorical claim this row used to make, which counting refutes: on ' + - 'this board 2026-09-18, 14 closed cards carry `pm:on-hold` and 5 carry ' + - '`needs-user-decision`, 9 of the 19 as `completed` (control: `pm:blocked` closed 20, so ' + - 'the closed enumerator fires) — these targets DO close. `PM_RESIDUE_LABELS` ' + - 'presupposes it too — ' + - 'H22 counts `pm:on-hold` as residue ON A CLOSED CARD. Every existing check passes on this ' + + `\`pm:blocked\` on ${indefinite.length} target(s) with no scheduled or declared release: ${named}${more}. ` + + 'The unlock predicate is "the `Blocked-by:` target closed"; no timer, sweep or queue in ' + + 'this loop closes `pm:on-hold` or `needs-user-decision`, and this card declares no ' + + 'label-transition exit for them — so this block has NO SCHEDULED OR DECLARED RELEASE. ' + + '⛔ Deliberately weaker than the categorical claim this row used to make, which counting ' + + 'refutes: on this board 2026-09-20, `pm:on-hold` closed 14 and `needs-user-decision` ' + + 'closed 15, 16 of the 29 `completed` (control: `pm:blocked` closed 21, so the closed ' + + 'enumerator fires) — these targets DO close. Every existing check passes on this ' + 'card (the line is present, the target resolves, the target is open, the label is ' + 'correct), which is why the measured instances were found by a human reading and by no ' + 'gauge; H9 asks the mirror question about the HELD card and nothing asked about the ' + @@ -30821,7 +30818,7 @@ async function selfTest() { t('#16393 summary: a bare line reports the divisor UNCHECKED, not agreed with', saidBy('h22Window', summaryLine({}, 0)).includes('RATE PREMISE UNOBSERVED'), true); t('#16393 summary: …and renders numbers, never `undefined`', saidBy('h22Window', summaryLine({}, 0)).includes('undefined'), false); - // -- H26: a block nothing is scheduled to close, + the stale chain (#11219) - + // -- H26: a block with no scheduled or declared release, + the chain (#11219) // The measured cards, by name, and both directions of every leg. const waiting = (number = 1119) => ({ number, @@ -30845,19 +30842,19 @@ async function selfTest() { ...extra, }); t('H26: target parked in pm:on-hold -> finding', typeof h26BlockOnIndefiniteTarget(waiting(), [tgt(987, ['pm:on-hold'])]), 'string'); - // #18017 — the categorical half was RETIRED, and what replaced it is pinned - // POSITIVELY. ⭐ An `absent:`-only check on the retired phrase would stay - // green if a later edit deleted the assertion along with the sentence it - // named — which is the exact failure mode #18017 exists to describe — so - // every retirement below is paired with a present: on the replacement, and - // the row's USEFUL half is pinned verbatim in its own case. - t('H26: …and the row says nobody is SCHEDULED to release the block', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('NO SCHEDULED RELEASE'), true); + // #18017 — the categorical half is RETIRED and its replacement pinned + // POSITIVELY: an absence-only check stays green when a later edit deletes the + // assertion with the sentence it named. ⛔ And each retired phrase is pinned + // in the CASE the row wrote it — `can never CLOSE`, not a paraphrase that was + // already absent before this change and would therefore pin nothing. + t('H26: …and the row says the block has no scheduled or declared release', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('NO SCHEDULED OR DECLARED RELEASE'), true); t('H26: …and the retired categorical claim is gone', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('NO MECHANISM THAT WILL EVER RELEASE IT'), false); - t('H26: …and the row no longer says the target can never close', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('can never close'), false); - t('H26: …and it carries the counted population that refuted it', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('14 closed cards carry `pm:on-hold`'), true); - t('H26: …with the control, so the count is a reading and not a bare number', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('control: `pm:blocked` closed 20'), true); - t('H26: …and this file\'s own corroboration, not the board count alone', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('`PM_RESIDUE_LABELS` presupposes it too'), true); + t('H26: …and so is the retired opening, in the case the row wrote it', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('can never CLOSE'), false); + t('H26: …and it carries the counted population that refuted them', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('`pm:on-hold` closed 14 and `needs-user-decision` closed 15'), true); + t('H26: …with the control, so the count is a reading and not a bare number', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('control: `pm:blocked` closed 21'), true); t('H26: ⭐ …and the USEFUL half survives verbatim', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('the release has to come from the target\'s own state changing (a ruling answered, a hold restarted) and someone has to want that'), true); + // #19255's stand-down outlives the rewording: a DECLARED label exit for the target still takes it out of this row's population. + t('H26: a target with a declared label-transition exit still stands down (#19255)', h26BlockOnIndefiniteTarget({ ...waiting(), body: 'Blocked-by: #987\nUnlock-action: re-check #987 when label pm:on-hold absent' }, [tgt(987, ['pm:on-hold'])], [], REPO_OS), null); t('H26: …and names the target and its state', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('`#987` (`pm:on-hold`)'), true); t('H26: target parked in needs-user-decision -> finding', typeof h26BlockOnIndefiniteTarget(waiting(75), [tgt(68, ['needs-user-decision'])]), 'string'); t('H26: a target carrying BOTH indefinite states names both', h26row(waiting(), [tgt(987, ['pm:on-hold', 'needs-user-decision'])]).includes('`pm:on-hold` + `needs-user-decision`'), true); @@ -30878,15 +30875,15 @@ async function selfTest() { // The chain leg. t('H26: a target that is itself pm:blocked -> the transitive row', typeof h26BlockOnIndefiniteTarget(waiting(1395), [tgt(10101, ['pm:blocked'])]), 'string'); t('H26: …and it says to look one level further', h26row(waiting(1395), [tgt(10101, ['pm:blocked'])]).includes('TRANSITIVE'), true); - t('H26: …and does not claim the block has no scheduled release', h26row(waiting(1395), [tgt(10101, ['pm:blocked'])]).includes('NO SCHEDULED RELEASE'), false); + t('H26: …and does not claim the block has no scheduled or declared release', h26row(waiting(1395), [tgt(10101, ['pm:blocked'])]).includes('NO SCHEDULED OR DECLARED RELEASE'), false); t('H26: …nor the retired categorical claim it replaced', h26row(waiting(1395), [tgt(10101, ['pm:blocked'])]).includes('NO MECHANISM'), false); // Both legs at once, on two different targets, in one row. const bothLegs = String(h26BlockOnIndefiniteTarget(waiting(), [tgt(987, ['pm:on-hold']), tgt(10101, ['pm:blocked'])]) ?? ''); - t('H26: both legs report together', bothLegs.includes('NO SCHEDULED RELEASE') && bothLegs.includes('TRANSITIVE'), true); + t('H26: both legs report together', bothLegs.includes('NO SCHEDULED OR DECLARED RELEASE') && bothLegs.includes('TRANSITIVE'), true); // A target that is BOTH parked and blocked is named ONCE, under the reading // that ends the wait forever rather than the one that merely lengthens it. const bothOnOne = String(h26BlockOnIndefiniteTarget(waiting(), [tgt(987, ['pm:on-hold', 'pm:blocked'])]) ?? ''); - t('H26: a parked AND blocked target is named once, as indefinite', bothOnOne.includes('NO SCHEDULED RELEASE'), true); + t('H26: a parked AND blocked target is named once, as indefinite', bothOnOne.includes('NO SCHEDULED OR DECLARED RELEASE'), true); t('H26: …and not a second time as a chain', bothOnOne.includes('TRANSITIVE'), false); // A partially indefinite block still reports: one live blocker does not make // the indefinite one fireable. From 2e6ee9e581d6afeaa939bbbae5f9d03cf38732c1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 20 Sep 2026 19:23:29 +0000 Subject: [PATCH 3/3] test(pm): place H26's stand-down case with the clean directions, and pin the retired opening in the case the row wrote it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two self-test placements, no behaviour change. The #19255 regression case — a card whose `Unlock-action:` declares a label-transition exit for the target still stands the row DOWN after the rewording — belongs with H26's other clean directions rather than in the middle of the `…and` chain that continues the firing case. Net delta against `origin/main` is +94 / −35 (net +59), inside the budget. Claude-Session: https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi Co-authored-by: Claude --- scripts/pm/check-half-states.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/pm/check-half-states.mjs b/scripts/pm/check-half-states.mjs index 034f6fdbad1..c49d47a7484 100644 --- a/scripts/pm/check-half-states.mjs +++ b/scripts/pm/check-half-states.mjs @@ -30853,12 +30853,12 @@ async function selfTest() { t('H26: …and it carries the counted population that refuted them', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('`pm:on-hold` closed 14 and `needs-user-decision` closed 15'), true); t('H26: …with the control, so the count is a reading and not a bare number', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('control: `pm:blocked` closed 21'), true); t('H26: ⭐ …and the USEFUL half survives verbatim', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('the release has to come from the target\'s own state changing (a ruling answered, a hold restarted) and someone has to want that'), true); - // #19255's stand-down outlives the rewording: a DECLARED label exit for the target still takes it out of this row's population. - t('H26: a target with a declared label-transition exit still stands down (#19255)', h26BlockOnIndefiniteTarget({ ...waiting(), body: 'Blocked-by: #987\nUnlock-action: re-check #987 when label pm:on-hold absent' }, [tgt(987, ['pm:on-hold'])], [], REPO_OS), null); t('H26: …and names the target and its state', h26row(waiting(), [tgt(987, ['pm:on-hold'])]).includes('`#987` (`pm:on-hold`)'), true); t('H26: target parked in needs-user-decision -> finding', typeof h26BlockOnIndefiniteTarget(waiting(75), [tgt(68, ['needs-user-decision'])]), 'string'); t('H26: a target carrying BOTH indefinite states names both', h26row(waiting(), [tgt(987, ['pm:on-hold', 'needs-user-decision'])]).includes('`pm:on-hold` + `needs-user-decision`'), true); - // The clean directions — an ordinary open target is not this row's business. + // The clean directions — an ordinary open target is not this row's business, + // nor is a parked one the card hands a fireable exit for (#19255 stands down). + t('H26: a parked target with a DECLARED label-transition exit stands down (#19255)', h26BlockOnIndefiniteTarget({ ...waiting(), body: 'Blocked-by: #987\nUnlock-action: re-check #987 when label pm:on-hold absent' }, [tgt(987, ['pm:on-hold'])], [], REPO_OS), null); t('H26: an ordinary open target -> clean', h26BlockOnIndefiniteTarget(waiting(), [tgt(987, ['pm:queue', 'domain:devx'])]), null); t('H26: an unlabelled open target -> clean', h26BlockOnIndefiniteTarget(waiting(), [tgt(987, [])]), null); t('H26: no targets at all -> no row (H4 owns the missing line)', h26BlockOnIndefiniteTarget(waiting(), []), null);