Uh oh!
There was an error while loading. Please reload this page.
Widen the half-state patrol to the states the board actually produces: H24/H25/H26, sibling-repo adoption, and re-derived windows - #11294
Merged
Conversation
… the paired-write remedies (#11196) The two-views contradiction gets a row of its own: an open card carrying `pm:queue` with a non-empty assignee is dispatchable to the queue view and taken to the claim rule at the same time, so nobody can legally move it. Pure intersection of two fields — no threshold, no identity test — because the field carries both dead agent claims and genuine human ownership and the maintainer's ruling puts the rule first, with any ownership exemption as an explicit marker later. The row names the login and states the asymmetric remedy instead. H8's landing re-label and H19's unlock scan are the two rollback paths that never named the assignee drop (only dead-claim reclamation did), so both remedy sentences now say the paired write includes it. The half-delivered H8 branch deliberately does not: there the label and the claim are correct. `pm:awaiting-maintainer` is implemented as a first-class state per the ruling of 2026-08-23 — vocabulary entry in ensure-pm-labels.sh, H25's exclusivity against every other state label, and membership in H11's parked inventory, H13's state vocabulary and H22's residue set, so no reader is left with a four-row hole. Its SKILL.md state-model row and its application to the specimen card are deliberately deferred, not dropped. Self-test 879 -> 931 cases, both directions pinned for every new predicate and every vocabulary membership. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMTpSRF5CjMmQBFfPtPCwJ
…adopts it verbatim (#11217) The patrol was installed in objectstack alone, which left 37 of the fleet's 59 open `pm:blocked` cards outside any machine sweep — a hand-run of H19's predicate over objectui found 7 blocks whose blocker had already closed, 58% of that repo's machine-readable blocks, one of them stale for a week. The same predicate catches objectstack's every hour. Two hardcodings made "copy the pair verbatim" unsafe, and both are removed: * the swept repo now resolves PM_SWEEP_REPO -> GITHUB_REPOSITORY -> default, so a copy reads the board it lives in instead of this one. A malformed value is refused at the CLI (exit 2) rather than silently replaced by the default: substituting a different board is the failure being closed. * the anchor issue comes from the repository variable HALF_STATE_ANCHOR_ISSUE, with this repo's 9857 kept only as a fallback GUARDED by the repository name. An install with no anchor configured fails loudly after the sweep instead of rewriting whatever #9857 happens to be in that repo. The objectstack leg is unchanged by construction: its runner sets GITHUB_REPOSITORY (and now PM_SWEEP_REPO) to the same string the hardcoded default carried, and the guarded fallback resolves to the same 9857 — pinned in the self-test in exactly the shape the runner provides. Per-repo installs with each repo's own GITHUB_TOKEN, per the grading ruling; no cross-repo credential. Self-test 931 -> 951 cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMTpSRF5CjMmQBFfPtPCwJ
…e transitive wait (#11219) The unlock predicate is "the `Blocked-by:` target CLOSED", and `pm:on-hold` / `needs-user-decision` are by definition states a card sits in WHILE OPEN. A block naming such a target is structurally indefinite and nothing reported it: the waiting card is perfectly well-formed — line present (H4), target resolves and is open (H19), label correct — and H9 audits the HELD card, not the waiting one. Six measured instances, every one found by a human reading, including one repo whose entire blocked inventory (2 of 2) waits on its single unanswered decision card. A second leg on the same data flags a target that is itself `pm:blocked`: the measured chain was real one hop up and false two hops up, and a single-level predicate cannot see that. It names the hop rather than chasing it. Quota-neutral: H19 already resolves every distinct target, from a listing in hand or with one GET, and a resolved target's LABELS rode in on a payload already paid for — the resolution rows simply stop discarding them. Report-only, and explicitly not a claim that the block is wrong; sometimes waiting on a deferred card is right. Deliberately not reported: a target labelled `pm:queue` while titled `[Decision]` — a mislabelling, not a fact in the labels. Self-test 951 -> 977 cases, both directions pinned for each leg, including the disjointness from H19 (a closed target is H19's row, an unresolved one is H19's sentence) and the case where both fire on one card. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMTpSRF5CjMmQBFfPtPCwJ
…ured merge rate (#11118) H8's docblock justified a two-page window with "~18 merges/day", a figure the repo had outrun more than sevenfold. The sentence claimed a reach "well past the longest measured unexecuted-verdict latency" (~11 days at that rate); the window it described had become 1.78 days. Nothing could notice, because the arithmetic was prose. Re-measured 2026-08-23T08:42:15Z, windows pinned as full ISO instants per the card's approxidate warning: rate 300 commits, 2026-08-21T04:00:19Z..2026-08-23T08:22:47Z = ~137.5/day (300/300 carrying the squash marker; agrees with the card's ~132/day) H8 2 pages = 1.78d (oldest merge 2026-08-21T14:00:28Z) 4 pages = 2.96d (oldest merge 2026-08-20T09:41:02Z) -> widened H22 2 pages = 0.65d of update-recency (~15.6 HOURS, ~2.6 sweeps) 4 pages = 1.70d -> widened H23 3 pages = 2.18d, ~8.7 sweeps -> unchanged H8 is widened rather than restated because its damage model is asymmetric: the residue it reports is the paired write nobody noticed, which correlates with age, so the population most likely to age out is the population the row is for. H22's surprise is the divisor — ordered by `updated`, its rows are consumed by issue ACTIVITY, not closures, and the rate is bursty in exactly the direction that ejects fresh residue while residue is being produced fastest. Four pages restores what its anti-drowning argument was choosing (a couple of days) and does not reopen the deep tail it refuses (500+ carriers spanning months). H23 kept its cap: it is the one window that DERIVED its number from a measured rate, and the only one that survived a re-measure. The derivation is now executable — `windowCoverageDays`, `sweepOverlap`, `MEASURED_MERGES_PER_DAY`, `PATROL_CADENCE_HOURS` — so a cap and the sentence justifying it cannot drift apart again. Cost: four extra requests per sweep, four sweeps a day, against a 15,000/h quota. Self-test 977 -> 997 cases, including the degenerate inputs that must answer "cannot divide" rather than Infinity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMTpSRF5CjMmQBFfPtPCwJ
This was referenced Aug 23, 2026
os-zhuang
marked this pull request as ready for review
August 23, 2026 09:14
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 23, 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 freeto 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#11196
Fixes#11217
Fixes#11219
Fixes#11118
One family, one defect shape: the patrol's input set and predicate vocabulary were narrower than the states the board actually produces. Four graded cards, one commit each, all on
scripts/pm/check-half-states.mjsand its workflow — the hot-file family dispatched as a fold rather than serialized.6bc3e47pm:queue+ assignee), thepm:awaiting-maintainerstate, the paired-write remedy texts5d86dc6160462302e2838Report-only throughout: no predicate here is a gate, no row writes a label, and a completed sweep still exits 0 with 0 or 40 findings.
#11196 — the two-views contradiction, and the state that had nowhere to live
H24 fires on an open card carrying
pm:queuewith a non-empty assignee. The queue view reads it as dispatchable, the claim rule reads it as taken; both readers are right about the field they read, so the card is available to everyone and forbidden to everyone at once. Every adjacent row declined the shape — H1 wants no assignee, H2 wants a missing claim comment (the measured carriers had complete ones), H3 wants two labels while here the second half of the contradiction is a field — which is how 17 cards across three repos sat in it unreported. Those three adjacencies are pinned as self-test cases, not asserted.The predicate is a pure intersection: no threshold, no timestamp, no identity test. Per the ruling of 2026-08-23 the rule lands first and any true-ownership exemption is an explicit marker later — so the row fires on a human assignment too, names the login so residue and ownership are separable by a reader, and states the asymmetric remedy (an agent may clear agent residue; ⛔ never a human's).
Paired writes (fix 2). The landing re-label (H8) and the unlock scan (H19) are the two rollback paths that never named the assignee drop; both remedy sentences now carry 「同笔摘 assignee」. H8's half-delivered branch deliberately does not — there the label and the claim are correct, and prescribing a drop would be the destructive write #10468 closed.
pm:awaiting-maintainer(fix 5) is implemented as a first-class state, spelling finalized as ruled:scripts/pm/ensure-pm-labels.sh(five-repo loop, 88-char description, consumers named in its comment block);pm:queue,pm:dispatched,pm:blocked,pm:on-hold,needs-user-decision— each with the specific lie it tells rather than a generic "these disagree";⛔ Deferred, declared not dropped: the SKILL.md state-model table row for the new label (protocol face, fable-mandatory, separate landing), and applying the label to specimen card #7276 — the seat's write at ACCEPT, never this PR's. The legacy cleanup ruled on the same day was already executed and is untouched here.
#11217 — the pair a sibling repo can adopt verbatim
Two hardcodings made "copy both files" unsafe, and both are gone:
PM_SWEEP_REPO→GITHUB_REPOSITORY→ default. A copy of the script in objectui, run with no override, used to sweep objectstack and write those findings into objectui's anchor: a fully green report about the wrong board. A malformed value is refused at the CLI (exit 2) instead of silently falling back — substituting a different board is the disease itself.HALF_STATE_ANCHOR_ISSUE, with9857kept only as a fallback guarded by the repository name. An install with no anchor configured fails loudly after the sweep (so the run summary still carries the findings) rather than rewriting whatever#9857happens to be in that repo.Per-repo installs, each with its own
GITHUB_TOKEN, per the grading ruling — no matrix, no cross-repo credential. The accepted consequence (cross-repoBlocked-by:targets stay UNJUDGED per install) is stated in both headers; #11218's loudness half is not addressed here and stays open.Objectstack leg unchanged — measured, not asserted. The old value was the literal
'objectstack-ai/objectstack'; the runner setsGITHUB_REPOSITORY(and nowPM_SWEEP_REPO) to that same string, so every request path is byte-identical. Observed offline by stubbingglobalThis.fetchand running the real CLI:Plus 20 self-test cases over
resolveSweepRepo, including the objectstack runner's exact env shape and the empty-string case an unset Actions variable expands to.>-folded scalar a more-indented continuation line keeps its newline literally, which would hand the expression parser a multi-line string. Measured on this very value and fixed by aligning the continuations; the comment above it says so.#11219 — the block nothing can ever release
The unlock predicate is "the
Blocked-by:target CLOSED", andpm:on-hold/needs-user-decisionare by definition states a card sits in while open. Such a block is structurally indefinite, and the waiting card passes every existing check: line present (H4), target resolves and is open (H19), label correct. H9 — the nearest neighbour — audits the held card, not the waiting one. All six measured instances were found by a human reading; one repo's entire blocked inventory (2 of 2) waits on its single unanswered decision card.A second leg on the same data flags a target that is itself
pm:blocked: the measured chain was real one hop up and false two hops up, and a single-level predicate cannot see that. It names the hop rather than chasing it (a request per hop, and chains can cycle).Quota-neutral, as the card asked: H19 already resolves every distinct target — from a listing in hand or with one GET — and a resolved target's labels rode in on a payload already paid for. The resolution rows simply stop discarding them. Not reported, deliberately: a target labelled
pm:queuewhile titled[Decision](one of the six) — a mislabelling, not a fact in the labels, and a title heuristic would make the sweeper guess at intent.#11118 — window arithmetic re-derived, with the windows pinned as ISO instants
H8's docblock justified two pages with
~18 merges/day. Re-measured 2026-08-23T08:42:15Z against the live endpoints, windows pinned as full ISO instants per the card's approxidate warning:2026-08-21T04:00:19Z … 2026-08-23T08:22:47Z2026-08-21T14:00:28Z2026-08-20T09:41:02Z2026-08-22T17:05:44Z … 08-23T08:39:14Z2026-08-21T15:44:49Z2026-08-21T04:00:19Z … 08-23T08:22:47ZH8 is widened rather than restated because its damage model is asymmetric: the residue it reports is the paired write nobody noticed, which correlates with age, so the population most likely to age out is the population the row exists for. H22's surprise is the divisor — ordered by
updated, its rows are consumed by issue activity, not closures, and the rate is bursty in exactly the direction that ejects fresh residue while residue is produced fastest. Four pages restores what its own anti-drowning argument was choosing (a couple of days) without reopening the deep tail it refuses (500+ carriers spanning months). H23 kept its cap: it is the one window that derived its number from a measured rate, and the only one that survived a re-measure.The derivation is now executable —
windowCoverageDays,sweepOverlap,MEASURED_MERGES_PER_DAY,PATROL_CADENCE_HOURS— so a cap and the sentence justifying it cannot drift apart again. Cost: four extra requests per sweep, four sweeps a day, against a 15,000/h core quota.Verification
Self-test: 879 → 997 cases pass, both directions pinned for every new predicate, every vocabulary membership, and each window constant (including the degenerate inputs that must answer "cannot divide" rather than
Infinity). One pre-existing pin — H22's "the measured residue set is the five from the census" — failed on the new label and was updated deliberately, with the reason for the one non-census member written into it.Live sweep not run, and it cannot be here: this container's
GITHUB_TOKENis the proxy's 14-character placeholder and the anonymous quota is spent, so--probereturns the classifiedPREREQUISITE NOT MET(exit 3) the file's own header documents. Self-test plus the offline fetch trace above is the verification surface; the workflow'spull_requesttrigger runs the real sweep on this PR against a runner where the transport prerequisite is met.Gates derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackon the actual change set at02e28386(three-dot, merge-base050d8d868) — 20 families named, each run with its exit code captured before any pipe:pnpm lint(repo-wideeslint . --no-inline-config, not a narrowed subset): exit 0 in 78 s, no output.check:partof-closing-keywordneeds a PR body to judge and was run against this one.skip-changeset: nothing here is published —scripts/pm/**tooling and one workflow. No user-visible surface changes, so no changeset is owed.Generated by Claude Code