Skip to content

fix(pm): H26 claims no SCHEDULED or DECLARED release, not "no mechanism" — counted, not asserted - #19426

Merged
os-steve merged 5 commits into
mainfrom
claude/issue-18017-h26-counted-not-asserted
Sep 20, 2026
Merged

os-steve merged 5 commits into
mainfrom
claude/issue-18017-h26-counted-not-asserted

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes #18017
Clause-②: no

H26 told every reader that a pm:blocked card whose Blocked-by: target carries pm:on-hold or
needs-user-decision has "NO MECHANISM THAT WILL EVER RELEASE IT", on the grounds that those are
"by definition states a card sits in WHILE OPEN". The grounds were an assertion. Counting refutes it.
The row is miscalibrated, not wrong, so the population is untouched — every card it fired on before
it fires on now — and only what it CLAIMS has changed: it now claims what it actually checked.

The count, and its control

Re-run of the filing card's own instrument, on this board, objectstack-ai/objectstack, taken
2026-09-20T19:06Z. REST /issues?labels=LABEL&state=STATE&per_page=100 walked to exhaustion, the
population being every ISSUE carrying the label, pull requests excluded.

label open issues closed issues closed state_reason
pm:on-hold 101 14 7 completed, 7 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: all three CLOSED legs came back non-zero, 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, 16 of those 29 closures completed — the hold
lifted and the work finished, which is exactly the release the retired wording said could not exist.

⛔ Excluding pull requests is not tidying: 96 of the 111 rows the closed needs-user-decision leg
returns are PRs. A census that skipped that filter would be measuring a different population, so the
filter is stated in the rationale block beside the numbers rather than left implicit.

Two readings agree on direction. The lead commit's 2026-09-18 reading (carried on this branch, see
below) reproduces row for row on pm:on-hold — 101 open / 14 closed, 7 completed + 7 not_planned —
while needs-user-decision moved 2/5 → 8/15 over the two days between the readings.

Before and after

BEFORE (origin/main a0e62e6, h26BlockOnIndefiniteTarget):

pm:blocked on N target(s) that can never CLOSE: … 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.

AFTER:

pm:blocked on N target(s) with no scheduled or declared release: … 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. …

⭐ H26's final sentence — the half the filing card calls the useful one — survives verbatim, and is
now pinned as such: "the release has to come from the target's own state changing (a ruling answered,
a hold restarted) and someone has to want that."

The same retirement is carried through every H26-owned site in this file, including the one a
line-oriented grep cannot see: the resolution-map rationale in sweepInto, whose "can never / close"
is split across two lines. Sites moved: the header docblock's H26 row, the region banner and rationale,
the INDEFINITE_TARGET_LABELS jsdoc, the emitted string, the sweepInto note, and the self-test.

The pin this card flips, on purpose

origin/main scripts/pm/check-half-states.mjs:30799 pinned the retired sentence POSITIVELY:

t('H26: …and the row says the block has no releasing mechanism',
  h26row(...).includes('NO MECHANISM THAT WILL EVER RELEASE IT'), true);

That case is replaced, not deleted, and three more that named the same phrase (bothLegs,
bothOnOne, the chain-leg negative) move to the new one. What stands in its place is a POSITIVE pin on
the replacement plus a retirement pin per retired phrase — because an absence-only check would stay
green if a later edit deleted the assertion along with the sentence it named, which is the exact
failure mode this card exists to describe.

⚠️ One correction to the lead commit, and it is the same class of defect. The lead pinned the
retired opening as .includes('can never close') — lower case. The row never wrote it that way; it
wrote can never CLOSE. Measured against origin/main's own module, before any change:

includes('can never CLOSE')  = true    ⇐ the spelling the row really used
includes('can never close')  = false   ⇐ the lead's pin: already false on main, so it pinned nothing

A negative pin that was already satisfied before the change is a phantom check: it evaluates, it passes,
and deleting it leaves every gate just as green. This PR pins can never CLOSE.

