tooling(pm): H48 — a governed PR at ACCEPT that is missing its label or its maintainer brief - #15956
Conversation
…r its 维护者速读 brief The seat's verdict on a governed PR is a HANDOFF, not a merge, and the handoff is one act with two writes: `needs-user-decision` puts the PR in the maintainer's review inbox, and a `## 维护者速读` comment makes reading it a two-minute job. One act with two writes has exactly two ways to come apart, and this report-only row is both of them — accepted and unfindable, or findable and unread. Population is the open PRs the sweep already lists, narrowed by the imported `governedPathsIn` matcher over the changed-file pages H36 already fetched — never a hand list of paths. Drafts are IN (a governed draft is the correct terminal state of that regime and still half a handoff); merged and closed are out. A governed PR with no verdict is CLEAN: it is not the maintainer's yet. The brief and the verdict are read from the PR's own issue-comment thread, and that is a NEW fetch class: `commentCache` holds card threads only (every listing feeding it filters `!i.pull_request`). It is declared as H36 and H43 declare theirs, bought one thread per GOVERNED OPEN PR — 11 of 31 on the board this landed against — and cached so a future PR-comment reader pays nothing. A thread that failed or reached the page ceiling is UNJUDGED rather than short: a partial read can only invent a missing half. The `## 维护者速读(草稿)` section in a PR BODY is the dev's draft, written before the review happened, and never satisfies the brief half — the row reads comments only and says so in its own sentence. Markers mirror `CLAIM_COMMENT_MARKER`/`RELEASE_COMMENT_MARKER` in three properties (line-anchored `m`, optional leading blockquote, no `g`) and deliberately not in the fourth: no `i` flag, for the reason `RELEASE_COMMENT_MARKER` states — a rule that landed with no dialect behind it starts at the canonical spelling and nothing wider. Self-test 2341 -> 2414 cases, none dropped. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
…lf-states-governed-pr-brief-row
|
ACCEPT — lands #15895 as ruled. Pure code (one file under What the seat verified, at head
The out-of-scope item, declined as a card: a governed PR carrying Next: #15899 (same file) dispatches when this lands. Implemented-by: os-dev executor, flight #15895, branch Generated by Claude Code |
Fixes #15895
One new report-only row in
scripts/pm/check-half-states.mjs— H48, bandstate. One file; nothing else moves.What the row reads
A seat's verdict on a governed-surface PR is a handoff, not a merge: Prime Directive 14 reserves the landing for the maintainer, so what a seat owes at ACCEPT is a complete handoff — and the rule that landed today makes that one act with two writes. The
needs-user-decisionlabel puts the PR in the maintainer's review inbox; a## 维护者速读comment is what makes reading it a two-minute job rather than a diff review.One act with two writes has exactly two ways to come apart, and this row is both of them:
No verdict at all is CLEAN, deliberately: a governed PR no seat has accepted yet is not the maintainer's, and nothing here asks anyone to hurry a verdict.
The
## 维护者速读(草稿)section a dev may carry in the PR body never satisfies the brief half — the draft is written before the review happened, while the brief is the seat's own word after it. The row reads comment threads and nothing else, and says so in its own sentence where a reader would otherwise assume a bug.Population, and the fetch class it declares
Population is the open PRs the sweep already lists from
/pulls, narrowed by the importedgovernedPathsInmatcher over the changed-file pages H36 already fetched — never a hand list of paths, and never a third matcher beside the one the queue guard enforces. A draft is IN (a governed draft is the correct terminal state of that regime and still half a handoff); merged and closed are out (the handoff is over and nothing here is fixable after it).The verdict and the brief both live on the PR's issue-comment thread, and that is a genuinely new fetch class:
commentCacheholds CARD threads only — every listing feeding it filters!i.pull_request, which is the same fact H44's clause states from the other side. So it is declared exactly as H36 and H43 declare theirs:bought one thread per governed open PR — 10 of 33 on the board this landed against, one page each — and cached in
prCommentCacheso a future PR-comment reader pays nothing for a thread this row already bought. A thread that failed, or was still full at the page ceiling, is UNJUDGED rather than short: a partial read can only invent a missing half, never miss one, and #4690's rule is that an input which could not be read must never render as one that was clean.Two bounds ride in the summary clause rather than in a comment nobody reads: a PR whose changed-file page went unread is not in this population at all, and a register that will not load (a sibling-repo install carries no governed pair) puts the whole row at
NOT MEASURED, as H43 already does.Markers
ACCEPT_VERDICT_MARKERandMAINTAINER_BRIEF_MARKERmirrorCLAIM_COMMENT_MARKER/RELEASE_COMMENT_MARKERin three properties — line-anchored with themflag, an optional leading blockquote, and nogflag — and deliberately not in the fourth. There is noiflag, for the reasonRELEASE_COMMENT_MARKERstates in its own docblock: a rule that landed with no dialect behind it starts at the canonical spelling and nothing wider, so an unboldedACCEPT, a lowercase**Accept**,**ACCEPTED**(a word about a different act) and a###sub-heading are malformed writes rather than unrecognised dialects. Each of those is pinned as a case.Evidence
Union re-run on the final tree, at
ea5b4f3912.node scripts/pm/check-half-states.mjs --self-test— 2341 → 2414 cases, 0 failures. The two case-name sets were diffed: 73 added, 0 dropped.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 32 families for this file list; all 32 run, all exit 0;--ranreconciles:✓ dispatch-gates --ran: 32 derived famil(ies) accounted for — 32 run, 0 NOT-MEASURED.node scripts/pm/check-governed-merges.mjs --test scripts/pm/check-half-states.mjs→ exit 0,✅ NOT governed.pnpm lintthroughscripts/pm/os-verify-lock.sh(slotissue-15895):VERDICT command-exit 0.check-clause2-carriers,ci-failure,check-governed-merges,dispatch-gates,check-test-completeness,check-partof-closing-keyword.Ablation — three legs, each restored and proved by blob hash
Every leg asserts the tree is at the HEAD blob before mutating, proves the mutation landed by counting the anchor text it meant to change in both directions, then restores with
git checkout HEAD -- PATHand proves the restore by blob-hash equality and an emptygit diff HEAD.findings.pushremovedemitted.includes('H48'), and the registry invariant from both sides (no band names a family the sweep never emits)Restore proof, identical on all three legs:
RESTORED_BLOB=ea5a25afd032d963db10b3c50d3285487ddbb2d1= the HEAD blob,git diff HEAD --quietexit 0. There is no build leg: this script is run directly from source bynode, with nodist/on the resolution path.Live sweeps, read-only, before and after
objectstack-ai/objectstackMaintainer briefs (H48): 11 of 11 governed open PRs judgedobjectstack-ai/objectuiMaintainer briefs (H48): 1 of 1 governed open PRs judgedThe other families move a little between the two runs because the live board moved (two governed PRs merged mid-session); no family's movement is attributable to this diff, which adds one row and touches no other predicate.
The zero is a measured zero, not a quiet one. Reading the ten governed open PRs in the population one by one, with the shipped predicate over their real comment threads:
#15946,#15837,#15814,#15626,#15612,#15427) and are clean by the rule above. Feeding each of them one synthetic ACCEPT comment makes all six fire, so the row is demonstrably capable of firing on this board.#15885and#15382are the 代读 shape — label and brief with no seat verdict — and stay clean with or without a synthetic verdict, because both halves are already there.objectui#7749) likewise carries verdict + label + brief and reads clean.Deliberately left out
Whether a governed PR carrying
needs-user-decisionwith no verdict behind it is itself a half-state — the 代读 shape, live on two PRs today — is not this card, and it is not implemented here. It looks worth a card to me: the label is a claim on the maintainer's attention, and one placed without a seat verdict behind it is a different assertion from the one this row patrols. Filing it is the PM's call, not a fix to smuggle into this diff.skip-changeset: this file publishes nothing from any package.Generated by Claude Code