pm(half-states): H19 founds every verdict on the set it judges - #18654
Merged
Merged
Conversation
H19 resolved `Blocked-by:` targets over the body∪comment union and judged against the body's set, but the UNJUDGED branch sat on the RESOLUTION step: a target superseded in the body and unreadable in the archive could still veto the verdict, producing a row no correct action by the card's owner could ever clear. Both founding states are now taken over the judged set. What the archive still does — and must, per #11747 — is withhold: an OPEN target it names still refuses a discharge, and an UNREADABLE one refuses a FULL discharge and is named in its own clause. Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Sep 17, 2026
os-justin
marked this pull request as ready for review
September 17, 2026 12:51
This was referenced Sep 17, 2026
fix(pm): make the stamp contract quotable — Markdown code is a quotation, rendered as written
#18730
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #18379
H19 gathered
Blocked-by:targets over the body ∪ comment union and judged the expiry against the body's set (#17564), but theUNJUDGEDbranch never moved with that split: it stayed founded on the resolution set, so a target a seat had superseded in the body could still veto the verdict from the comment archive whenever its number was unreadable. Two conditions had to coincide, which is why it outlived #17564 — the target is superseded (judging correctly ignores it) and the superseded number 404s (resolution cannot dismiss it either) — and the resulting row was unclearable by construction: the card's owner had already done the prescribed thing, and the only remaining lever, editing the archived comments, is the write the protocol forbids because those comments record what an earlier seat measured and why the line was migrated.Authored by
session_01Gqi43smmqjJ5sUrhfoPeKu(Claude Code). Every reading below was taken on this branch atab30706ed, the head this body describes.The two sets, before and after
blockerTargetsForis untouched: DISCOVERY stays a union, because H26 and H28 share those resolutions and a comment-borne target is a perfectly good answer to what they ask. What changed is insideh19BlockOutlivedBlocker.UNJUDGEDUNJUDGED(unchanged)The denominator moved with it: an archive-only unreadable target no longer enters the row's
N of Mcount, which was the other half of the sentence the card quotes (「1 of 2 … the set judged against the BODY's set」 on a card whose body names one target).What the archive is still consulted for, and must be: evidence that the wait may still be RUNNING. An OPEN target it names still withholds a discharge, and an UNREADABLE one now withholds a FULL discharge in a new branch (「No target this row JUDGED is still open, but N target(s) the comment archive names … could not be read at all」). Narrowing that half to the body too would re-create #11747 — the measured mirror defect, where a re-park parks the live blocker in a comment while the body still names the spent one, and a card was released to
pm:queuewhile its real blocker was open and dispatched. Only the power to FOUND a verdict is carrier-scoped; evidence of a live wait never is.⛔
UNJUDGEDis not silenced. Three controls say so live, below.Pins
Battery
H19 judged-set founding— the file's second registered battery (SELF_TEST_BATTERIES), 37 cases registered, pinned at 34;SELF_TEST_BATTERY_FLOORraised1→2, so deleting either battery now fails the roster rather than shrinking a total nobody reads.It floors a NEGATIVE — a row that must not fire — plus the controls that keep the silence scoped:
Blocked-by: #18336, the archive naming#13515, resolved 404) ⇒ discovery still yields both targets, and no row of any kind;UNJUDGEDfires, counted1 of 1, still refusing to be read as a quiet row;UNJUDGEDfires;1 of 1, full discharge;pm:blockedcards: 32 parkBlocked-by:outside the body (invisible to the unlock scan), 16 wait on already-closed upstreams, 7 name no blocker at all #11747's mirror ⇒ an OPEN archive target still withholds, still1 of 2, stillPARTIAL;Two existing pins were re-judged rather than deleted, and both are called out where they sit:
H19 precedence: an UNRESOLVED comment-borne target is NOT silenced by the bodyasserted exactly the row this card measures as unclearable. Same fixture, re-judged to the direction the file's own asymmetry table always declared: it now founds no row beside a body set.H19 superseded: the clause rides the UNJUDGED branch as wellused a fixture whose body named a target that appeared in no resolution. Its unreadable target is now the BODY's own, which is what the pin was always about: a hygiene clause can only ride a row something in the judged set founded.One prose correction rides along: the summary line's H19 shortfall clause promised that every unresolved target is named on its own card's row. That clause was measured false once already (#11218) by promising more than the rows deliver, and a repair that narrows what a row founds owes it a second correction — it now names the archive-only class it no longer founds a row for.
Ablation — from the committed fix, restored by blob hash under a trap
Mutation: the founding filter
state === 'unresolved' && carries(r)reverted tostate === 'unresolved', on disk, atHEAD=ab30706ed.Direction: RED, as predicted, and the reds are the right ones — five negatives plus three counts:
The
gotvalue on the card's own fixture is the measured sentence, reproduced verbatim by the ablated code:Self-test
Run under
scripts/pm/os-verify-lock.sh(VERDICT command-exit 0), as were the ablated run and the live sweep.Live sweep — report-only, from this worktree, after the fix
node scripts/pm/check-half-states.mjsat 2026-09-17T12:25Z, headab30706ed, under the verify lock (VERDICT command-exit 0, held 366s): swept 409 open pm-/p0-labeled issues, 462 half-states, 12 H19 rows.Before — anchor #9857 as the scheduled patrol rewrote it at
2026-09-17T07:50:06Z(four H19 rows, allUNJUDGED, which sort above the body's size trim):11973,11975,15140,15214.After — this run:
#18336) — the two that were always true of it.UNJUDGEDon#13689(HTTP 404) — the same unreadable epic, reached through that card's body. This is the scope note on the card, and it is the control that proves nothing was silenced.UNJUDGEDonobjectstack-ai/cloudtargets (HTTP 403, repo not readable to this credential) — the standing accepted cross-repo class, untouched.numberin the analytics response, and a metric tile ignores theformatstyle it is handed — measured on 17.3.0 #15768, lint: refuse an incoherent dataset measure (aggregate × field type) at authoring time, from the spec's compatibility matrix (lint leg of #16099) #16354):is CLOSED … the block has outlived its blocker.Blocked-by:set is outside what H19 judges, so it rides a row rather than founding one」.Gates
Derived from this worktree with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack scripts/pm/check-half-states.mjs(list re-derived after the commit: identical), every command run, each exit code captured by redirect-then-$?, reconciled with--ran:All 38 exited 0. In full:
skip-changeset:scripts/pm/**is the fast lane — nothing in any package'sfiles[]moves, and the patrol script is not published.Acceptance notes
h26BlockOnIndefiniteTarget) reads the same union with no carrier split at all, so a target superseded in the body can still found an indefinite-wait row there. It is a different row with a different founding question and no measured instance was taken in this act, so it is an observation, not a card. Carrier: whoever next opens H26.Generated by Claude Code