Nothing in the fleet reads mergeStateStatus — under a strict ruleset, two independent writers would call a BEHIND pull request mergeable right now #269
Replies: 6 comments
Re-verified at
|
| what the body says | at 0a29cd6 |
still true? |
|---|---|---|
the handoff GraphQL selects headRefOid mergeable |
duty-builder.sh L833 |
yes — mergeStateStatus is still not requested |
converged.jq hands off on mergeable == "MERGEABLE" |
converged.jq L27-L29 |
yes — MERGEABLE ⇒ "true", UNKNOWN ⇒ defer-unknown, everything else "false" |
the rebase duty fires on CONFLICTING and nothing else |
duty-builder.sh L883-L884 |
yes — --json number,mergeable,isDraft, selecting mergeable == "CONFLICTING" |
And the ceremony half is still current, which matters because the body pins it to a version. crew consumes heavy-duty/ceremony/.github/workflows/labels.yml@0.4.1 — the pin has not moved, so the reconciler this thread describes is the one running against this repository today.
2. The A-condition still has not fired, and that is why nothing is broken
Re-checked directly rather than inferred from the absence of complaints:
$ gh api repos/heavy-duty/crew/rulesets
[]
$ gh api repos/heavy-duty/crew/branches/main/protection
{"message":"Not Found", "status":"404"}
No ruleset, no branch protection. mergeStateStatus reports CLEAN for a behind branch when nothing requires up-to-date branches, so BEHIND cannot appear on this repository yet and state:needs-human is still honest every time it is written. The defect is latent, not live — the same standing the thread opened with.
#212 is where the trigger comes from, and re-read from /repos/heavy-duty/crew/issues/212/timeline rather than from its thread: blocked since 2026-07-31T20:29:00Z and never lifted; needs-ruling removed 2026-08-01T20:36:48Z when option A was recorded. So the ruling that creates the trigger is decided and the trigger itself is still gated. Nothing in the two days of merges since touched a mergeability read.
3. One thing worth knowing before Q1 is answered, and it is a warning about evidence rather than a new argument
#290 is ready and unclaimed, and its phase 2 mines "the board vs the truth: label-state churn, windows where labels misreported the ball" across 2026-08-01/02. That is precisely this thread's failure mode described in general terms, so it is worth saying plainly what that report will and will not show:
It will find no instance of this defect, and the absence is not evidence against it. No ruleset existed during the window the report covers, so BEHIND never occurred and neither writer of state:needs-human ever had the chance to lie for this reason. A reader who takes "the report found no case" as a reason to drop this thread would be reading a condition that was never tested as a test that passed. Recorded here so that inference is available at the point someone might make it.
What has not changed
Q1, Q2 and Q3 are untouched and unanswered. Triage's read is still tell rather than update, for the reason the body gives — the operator's two clicks are cheaper than a discarded panel and a fresh round, and #212's ruleset requires no re-review — and it is still a judgement about how the operator wants to merge, which is @danmt's. Q2 still goes to heavy-duty/ceremony once Q1 lands, and not before: asking ceremony to design a taxonomy change against a crew decision nobody has made is the wrong order, and it would land on every governed repo.
Nothing is waiting on this thread. Re-derived from the parsed dependency declarations of all 52 open issues rather than from the cross-reference list: no open issue's blocker set names it, and #212 — the issue it came from — is held by its own two-number declaration independently of it.
Triage, 2026-08-02.
The prediction this thread pre-registered is discharged — the report landed, and it found no instance, for the reason stated in advanceMy comment yesterday put a claim on the record before the evidence existed, so it is worth closing out now that the evidence does. It said #290 was " The prediction, verbatim:
Confirmed. The report's "The board versus the truth" section measures 377 Re-verified at
|
The issue that creates this defect's trigger is closed — the trigger is now a release window, and that gives Q1 a deadline it did not haveMy comment at Nothing was delivered by that close. The ruleset does not exist, and I checked rather than inferred it, at this write: So The defect itself, re-checked at
|
Q1's deadline moved from "seven windows out" to "three" — and the defect is re-verified latent at
|
| if Q1 answers… | the work lands in | distance |
|---|---|---|
| tell | one sentence in a handoff comment, minted beside the ruleset at #332 (0.1.9) |
seven windows |
| update | engine dispatch work at #339 (0.1.5) / #329 (0.1.6) |
three windows |
And #339 is not merely the nearest plausible home — it is the home. Its scope is that "every marker, label write, review request, announce, and handoff migrates from prompts-and-agents into deterministic engine steps", with the done-declaration and the whole handoff named in its to-mint list. A convergence gate that must read a second mergeability field is a handoff-step decision, and #339's init is where the handoff steps get written.
So the deadline is #339's release-init, not #332's. Answer Q1 tell and nothing is owed until 0.1.9; leave it unanswered and 0.1.5's init has to either guess it or design around it. That is three windows out and the first of them opens when the cut in flight lands.
Q2 is untouched: whether BEHIND is owed a blocker in ceremony's taxonomy is a heavy-duty/ceremony discussion, opened there once Q1 lands and not before — asking ceremony to design against a crew decision nobody has made is the wrong order, and it would land on every governed repo.
3. The census in my last comment is void
It read "over all 22 open blocked bodies: every one names #346." Board at this write: 31 open — 19 epic, 6 post-merge, 3 blocked, 2 claimed, 1 ready (19+6+3+2+1 = 31), zero needs-triage, attention, stale and offsite, two open PRs (#401 draft at blocker:ci-red, #410 at state:needs-human). Blocker parse over every open blocked body: #406 → {#402, #403}, #408 → {#406}, #411 → {#408}; union {#402, #403, #406, #408}, none of them this thread. One needs-ruling stands on the board — on #400 since 2026-08-07T21:32:17Z, the release cut's drill-sequencing ask — and it names nothing here.
Outcome unchanged: ask, waiting on @danmt
Q1 — when the engine finds a converged PR that is BEHIND, does it update the branch or tell the operator? — still decides the other two, and triage's read is still tell: the operator's two clicks are cheaper than a discarded panel and a fresh round, and the ruled ruleset requires no re-review. It remains a judgement about how the operator wants to merge, which is why it is asked rather than picked.
Nothing is waiting on this thread. No needs-ruling label is owed — nothing on the board is held by it — so the ruling ladder has no clock here and no rung fires. The 7-day nudge applies: absent real activity triage returns on 2026-08-14, and in any case at #339's init, whichever is first.
Triage, 2026-08-07.
The 7-day return, two days late — and the reconciler half turns out not to be a pin bump awayTriage 2026-08-16 1. The defect is intact at
|
site at 394bdad |
what it reads |
|---|---|
duty-builder.sh#L2332 |
the handoff GraphQL still selects headRefOid mergeable |
converged.jq#L71-L72 |
hands off on mergeable == "MERGEABLE", defers on UNKNOWN |
duty-builder.sh#L2391-L2392 |
the rebase duty fires on mergeable == "CONFLICTING" and nothing else |
2. New: the ceremony half cannot be fixed by moving the pin
This thread has only ever measured the reconciler at the pin crew consumes. Both were measured this time.
crew is pinned to ceremony 0.6.2, read from the four workflows at 394bdad (labels.yml, labels-sweep.yml, release.yml, release-guards.yml). At that pin, labels-reconcile.sh has zero mergeStateStatus: it fetches --json mergeable,statusCheckRollup (L984) and derives blocker:conflict from CONFLICTING alone (L493) — so a BEHIND pull request still carries zero blockers.
And ceremony has published through 0.7.3 since, where the count is also zero. So the four tags between the pin and the head change nothing here: this is not a bump crew is behind on, it is a datum nobody upstream reads either. Q2 — whether BEHIND is owed a blocker in ceremony's taxonomy — is exactly where this thread put it, and still opens there only once Q1 lands.
3. Q1's deadline moved again — from three windows out to two
The 0.1.2 cut this thread was waiting on landed (tag published 2026-08-10T20:47:40Z). The standing window is now 0.1.3 (#327), so 0.1.5 (#339) — the window whose init has to either answer Q1 or design around it — is two windows out, not three. #339 is open, epic + release, carrying no queue label; its window has not opened.
4. The board this ran against
26 open — 19 epic, 5 post-merge (#181, #217, #240, #359, #400), 2 blocked (#454, #457) — and 0 each of ready, claimed, needs-triage, needs-ruling, attention, stale and offsite. 0 open pull requests. The shipped blockers.jq run over both blocked bodies against a live state map returns #454 → {327, 452} and #457 → {327, 454}; the promotion set is empty, and neither names this thread. Nothing on the board is held by this ask — and the empty claimable queue is not this ask's doing either: 0.1.3 is at release-init step 1, whose issues are minted as one wave by your own instruction.
5. Why this is late
Four of these asks were due a 7-day return and none was written: this one and #229 on 2026-08-14, #351 and the #291 placement ask on #338 on 2026-08-11. All four are served in this tick; #317's falls tomorrow and is not yet owed. What triage did write in that window was map and figure corrections on #327 and #338. Recorded rather than quietly re-dated: a nudge discipline that only reports its hits is not a discipline.
Outcome unchanged: ask, waiting on @danmt
Q1 — when the engine finds a converged PR that is BEHIND, does it update the branch or tell the operator? — still decides the other two, and triage's read is still tell. No needs-ruling is owed and none is set; nothing on the board is held, so no rung of the ladder fires.
You are present and this is not sitting behind an absent human — your last write here is the #455 reply of 2026-08-14T21:03:42Z, and you were closing work in heavy-duty/incubator as recently as 2026-08-16T11:32:15Z. So: one word answers it, and one word parks it. If you park it, triage stops nudging until you unpark it. Absent either, triage returns on 2026-08-23, and in any case at 0.1.5's init, whichever is first.
Triage, 2026-08-16.
The return, 9 days late — the pin moved two minor versions and the count is still zero, so §2's conclusion is stronger than when it was writtenTriage 2026-09-01. This is the return my comment of 2026-08-16 promised for 2026-08-23; it is nine days overdue, dealt with at the foot rather than passed over. Re-run at 1. The defect is intact at
|
site at 70bd7d4 |
was at 394bdad |
what it reads |
|---|---|---|
duty-builder.sh#L2962 |
:2332 |
the handoff GraphQL still selects headRefOid mergeable |
converged.jq#L71-L72 |
:71-72 |
hands off on mergeable == "MERGEABLE", defers on UNKNOWN |
duty-builder.sh#L3048 |
:2391-2392 |
the rebase duty fires on mergeable == "CONFLICTING" and nothing else |
Two of three line references would now point at unrelated code. Worth stating because this thread's argument rests on those three reads and a permalink that resolves to the wrong line is worse than one that 404s.
2. §2's load-bearing premise has flipped, and the conclusion survives it — measured at the pin crew actually consumes today
Last time this section said: "crew is pinned to ceremony 0.6.2 … and ceremony has published through 0.7.3 since, where the count is also zero."
Crew is no longer at 0.6.2. #603 landed 2026-09-01T07:57:49Z, hours before this write, moving all ten references to 0.7.7 — read from the workflows at 70bd7d4, not from the issue. And ceremony's head tag is now 0.7.8, with #573 on the board to take crew there.
So the pin premise this thread's §2 rested on is stale by five tags. Re-measured at both ends:
actions/labels-reconcile/labels-reconcile.sh |
bytes | mergeStateStatus |
mergeable |
|---|---|---|---|
ceremony 0.7.7 — the pin crew consumes today |
110,458 | 0 | 9 |
ceremony 0.7.8 — the head, and #573's target |
113,981 | 0 | 9 |
A code search across the whole of heavy-duty/ceremony returns 0 hits for mergeStateStatus.
The conclusion is therefore unchanged and better supported than when it was written. It is no longer "four tags between the pin and the head change nothing" — it is that crew has since crossed five of those tags, the reconciler grew 3.5 KB across the last one alone, and the datum is still read by nobody upstream. Q2 opens in ceremony, exactly where this thread put it, and still only once Q1 lands.
One inference to head off, because it is the natural one and it is wrong: the pending 0.7.8 bump does not close this. #573 is a scaffold migration; the reconciler at 0.7.8 has the same zero. Nobody should read that issue landing as this thread being served.
3. Q1's deadline has not moved
The standing window is still 0.1.3 (#327), so 0.1.5 (#339) — the window whose init has to either answer Q1 or design around it — is still two windows out. #339 is open, epic + release, no queue label, window not opened. Unchanged from last time, and that is the honest reading rather than a re-derived urgency.
4. The board this ran against
51 open (was 26) — 18 epic, 20 blocked, 10 ready, 2 claimed, 1 post-merge, 7 operator — and 0 each of needs-ruling, attention, needs-triage and stale. Two open pull requests, #630 and #631, both opened this morning, both fork heads. 0.1.3's wave landed in the interval, which is why the claimable queue went from empty to ten.
The shipped blockers.jq run over all 20 open blocked bodies: nothing on the board is held by this ask, and no open issue is gated on it. No needs-ruling is owed and none is set, so no rung of the ladder fires.
(Neither open PR is BEHIND-shaped evidence for this thread yet — both were opened minutes ago against a head that has not moved under them. This thread's scenario needs a converged PR that has fallen behind, and there is not one on the board at this write. Said explicitly because "two PRs are open" is the kind of fact a reader turns into evidence it is not.)
5. Why this is nine days late
Fourth consecutive recorded miss across this family: this thread, #229 and #338 promised 2026-08-23, #317 promised 2026-08-24, and all four were found overdue and left unhonoured on 08-27, 08-29 and 08-31 before today. Recorded rather than re-dated: a nudge discipline that only reports its hits is not a discipline.
The premise most worth re-measuring is the one about the human, and it does not say what the nudging assumes. @danmt has commented on eight crew issues since these returns fell due — most recently 2026-08-31T21:40:23Z — and on zero of the fourteen open crew discussions; the last @danmt reply on any crew discussion was 2026-08-19T19:17Z. The full measurement is in #317's return of today, stated once there rather than four times. It changes nothing about the outcome, licenses no pick, and is reported because a fifth identically-shaped nudge is the move that has now failed four times.
Outcome unchanged: ask, waiting on @danmt
Q1 — when the engine finds a converged PR that is BEHIND, does it update the branch or tell the operator? — still decides the other two, and triage's read is still tell.
One word answers it and one word parks it. If you park it, triage stops nudging until you unpark it. Absent either, triage returns on 2026-09-08, and in any case at 0.1.5's init, whichever is first.
Triage, 2026-09-01.
Uh oh!
There was an error while loading. Please reload this page.
Opened by triage 2026-08-01, on picking option A at the past-24h rung of #212's ruling (the decision). #212 says triage mints this as its own work order when the ruling lands. Triage is not minting it, and this thread is why: the fix's shape is undecided, it spans two repositories, and an issue written today would hand a builder three guesses.
The defect, stated once
Nothing in this fleet reads
mergeStateStatus. Both readers of a PR's mergeability read onlymergeable, which is a conflict verdict —MERGEABLE/CONFLICTING/UNKNOWN— and never reports behind.crew's engine, at
d022c71:duty-builder.sh#L600-L607headRefOid mergeable—mergeStateStatusis not requestedconverged.jq#L26-L30mergeable == "MERGEABLE"duty-builder.sh#L651-L653mergeable == "CONFLICTING"and nothing elseceremony's reconciler, at the pin crew consumes (
labels-reconcile.sh@0.4.1): it fetchesgh pr view --json mergeable,statusCheckRollup, derivesblocker:conflictfromCONFLICTINGalone, and never readsmergeStateStatus. So aBEHINDPR carries zero blockers, and the reconciler's own rule — no blockers plus head-current approvals ⇒state:needs-human— makes it assert the label too.Under a
strict: trueruleset a PR twelve commits behindmainis stillMERGEABLE. So the engine hands it off, the engine and the reconciler both setstate:needs-human— a label whose contract is "this PR could be merged right now" — and GitHub refuses the merge. Both writers of that label would be lying, independently, for the same reason.This is A-conditional. With no ruleset a behind-but-mergeable PR genuinely can be merged, and
state:needs-humanis honest today. Nothing is wrong onmainright now, and nothing on the board waits on this thread.Why triage is asking instead of minting
#212's rung-12 re-read called the fix shape "known" — read
mergeStateStatus(BEHIND) alongsidemergeable, in both the convergence gate and the rebase trigger. Closing the ruling out, that turned out to be the easy third of it. Two things it does not decide:1. Making the branch current resets crew's panel, and GitHub's ruleset does not ask it to. Updating a behind branch produces a new head.
converged.jqcounts approvals only at the current head — deliberately, since an approval is of a specific tree — so every panel verdict is discarded and a fresh round is requested. But #212's ruleset adds norequired_pull_request_reviews; its only requirement isrelease-guards / guards. So GitHub will let the operator press Update branch, wait forguards, and merge — with no re-review at all. If the engine auto-updates, the fleet spends a full round on a PR the operator is merging anyway. That is not a livelock and it does not block the merge; it is wasted fleet work and a board that readsstate:addressingon a PR nobody is addressing.2. The honest label may not exist yet, and it is not crew's to invent. Behind, under strict is precisely what is in the way — the blocker axis, not the state axis — and the remedy is exactly what
blocker:conflictalready names: the builder owes a rebase. Butblocker:*is machine-owned by ceremony's reconciler, and the taxonomy is mirrored byte-identical into every governed repo. crew cannot add a blocker, and crew cannot change howblocker:conflictis computed. Either ceremony's reconciler learnsmergeStateStatus, or crew's engine papers over a label ceremony keeps re-asserting.The questions whose answers let triage write the issue
@danmt — Q1 is the one that decides the other two, and it is cheap to answer.
Q1 — when the engine finds a converged PR that is
BEHIND, does it update the branch, or does it tell the operator?BEHINDlikeCONFLICTINGand fires the rebase duty. The PR is merge-ready when the operator arrives. Cost: a discarded panel and a fresh round every timemainmoves under an open PR — on this fleet, often.state:needs-humanstill says mergeable right now about a PR that is not, until Q2 fixes the label.Triage's read is tell, on the grounds that the operator's two clicks are cheaper than a fleet round and that GitHub asks for no re-review — but this is a judgment about how the operator actually wants to merge, and that is yours.
Q2 — is
BEHINDowed a blocker in ceremony's taxonomy, or is it crew's engine's problem alone? If tell wins, something has to stop both writers asserting mergeable right now. The cleanest shape is ceremony's reconciler readingmergeStateStatusand raisingblocker:conflict(or a sibling) onBEHIND— which makes the existing "no blockers ⇒ needs-human" rule do the work in every governed repo at once, including the three that are not crew. That is a heavy-duty/ceremony discussion, and triage will open it there rather than here once Q1 is answered; opening it before would be asking ceremony to design against a crew decision nobody has made.Q3 — does any of this land before the ruleset, or with it? #212's acceptance criteria already put ruleset creation post-merge and operator-executed. If the answer to Q1 is tell and Q2 goes to ceremony, then the crew-side work is one sentence in a handoff comment, and the ruleset can precede it — the exposure in the gap is a label that overstates for as long as it takes an operator to click. If the answer is update, the engine work is a genuine prerequisite and the ruleset waits for it.
What is true regardless, and worth keeping
mergeStateStatusis available at crew's tooling with no preview header — verified today againstgh 2.46.0, both as a GraphQL field onpullRequestand asgh pr list --json mergeStateStatus. And itsBEHINDvalue only appears when a ruleset requires up-to-date branches; without one it reportsCLEANfor a behind branch. So whatever is built cannot be tested against this repository until the ruleset exists, and its unit tests will have to be built on synthetic payloads —shared/test/run.sh'smk_prhelper in theconverged.jqblock is where they would live.Nothing is waiting on this thread. It gates no issue and blocks no builder; #212 is
blockedon #211 and #162 independently of it.All reactions