Ablations — both legs, on disk, restored

Run through scripts/ablation-replace.mjs, which proves the mutation reached the file (anchor count and
git hash-object blob) and proves the restore against HEAD rather than against an exit code.

Run on head 2e6ee9e (file blob c49d47a7484d), in an isolated worktree so no other measurement
shared a tree with the mutation.

ablation mutation result
the replacement phrase is really asserted has NO SCHEDULED OR DECLARED RELEASE. ' +has no release anyone has scheduled. ' + blob c49d47a7484dd50b37d13bb1; 3 cases RED, self-test exit 1
the counted census clause is really asserted closed 15, 16 of the 29 …closed 21 → the same sentence without the counts blob c49d47a7484d7052be9a1f12; 2 cases RED, self-test exit 1

Both restored: blob == HEAD (c49d47a7484d) and git diff HEAD empty, git status --porcelain empty
after each leg. ⛔ No ablation artefact is left in the tree; neither leg is a permanent test.

What was carried forward from the lead commit, and what was dropped

The branch already existed on origin at 9180b90 — the domain:devx seat's aborted dispatch of
2026-09-18, one commit, +92 / −30, no PR, base 86b9000. It is merged, never re-derived and never
force-pushed
: 3746268 is the merge commit, and its history is intact.

Merge conflict, measured rather than assumed. Exactly one textual conflict, and it is a boundary
rather than a rewrite: #19255 inserted the label-transition exit block (UNLOCK_LABEL_EXIT_RE,
unlockLabelExits, h19DeclaredExitFired) directly above H26's banner comment, so both sides edited
that one banner line. Everything else merged textually with no conflict, and the merge reproduces the
lead's delta exactly (+92 / −30 against origin/main).

Kept from the lead: the direction itself (claims no SCHEDULED release, not "no mechanism" — counted,
not asserted); the census-with-control shape in the rationale block; the retirement carried to every
H26-owned site including the two-line-split one; the requirement that the self-test pin the new wording
POSITIVELY; H26's useful final sentence preserved verbatim.

Dropped or rewritten, because #19255 landed in between:

Verification

  • node scripts/pm/check-half-states.mjs --self-testexit 0, 5125 cases (5118 at origin/main
    a0e62e6, measured on a throwaway worktree at that commit; +7 here).
  • SELF_TEST_BATTERY_FLOOR unchanged at 8, roster unchanged, every battery's registered count
    unchanged (H66 183/172, H19 37/34, H65 46/42, H67 152/142, H2/H47/H66 213/184, ISSUE_BODY_LIMIT 55/52, H19/H26 11/10, H68 26/24). The new cases are plain t(...) cases beside H26's existing ones,
    ⛔ not at selfTest()'s tail and ⛔ not a new battery.
  • H26 cases: 26 → 32.
  • #19255 regression case added: a card whose body carries
    Unlock-action: re-check #987 when label pm:on-hold absent still stands the row down for that
    target after the rewrite.
  • Tree-wide grep before rewording (assumption 2, verified): on origin/main, NO MECHANISM has 5
    occurrences and can never CLOSE 2, and every one of them is inside this single file. Nothing
    under .claude/, scripts/ elsewhere, or .github/ pins either phrase, so no other file has to move
    in this PR.
  • The derived gate union (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack,
    41 commands, change set derived by the tool itself) is run with each exit captured before any pipe;
    results are in the os-dev-report comment on finding(pm-gate): H26's "can never CLOSE" premise is asserted, not counted — counting refutes it, and objectui's ported copy cannot be fixed without this one moving first #18017.

