feat(pm): H58 — a pm:queue card whose own headings declare it a ruling - #17558
Merged
Conversation
`pm:queue` carries two incompatible meanings — "triaged and registered" and "ready for a dev" — and the card-selection total order reads the second. Three seats measured cards graded into the pool that were rulings or records; the price of one instance is a dev round. Adds one report-only row to the half-state patrol. It matches a frozen, heading-anchored marker set, each anchor beside the live card it was measured on, over the `pm:queue` label page the sweep already lists, reading the comment thread only where another row already bought it. Exit codes unchanged; nothing blocked; no label written. The remedy names the four states that already exist — `tracking`, `needs-user-decision`, closed, or a corrected `pm:queue` — because every measured instance resolved into one of them. No fifth state is proposed. Two Zone-2 readings moved the anchor set: "Both are dispositions" is PROSE on the card it was measured on, not a heading, so it is recorded and refused; and both handover cards carry the heading "Two rulings of mine you may want to overturn", which is the spelling pinned in its place. Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK Co-authored-by: Claude <noreply@anthropic.com>
os-litant
marked this pull request as ready for review
September 10, 2026 23:18
os-litant
enabled auto-merge
September 10, 2026 23:19
This was referenced Sep 11, 2026
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 #17417
One new report-only row on the half-state patrol — H58 — and nothing else.
scripts/pm/check-half-states.mjsis the whole diff: one file, +510 lines, 0 deletions.What the row reads
pm:queuecarries two incompatible meanings — 「triaged and registered」 and 「ready for a dev」 — and the card-selection total order reads the second. H58 names an openpm:queuecard whose own section heading declares the deliverable is a ruling, a decision or a record, so the triage seat's per-fire read can dispose it into a state that already exists.tracking,needs-user-decision, shut as completed, or a correctedpm:queue— and proposes no fifth.pm:queuelabel page the sweep already lists (SEEN_LABEL_PAGES); the body rides in on that payload; the thread is read only out ofcommentCache, never fetched. The body-only vs body+thread split is disclosed on the summary line, because a per-card UNJUDGED row on every queued card would flood the board rather than inform it.[Decision]title against the same label; H58 reads the body and the thread. A card wearing both fires both, and a self-test pins that.The anchor set — measured as HEADINGS, frozen, capped
H58_RULING_MARKER_ANCHORS, three entries under a declared cap of five, each frozen beside the live card it was measured on.Anchor 1 — measured on objectui#8449
Anchor 2 — measured on objectui#8365
Anchor 3 — measured on objectui#7089 and objectui#7233
Matching is line-anchored on an ATX heading outside fenced code (
^up to three spaces, one to six#, whitespace, text; markdown decoration removed). A marker quoted in prose, in a table, in a code span, in a blockquote or inside a fence does not fire — this card's own body quotes all three spellings and correctly produces no row.Zone-2 readings, measured before acting
(a) The pre-edit state, on
9540590b.grep -cfor the three marker phrases in the sweeper: 0. Newest registered row:H57: 'stall'.--self-testbefore the edit: 3367 cases pass; after: 3462 cases pass (+95).(b) The live heading spellings — and two of the dispatch's premises moved. One REST GET per card, no retry, five cards:
Two corrections, and the live spelling wins both times:
PROSE_BLOCKER_ANCHORS's own rule, for its own reason. It is recorded in the banner and deliberately absent from the set, and the self-test exercises it as a negative precisely because it must not fire.Also measured, and worth the seat's attention: all five instances have already been disposed — objectui#8449 is no longer open; objectui#8365 now carries
needs-user-decision; objectui#8429 now carriespm:blocked; objectui#7089 and objectui#7233 now carrytracking. None of them carriespm:queuetoday, so none would fire now — the row is forward-looking, and a quiet board on these five is the card's own datum holding rather than a broken row.(c) Negative controls, all pinned as cases. The filing card's own body shape — all three spellings, in italic prose and a table cell, under headings of its own — produces no row, with a positive control asserting the fixture really carries the words. A marker inside a fence (backtick and tilde spellings) produces no row. An ordinary queued card produces no row. Fourteen negatives in a length-pinned table, driven whole.
(d) The band, argued on the refused bands' own criteria. Registered
H58: 'state'. ⛔ Notgate— that band is for an absence reading as a green light on a possibly-stripped gate; here both carriers are present and legible and they disagree (H54's refusal, for H54's reason). ⛔ Notstall— both halves of that criterion fail: whether such a card is stopped is unmeasured by this row (it sits in the dispatch pool and any seat may pick it up), and something else does move it — the triage seat's per-fire read, which is exactly what disposed all five measured instances. ⛔ Notinventory— the row alarms about one card; the population reading is a summary clause and takes no band at all (H39's shape). What is left isstate's criterion exactly, and it is H54's band.The live sweep
One report-mode sweep of
objectstack-ai/objectstackon24cdcf9c, through the proxy re-exec, exit 0. The new clause renders unconditionally:The family ledger shows the row's one finding, and that the body trim ate it:
That one row is objectstack#16245, a genuine live specimen on this board — an open
pm:queuecard whose body carries:A spelling neither the filing card nor the dispatch measured, matched because the anchor is the distinctive head of the measured heading rather than the whole line. Identified by re-running the exported
rulingMarkerHeadingsover the samepm:queuelisting (268 cards, 1 hit) rather than by a second sweep. ⛔ No comment was posted on that card: it is triage input, and the disposition is the triage seat's.Ablation — both mechanisms proved failable, on disk, and restored
Each leg: mutate, prove the mutation reached disk by occurrence count and by
git hash-objectagainst the HEAD blob, run, restore withgit checkout HEAD --, prove the restore by blob equality and an emptygit diff HEAD. Absolute-pathtrap … EXIT INT TERMthroughout. There is no build step for this script — it is run directly from source, so there is nodist/leg to preflight, and the on-disk proof is the whole of it.Predicted direction for both: red. Observed: red, both, with a clean separation.
A first attempt at leg 1 aborted before running, because its guard demanded the injected text occur exactly once and the file already contained an identical line in
proseBlockerRefs. The guard was wrong, not the ablation; it was corrected topre-existing + 1, and the trap had already restored the tree. Recorded because a guard that fails toward "refuse" is the one direction that never produces a false reading.Four-axis note on the one judgement — the anchor set's breadth and the band
The single judgement here is how wide to make the frozen set and where the row sits in the trim, and the four axes point the same way. 实际业务需求: every anchor is a spelling measured on a live card, and the one candidate that was measured in prose is refused rather than admitted for symmetry — the live sweep then found a sixth real carrier on this board with a spelling nobody predicted, which is the measurement, not a hope. 项目长远合理性: the row is triage input under an existing state vocabulary, so the contract-first direction is served by a reader that makes an existing state reachable rather than by a protocol change that adds one. 防 AI 写代码犯错: the tight direction is the strict one — heading-anchored matching refuses prose, tables, code spans, blockquotes and fences structurally, and a lenient substring scan would have made the patrol loudest on precisely the careful cards that explain the shape (this card among them); a
stateband keeps the row below the twogatefamilies, so nothing that can tell a stripped gate from an ungated card is displaced by it. 创业阶段不扩散需求: three anchors, cap five, ⛔ no new label, ⛔ no new state, ⛔ no new listing and ⛔ no new request — the row buys nothing the sweep was not already paying for, and a sixth anchor is deliberately an argument rather than a one-line edit.Verification
Gate families derived from the final diff by
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack— no paths, so the script derives its own change set from the merge base, and it printed change set derived from git — 1 path(s) vs merge base 9540590. All 39 were run; every exit code was captured before any pipe; reconciliation with--ran:39 of 39 exit 0. Their own verdict lines, for the families this diff actually moves:
Beyond the derivation:
pnpm lint—eslint . --no-inline-config, the repo-wide union, ⛔ not a narrowing — on24cdcf9c: exit 0, no output.npx eslint --no-inline-config scripts/pm/check-half-states.mjs: exit 0.grep -naPfor raw ASCII control bytes on the edited file: no match, exit 1.node scripts/pm/check-governed-merges.mjs --branch claude/issue-17417-queued-card-ruling-marker: exit 0 —✅ NOT governed — ordinary queue landing applies to a PR with exactly this file list.node scripts/pm/check-half-states.mjs --probe: exit 0,transport prerequisite met.Changeset: none. Nothing published moves —
scripts/pm/**is in no package'sfiles[], and the diff touches one file under it. Theskip-changesetlabel is applied on this PR and the label set is read back after the write.Acceptance notes
noted, not filed:objectstack#16245 is a live carrier of exactly this shape on this board — open,pm:queue, and its body carries the heading quoted above. It is the row's output, not a defect of its own: the triage seat's per-fire read is the named carrier, so the row itself is the hand-off and ⛔ no card was opened for it. Successor: the triage seat, on its next fire.noted, not filed:the sweeper's markdown body trim ate the one H58 row it computed (computed 1, rendered 0on the live run) while the family ledger still named it. That is the ledger mechanism working as designed, not a defect — but it does mean a first reader of the anchor issue sees the family and not the card. Successor: whoever next revisits the trim's reserved-section budget.noted, not filed:PR feat(gates): give the closing-keyword parity gate a --body pre-flight mode #17552's--bodylint mode has not landed onmain—scripts/check-closing-keyword-parity.mjscarries no such flag at9540590b— so this body was written to the discipline by hand instead: outside fenced blocks it contains zero closing verbs anywhere after the first line, which is stricter than "none beside a card number" and is checkable by grep. Successor: that PR's author.Clause-②: no — a report-only patrol row; no accept set and no public surface moves.
Generated by Claude Code