feat(pm): H61 reports a merged PR or closed card still carrying needs:contract-review - #17831
Merged
claude[bot] merged 1 commit intoSep 12, 2026
Merged
Conversation
…s:contract-review` The half-state patrol could see a contract-review gate on an OPEN carrier (H31, H51, H53) and a gate REMOVAL (H35). It could see nothing at the other end: a pull request that MERGED, or a card that CLOSED, with the label still on it. 载体纪律's invariant is 「开着的载体恒 = 真实待审」, so each of those labels still tells a later reader that a real contract review is owed on work already in `main`. H61 is one report-only row per landed carrier, printing the merge or closure stamp and the PR's head sha. It costs no request: merged PRs come from H8's 8-day window and closed cards from H22's 3-day window, whose list rows already carry `labels`, `merged_at` / `closed_at` and `head.sha`. Closed-UNMERGED PRs are excluded by decision (measured: 2 of 68 carriers) and both legs pin the refusal. Exit codes, bands and every other row are unchanged, and the script still never writes a label. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK
This was referenced Sep 12, 2026
claude
Bot
deleted the
claude/issue-17510-h-row-merged-carrier-still-gated
branch
September 12, 2026 10:29
This was referenced Sep 12, 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.
The half-state patrol could see a contract-review gate on an OPEN carrier (H31 compares the card's and the PR's, H51 reads one outliving a verdict on the current head, H53 reads one with no increment behind it) and it could see a gate REMOVAL (H35). It could see nothing at the other end: a pull request that MERGED, or a card that CLOSED, with
needs:contract-reviewstill on it. 载体纪律's invariant is 「开着的载体恒 = 真实待审」, so every one of those labels still tells a later reader — a seat, a post-merge tier audit, any candidate query that forgets to scopestate=open— that a real contract review is owed on work that is already inmain. This adds H61, one report-only row per landed carrier, printing the merge or closure stamp and the PR's head sha, so the post-merge tier audit has a list instead of a memory.It costs no request. Both populations were already in hand by the time the row runs: merged PRs from H8's 8-day window (
listRecentlyMergedPullRequests) and closed cards from H22's 3-day window (listRecentlyClosedIssues), whose list rows already carrylabels,merged_at/closed_atandhead.sha. Exit codes, bands and every other row are untouched — a completed sweep still exits 0 whatever it finds, and the script still never writes a label: clearing a carrier is 清标即落地, a seat's audited write with its provenance comment, and a sweeper striking a review gate would be issuing the verdict.Fixes #17510
Acceptance notes
The director seat's ask, verbatim
From comment 5597753733 on #17040 (os-bill, 2026-09-09T07:11Z, "What the mechanisable fix should now also cover"), quoted exactly as written:
The
pm:*half of that sentence is H22's and stays there. The CARRIER half is H61. The card paraphrases it as 「merged PR / closed card still carrying a gate label」; the sentence above is the actual wording.Premises, re-checked on
origin/mainatf830fa21h51SpeaksAboutexcludes a merged or closed PR by name; H31's population test excludes a merged delivering PR and a closed card by name; H35 reads removal events; H8 readspm:dispatched; H22 readspm:*on closed cards and files no row at all since [finding] H22 files 94 of the patrol's 247 rows against a population the 2026-08-31 ruling says needs no cleanup — the census clause and the row family now disagree inside one file #14072.CONTRACT_REVIEW_LABELis reused — no second spelling, pinned the way H51 and H53 pin it.HALF_STATE_FAMILY_BANDentry, andfamilyRegistryCoverageis the mechanical index that holds the band registry equal to the codes the sweep actually pushes. H61 follows that idiom; adding a lone H61 line to an index that stops thirty-three rows earlier would have been worse than leaving it out.33e07f7c(PR feat(pm): refuse a merge group whose queued PR still carriesneeds:contract-review#17484) is an ancestor oforigin/main. The front end is closed, so this row is residue reporting, not a bypass, and the row says so in its own text.The measured population, 2026-09-12
GET /repos/{owner}/{repo}/issues?state=closed&labels=needs:contract-review, one page per repo, both complete (39 and 29 rows, under the 100-row page size):Oldest closure still wearing the label: 2026-08-20T23:58Z (objectstack), 2026-08-30T17:32Z (objectui). The card named three specimens; the class is an order of magnitude larger.
What the row reaches, and what it does not. H61 reads the two windows the sweep already holds, so a merged carrier is visible for 8 days after it lands and a closed-card carrier for 3. At the patrol's 6-hour cadence that is roughly 32 and 12 consecutive sweeps: every NEW carrier is seen, repeatedly. What ages out is the standing backlog above. That boundary is deliberate — the alternative is a
state=closed&labels=…listing, which is both a new fetch class and a second closed-card reader besidepmLabelListingPath, whosestate=openscoping is the entire mechanism by which maintainer ruling 批 #13 holds. The header records the number so a reader can tell a short reach from a clean board.Work item B — closed-UNMERGED PRs are EXCLUDED
Stated, not folded. Two of the 68 (objectstack #16733 and #14923; objectui 0 — 2.9%) are pull requests closed without merging, carrier still on. They are out of this row for three reasons:
main; the post-merge tier audit is the consumer. A PR closed unmerged delivered nothing and owes no post-merge verdict — the shape H51's own header already calls moot, 「载体不迁移」.listRecentlyMergedPullRequestsSELECTS onmerged_at, and its header says why in as many words: an unmerged closed PR is an abandoned attempt, not a delivery. Widening that selector would change what H8 and H49 are handed — two other rows' inputs, and out of scope here.Both legs refuse them, and both refusals are pinned. Writing that pin is what found a real hole in my own first draft: a closed-unmerged PR arriving from the PULLS listing carries no
pull_requestfield at all, whilestateisclosedandclosed_atis readable — so the card leg accepted it.h61SpeaksAboutClosedCardnow refuses a row carryingheadas well. Live, the two collections never cross; a population test that is only correct because of its caller is one refactor from being wrong.The sha is the HEAD, not the merge commit
The row prints
head.sha. 载体纪律 says a clearing write cites 「所判 head」 and H51 resolves a verdict againstpr.head.sha, so the head is the only sha a contract-review record can be matched on. Measured on the three specimens that filed this card: #16783 head47eea7a8/ merge commit854639b3; #17036 head530469ff/ merge commite4fd55d9; objectui#8779 heada04441f7/ merge commit6cc48c4e. The three shas quoted on the card and in the dispatch are the MERGE COMMITS — a reader searching a thread for one of them finds nothing. Noted, not filed: a card-text inaccuracy, not a defect in any code.Band, and the repo column
inventory. Refused in turn: notgate(that band is for a row that can tell a STRIPPED gate from an ungated one — an absence reading as a green light; here the carrier is present), notstall(nothing is stopped — the increment merged), notstate(whose definition says a LIVE card, while every subject here is merged or closed, ARCHIVE in ruling 批 #13's own word for this population). Being first in line for the body trim is correct for it, and the ledger callout names the family and the omitted count rather than dropping rows silently. The row text is deliberately short for this file for the same reason: it fires per carrier, and 20 of the 23 objectstack merged carriers sit inside the 8-day window today.No repo column: each install sweeps its own repository through
resolveSweepRepo, one token and one board, so this row can never see more than one and the column would be a constant. The two-install figures above are a hand measurement taken for the header, not something a run reproduces.Verification
node scripts/pm/check-half-states.mjs --self-test:: exit 0 — 3748 cases pass (3707 before this change, 41 new). Both directions per the dispatch: a merged PR carrier fires, a closed card carrier fires, an OPEN PR carrier does not (and H51 still speaks about exactly that PR, so nothing fell between the two rows), a merged PR without the label does not, a closed-unmerged PR does not through either leg, unreadablelabelsis excluded rather than read as unlabelled, an unreadable stamp yields no row. The row text is pinned on the invariant, the remedy (清标即落地 / 同笔剥双载体 / the provenance comment / who acts), the "never a label written from this script" clause, the neighbouring rows it defers to, and the fact that it printsCONTRACT_REVIEW_LABELrather than a re-typed literal.EXIT_PREREQUISITE_NOT_METis still 3,HALF_STATE_FAMILY_BANDSstill spellsgate,unregistered,stall,state,inventory,isLoudFindingis false on the row, andfamilyRegistryCoverage().missing.lengthis 0.node scripts/pm/dispatch-gates.mjs --commandsin this worktree were run; each exit code is in the report on [finding] three PRs merged on 2026-09-09 still carryingneeds:contract-review— no H row names a merged PR or closed card that still wears a gate label #17510.scripts/pm/**publishes nothing from any released package, which lint.yml calls the textbookskip-changesetcase in as many words.Out of scope, noted not filed
ObjectQL.findOne/update/deletereturnhookContext.resultunder aPromise[any]declaration — nothing to guard, because nothing is declared #16231 no longer carriespm:queue— the director's row 1 recorded it on 2026-09-09 and it has since been cleared. The carrier is still on, which is why it serves as this row's closed-card specimen.Generated by Claude Code