skip-changeset: the diff publishes nothing. scripts/pm/** is on the no-publish fast path and appears
in no released package's files[].

Acceptance notes

  • Cost, disclosed: the rendered row grows from 1370 to 1726 characters for a single indefinite
    target. ISSUE_BODY_LIMIT is 262144 bytes and the renderer trims low-band rows first, so this is a
    real if small charge against the anchor body — paid because the sentence is quoted outward and the
    count has to travel with the quote. The full census lives in the rationale block, not in the row.
  • Not edited, deliberately: the remedy paragraph [finding] a pm:blocked card whose Blocked-by target is a needs-user-decision card can never be released — the unlock predicate is "closed", and Unlock-action recognizes exactly one PR-shaped spelling #19255 added quotes a maintainer ruling in Chinese,
    「无机制可唤醒的卡 ⛔ 不 hold」. That is the maintainer's wording about when not to hold a card, not this
    row's structural verdict about a target, and AGENTS.md forbids rewriting a quoted ruling — so it stays
    verbatim.
  • Not edited, out of surface: objectui's byte-pinned port of this file follows through
    scripts/upstream-port-pin.json on the consumer card (objectui#9317 / objectui PR The datasource-admin HTTP family is reachable unauthenticated, read and write — mounts outside the enforceAuth seam #9391); ⛔ no
    divergence is declared here and the pin is not touched.
  • Observation, not filed: the phantom-pin shape corrected above (a negative .includes(...) that was
    already false before the change) is a self-test hazard with no mechanical guard — a case that cannot
    fail reads identically to one that passes. Noted rather than filed because no such pin exists on
    origin/main; the one instance lived only on an unmerged branch and is fixed here. Successor: the next
    PR touching this suite's negative pins.

Generated by Claude Code

…ted, not asserted

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 <noreply@anthropic.com>
One textual conflict, and it is a boundary rather than a rewrite: #19255
inserted the label-transition exit block (`UNLOCK_LABEL_EXIT_RE`,
`unlockLabelExits`, `h19DeclaredExitFired`) directly above H26's banner
comment, so both sides edited the same banner line. Resolved by keeping
#19255's new block whole and taking this branch's banner wording.

The rest of #19255's H26 work — the `exitKeys` stand-down on a target with a
declared label exit, the widened `h26BlockOnIndefiniteTarget` signature and
the `⭐ The exit:` remedy paragraph — merged textually with no conflict. The
follow-up commit rewrites this branch's wording AROUND it, because a row that
now stands down for a declared exit can no longer say no sweep in this loop
can fire anything.

Claude-Session: https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi
Co-authored-by: Claude <noreply@anthropic.com>
…sm" — counted, not asserted

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 <noreply@anthropic.com>
…pin the retired opening in the case the row wrote it

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 <noreply@anthropic.com>
@os-steve os-steve added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 20, 2026 — with Claude

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e0e0be006e41e021117d35f92d846209247093d5

① Derived judgments

② Semver level

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

③ Boundary flags

open_questions empty. Declared file-surface extension accepted: two further H26-owned sites in the same file (the header docblock's H26 row :586–:614 and the sweepInto note) carried the retired premise and moved in the same PR — assumption 2's own rule. Mechanism assumptions: 1 refined (one conflict hunk, the banner), 2 verified and counted (5 + 2 occurrences, all in this file), 3 verified with a new pin, 4 holds with no edit. The PR body quotes the first ablation run's hashes (pre-merge head) and is written once; the final-head hashes are in the report — recorded, not a defect. Disclosed cost: the rendered row grows 1370 → 1726 chars per indefinite target on the anchor body (ISSUE_BODY_LIMIT 262,144 B). Two noted, not filed items with carriers (the phantom-negative-pin shape has no mechanical guard; #19255's quoted maintainer ruling 「无机制可唤醒的卡 ⛔ 不 hold」 stays verbatim by AGENTS.md's rule — it is about when not to HOLD, not this row's verdict).

Implemented-by: claude/issue-18017-h26-counted-not-asserted
Reviewed-by: session_017ETYWqMQD4qMtZzAGovWNi

VERDICT: PASS


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants