Skip to content

pm: two report-only half-state rows — H45 (pm:epic with pm:queue) and H46 (an open PR closing a card with no claim) - #15690

Merged
os-steve merged 2 commits into
mainfrom
claude/issue-15667-half-states-epic-and-claimless-pr-rows
Sep 5, 2026
Merged

pm: two report-only half-state rows — H45 (pm:epic with pm:queue) and H46 (an open PR closing a card with no claim)#15690
os-steve merged 2 commits into
mainfrom
claude/issue-15667-half-states-epic-and-claimless-pr-rows

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #15667

Two report-only rows on scripts/pm/check-half-states.mjs, both in the state band. No other file changes; report-only exit semantics are unchanged (a completed sweep still exits 0 whatever it found).

H45 — reserved and handed over at once

An open card carrying both pm:epic and pm:queue. H3's shape and H3's cost: a pure label intersection, no threshold, no timestamp. pm:epic says a named epic PM has reserved the card; pm:queue says it has been handed over and any seat may take it. Both at once and each reader is right about the label it read.

Carriers are listed by construction — every pm:queue carrier is on a label page the sweep already pages — so the row buys no request and needs no gathering policy.

H46 — implemented without a claim

An open pull request (drafts included) whose body binds a closing keyword to an open card that either

  • (a) still carries pm:queue — published as available while a PR is already set to close it; or
  • (b) has no comment that is a Claim: whose Branch: line names the PR's head branch.

Part of #N is not a closing keyword and never fires the row.

Nothing is re-spelled. The keyword extraction is closingKeywordTargets — H7's and H21's, so the three rows can never disagree about what GitHub will act on. The claim half is CLAIM_COMMENT_MARKER and the branch half is claimedBranches, the pair scripts/pm/check-clause2-carriers.mjs reads. No existing row's predicate or wording is touched.

Placement is load-bearing. H46 is judged after H44, because leg (b) adds threads to the shared comment cache and H44's corpus is whatever the rows above it already fetched. Judging H46 first would widen H44's input and change what an existing row reports.

Three unjudged states, each silent rather than clean (#4690): a bound number the sweep cannot see as an open card (closed, a PR, or beyond the listing ceiling); a thread that could not be read; and a PR with no head ref, on leg (b). The new summary clause states the coverage pair and says the count is a lower bound.

Live run — both repos, on this branch

GITHUB_TOKEN=… PM_SWEEP_REPO=OWNER/REPO node scripts/pm/check-half-states.mjs, at commit 88d7a4f2d.

On the sha. Every reading in this body — the sweeps, the gate battery, pnpm lint, the ablation — was taken at 88d7a4f2d. The pushed head is 13db40aac, a merge of current main made so the branch could advance without a force-push. The two are the same tree: git rev-parse 88d7a4f2d^{tree} and git rev-parse 13db40aac^{tree} both answer e0a69d2d5f0e34e8fc4a9c586b89fb6c5e4913ac, and git diff --stat 88d7a4f2d 13db40aac is empty — so the readings are about the bytes that were pushed, not a tree that has moved. Re-confirmed on the pushed head anyway: ✓ check-half-states self-test: 2223 cases pass. (exit 0) and check-nul-bytes: OK (scanned 7583 text file(s) … no raw ASCII control bytes) (exit 0).

repo H45 H46 H46 coverage
objectstack-ai/objectstack 0 17 31 of 31 bound card thread(s) read
objectstack-ai/objectui 0 1 6 of 6 bound card thread(s) read

H45 reads 0 on both boards — measured, not assumed: no open card currently carries both labels, which is the state the sibling rule card describes as correct.

The 17 H46 rows in objectstack are all leg (b); leg (a) fired on nothing. PRs flagged:

Corroboration. Three of them were checked against the live board by hand, and H2 independently files assignee set but no claim comment on the thread on the very same cards (#15321, #14025, #15068 — rows in the same sweep output). #15321's thread carries a comment reading Claiming this card. in prose, which is exactly the shape the maintainer's 2026-08-11 ruling makes non-machine-readable. So the row is reporting real half-states, not parser noise.

A live control for the clean direction: this PR's own body binds Fixes #15667, and #15667 carries pm:dispatched with a comment whose first line is Claim: and whose Branch: line names this PR's head branch — so H46 is silent about this PR. The clean leg is exercised by the live board, not only by fixtures.

H45's second half — measured, and left out

The card conditions the parent half on costing at most one read per pm:epic card. Measured 2026-09-05 against the live API:

It is left out on the other condition, the budget one — no new fetch class beyond what the sweep already reads for PRs and cards:

  1. The sub-issues endpoint family is called nowhere in this file. Its endpoints today are the label and unscoped issue listings, the open-PR listing, issue comments, a single issue, a single PR, PR changed files, PR reviews, commits and issue events. /issues/{n}/parent is a new class, and the card anticipated exactly this (if H45's second half needs the sub-issues API, measure and say).
  2. There is a second cost the request count hides: pm:epic is not one of the seven label pages the sweep fetches, so the population does not exist yet. Folding a pm:epic page into seen would widen the input of every other row that reads it — H1, H2, H4, H9, H11, H24, H25, H29, H30 — which is a change to what existing rows report, and this card forbids that.

So per the card's own disposition the half is left out and the cost is reported for the seat to file. The absence is disclosed in the H45 header as an unread surface, never a clean reading.

Verification

Every verdict below is the line the check itself printed; exit codes were captured before any pipe.

  • node scripts/pm/check-half-states.mjs --self-test — exit 0, ✓ check-half-states self-test: 2223 cases pass.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 31 commands from the real changeset; all 31 run, results below.
  • 30 of 31 exited 0, each verdict line captured with the exit code read before any pipe (cmd > log 2>&1; EXIT=$?). The ones that print a verdict: ✓ check-half-states self-test: 2223 cases pass. · ✓ dispatch-gates self-test: 1445 cases pass. · ✓ check-closing-keyword-parity --self-test: 24 assertions, 5 mutations of the shipped parsers each driven to red. — the parity guard extracts closingKeywordRe() from the shipped bytes, so a green there is the statement that this PR did not move the keyword grammar · ✓ check-partof-closing-keyword self-test: 28 cases pass. · ✓ check-self-test-wired: every one of the 171 script(s) CI runs that ship a --self-test has that self-test run by CI. · ✓ check-nul-bytes --self-test: 75 assertions over a temp git repo (real scan() path) · ✓ check:entry-guard: 214 scripts/ file(s) … · ✓ check:parse-guard: 213 scripts/ file(s) … · ✓ comment-mask corpus sweep …: 5970 files, 0 disagree, 0 unparseable. The whole battery ran under bash scripts/pm/os-verify-lock.sh, which reported VERDICT command-exit 0 · held the lock 771s (12m51s) · waited 36s — shared-box seconds, as the wrapper itself says.
  • pnpm check:partof-closing-keyword / node scripts/check-partof-closing-keyword.mjs — exit 2, NOT WIRED — neither PR_BODY nor PR_NUMBER is set. Recorded as NOT MEASURED, not as a pass; re-run with this PR's own body: with PR_BODY set to this very body it exits 0, ✓ check:partof-closing-keyword: this PR carries no Part-of/closing-keyword contradiction.
  • pnpm lint (eslint . --no-inline-config, the whole repo — the style authority of record) through the same lock wrapper: exit 0, eslint printed no problems. VERDICT command-exit 0 · held the lock 112s (1m52s) · waited 347s (5m47s). This is the full farm run, not a narrowing.
  • Control bytes: grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the edited file — exit 1, zero hits (pnpm check:nul-bytes green above).

Ablation — three legs, each proving a new row can go red

Each leg was mutated on the committed tree, the mutation was confirmed on disk (grep count of the removed anchor fell to 0 and of the injected text rose to 1, and the file's git hash-object moved off its HEAD blob), the suite was run, and the leg was restored with git checkout HEAD -- "$REPO_ROOT/scripts/pm/check-half-states.mjs" — the path absolute, resolved from git rev-parse --show-toplevel, and HEAD named so the restore cannot read back the mutation through a written index — under a trap … EXIT INT TERM. Restoration is proven by git diff HEAD naming no file and the hash returning to the HEAD blob d6cad42a02647aad9037377863b4748a667195b4 — not by the exit code of the restore. This script has no build step, so there is no dist/ leg.

leg mutated red cases first case named
H45's label pair 3 of 2223 H45: `pm:epic` + `pm:queue` -> finding
H46 leg (a), the pm:queue read 8 of 2223 H46: a `pm:queue` card -> finding
H46 leg (b), the claim/branch match 5 of 2223 H46: a card whose only `Claim:` names another branch -> finding

Self-test battery

Both directions for each row, in the file's own style — pm:epic alone, pm:epic + pm:dispatched, pm:epic + pm:queue, either label order, a third state alongside, and both adjacency cases against H3; for H46, a card claimed on this branch (clean, including the blockquote and Claimed:/Branches: spellings), a pm:queue card (fires), a claim naming another branch (fires), Part of only (clean), a keyword in backticks (clean), a bound number with no open card (unjudged), unconsulted and unreadable threads (unjudged), the per-card-number binding, the gathering policy, the list cap, the band, the registry-coverage proof, the count keys and the summary clause.

Not governed, skip-changeset

scripts/pm/** publishes nothing from any package, so the label applies and is set on this PR. Draft, targeting main; the seat reviews and flips it.


🤖 Generated with Claude Code

https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox


Generated by Claude Code

H45 flags an open card carrying both `pm:epic` and `pm:queue` — reserved by a
named epic PM and offered to every seat at once. A pure label intersection over
the `pm:queue` page the sweep already reads, H3's shape and H3's cost.

H46 flags an OPEN pull request (drafts included) whose body binds a closing
keyword to an open card that either still carries `pm:queue`, or has no comment
that is a `Claim:` whose `Branch:` line names the PR's head branch. The keyword
extraction is H7's and H21's `closingKeywordTargets`; the claim pair is
`CLAIM_COMMENT_MARKER` + `claimedBranches`, the predicates
`check-clause2-carriers.mjs` reads. Nothing is re-spelled.

Both rows are report-only and join the `state` band. H46 is judged AFTER H44 so
the threads it buys cannot widen H44's corpus and change what an existing row
reports.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 5, 2026
@github-actions github-actions Bot added the size/m label Sep 5, 2026
…ess-pr-rows

Brings the branch onto current main so the verified tree and the pushed tree are
the same tree; no conflict and no content change on this branch's one file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox

os-steve commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

ACCEPT — contract-tier in-seat review of PR #15690, head 13db40aac (skills seat, 2026-09-05T03:3xZ).

Implemented-by: claude/issue-15667-half-states-epic-and-claimless-pr-rows (os-dev subagent)
Reviewed-by: session_019RfFHiRCSs3JXLK4cwcfox (skills seat)

Verified in a detached compare worktree at the PR head against merge-base a55efc6c1, not from the report: one file, scripts/pm/check-half-states.mjs, +309 / −3, the three removed lines being the header's registry-size arithmetic. H45 is a pure label intersection (pm:epic + pm:queue) over the cards the sweep already lists, H3's shape and cost; H46 flags an open PR, drafts included, whose body binds a closing keyword to an open card that still carries pm:queue or has no Claim: comment whose Branch: line names the PR's head branch — the keyword extraction is closingKeywordTargets (H7/H21's), the claim pair is CLAIM_COMMENT_MARKER + claimedBranches (the pair check-clause2-carriers.mjs reads); no marker respelled, no existing row's predicate or wording moved, both rows in the state class, H46 judged after H44 so H44's corpus is unchanged. Seat runs on the head: --self-test 2223 cases pass (exit 0), check-governed-merges --test exit 0 — not a governed surface; no model names in the diff. The dev's evidence: 31 derived commands through the lock, 30 exit 0 and the one NOT WIRED gate re-run green with the PR's own body; whole-repo lint exit 0; three ablations on a committed head each turned the new rows red (3 / 8 / 5 cases) with restore proved by blob hash; live run at the same tree over both boards — objectstack H45 0 · H46 17 (all leg b; three verified by hand and corroborated by H2), objectui H45 0 · H46 1 (leg a: draft PR #7621 binds #7443, still pm:queue) — and this PR's own Fixes #15667 is silent under H46 because the card's Claim: names this branch. CI on 13db40aac: 34 check runs, 26 success, 8 skipped, none failing, the live half-state sweep among them.

H45's optional parent half is left out on the card's own budget condition — the sub-issues parent read is one call per pm:epic card, but it is a new fetch class and a new population outside seen; the seat files that as its own card. The 17 + 1 live H46 rows are board state for the next patrol, not this PR's to fix.

Landing regime: pure code ⇒ ready + auto-merge in this same act (the queue's SQUASH governs whatever method the tool reports); closes #15667.


Generated by Claude Code

@os-steve
os-steve marked this pull request as ready for review September 5, 2026 03:33
@os-steve
os-steve enabled auto-merge September 5, 2026 03:34
@os-steve
os-steve added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit b2a5c93 Sep 5, 2026
36 checks passed
@os-steve
os-steve deleted the claude/issue-15667-half-states-epic-and-claimless-pr-rows branch September 5, 2026 04:26
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