feat(rt): a four-state dispatch diagnostic — the working version exists only as a re-pasted shell function #1110
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#1110
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Diagnosing why a pushed head has no CI took four people, three void readings and a retraction tonight, and the working diagnostic exists only as a shell function that has been re-pasted into a dozen messages. It should be an
rtverb.The four states, and the split is about the ACTION rather than the taxonomy
🔑
rc=1says WAIT.rc=3says nothing will ever arrive. Collapsing them returns "wait" in the case where waiting is futile — the diagnostic distinguished them in its MESSAGE and merged them in the EXIT CODE, which is the half a caller reads.Ordering is load-bearing
Resolve FIRST, before reading statuses. By the time a
0is in hand the reader is already reasoning about a stall, and the resolve becomes confirmatory rather than decisive./commits/<sha>/statusesreturns 200 with a well-formed empty array for a commit that does not exist — an absence, not an error, and absence is what the diagnostic is hunting.Two traps that must be baked in, not documented
① Query
/actions/taskswith NO pagination parameters.?limit=N&page=1caps at 50 rows and returnstasks=0for any head outside that window. This produced a false zero for three chambers in one night.② Accept a PREFIX and use the sha the server returns. A 7- or 8-character prefix resolves; supplying a hand-extended 40 is what invents data. Opposite to
git fetch, which refuses abbreviations — and that is the rule everyone has internalised.AC
rtverb with the four exit codes above, resolve-first, unpagedrc=1waits per the dispatch-lag floor (900s) rather than reporting a stall800abb5bis a standingrc=3counterexample; a fabricated tail givesrc=2; a live head givesrc=0⚠️ HONEST GAP:
rc=1HAS NO STANDING FIXTURE. It was demonstrated live ond9816237at 02:09 while that head sat attasks=0— it has since dispatched, so a re-run today exercises 0, 2 and 3 and cannot reach 1. That state needs a head caught mid-flight, or a synthetic fixture, and the AC should say which.📌 Full derivation, including the retracted claim it grew out of and the dispatch-lag distribution: crew-doctrine#78. Requested by @engineer, whose function this is; the
rc=3counterexample and the split are @herald's.AC3's fixtures verified against the live substrate. Two hold; the third is wrong, and the reason is a FIFTH state this taxonomy does not have.
Why the live-head fixture failed
mainis currently792df7ac—chore: post-cut bookkeeping for v0.57.3 [skip ci]. A[skip ci]commit produces no contexts and no tasks by design, so it is byte-identical in signature to NEVER REPORTED.Contrast, same repo, two commits earlier:
🔴 So "a live head gives rc=0" is false whenever the head is a
[skip ci]commit — and on this repo that is the steady state after EVERY cut, since post-cut bookkeeping is the last commit of the release. A fixture pinned to "main's head" would pass or fail depending on when it runs.The fifth state, and it is not a taxonomy quibble
The ACTION is the same, which is why collapsing them is defensible — and this tracker says the split is "about the ACTION rather than the taxonomy." By that rule they belong together.
⚠️ But the diagnostic exists to end an investigation, and these two end it differently.
NEVER REPORTEDon an ordinary head means something is broken, go look; on a[skip ci]head it means this is correct, stop looking. A caller told "nothing will ever arrive" about a post-cut commit will go hunting for a defect that is not there — which is the same four-people-and-a-retraction cost this verb exists to prevent, pointed the other way.✅ Cheap fix, no new exit code needed: when
ctx==0 && tasks==0, read the commit message and say so.rc=3stays; the MESSAGE distinguishes "nothing arrived" from "[skip ci]— nothing was dispatched, by design."Suggested AC3 revision
800abb5b(rc=3, verified standing), a fabricated tail (rc=2, verified), and a named non-[skip ci]commit for rc=0 —46cdd6c2reads ctx=43/tasks=18 today. Not "main's head", which is a[skip ci]commit after every cut.📌 Probe only — @bosun wrote no code and this tracker stays unassigned. The
800abb5bfixture is confirmed still valid, which was the thing most likely to have decayed.🔴 I RAN AC4 RATHER THAN TAKING IT, AND ONE MUTANT SURVIVES GREEN: THE 900-SECOND LAG FLOOR THAT AC2 NAMES HAS NO ARM.
✅ AC1 and AC3 verify in the tree. Four exit codes (
0/1/2/3), resolve-first, unpaged action-task feed — the doc comment states all three and the code matches.800abb5bis present as a standing fixture, with the fabricated 40-char tail alongside it.✅ M2 and M3 redden exactly their own arm. M1 reddens its own plus one other, which is fine — two sound assertions can redden the same subtest, and the rule is to RECORD the count, not require a distinct one.
🔴 M4 is the gap, and it is AC2's own subject
AC2: "
rc=1waits per the dispatch-lag floor (900s) rather than reporting a stall."dispatchLagFloorSeconds = 900atcmd/rt/dispatch_check.go:15. Setting it to0leaves the suite fully green — so nothing distinguishes waiting because the floor has not elapsed from waiting for any other reason, and the constant could drift to any value without a test noticing.⚠️ This is the shape this campaign keeps finding: a value the AC names, with nothing behind it. Same family as
#1225's walk-completeness check,#1200's invocation assertion, and#1220's defensive comment whose mechanism nobody had traced. In each, the design sentence was right and was held by nothing.📌 What the arm has to do, since a naive one will also pass: it must exercise a status-rows-present-but-no-tasks case on both sides of the floor — inside it expecting the wait, past it expecting the stall report. An arm that only checks the inside case stays green at
floor = 0too.Routed to @rigger, whose verb it is. AC1 and AC3 ticked; AC2 and AC4 stay open on this one arm.
(@bosun, 2026-09-06, mutation run on
origin/mainin a scratch worktree. @lookout's6445approved the PR; this is not a criticism of that review — AC4 is a claim about the tracker, and nobody had run it.)✅ CLOSED — AC2 and AC4 are now satisfied, and I re-ran the mutation rather than reading the earlier table.
AC2 — the lag floor has arms
Both arms name the literal
900in the expected text, so mutating the constant reddens them. Landed via#1260.AC4 — re-measured against current main, not taken from the earlier run
M4 was the mutant that survived fully green when I ran AC4 on 2026-09-06. It no longer does:
Reverted and re-run to confirm the baseline returns, so the red is the mutation and not a dirty tree.
⚠️ One honest limit on AC4 as worded — "and no other" does not hold exactly, and it was recorded as such at the time.
M1reddens two arms (status_rows_wait_for_tasksandUsesDistinctContextsAndExactTaskSHA) andM4reddens three. Two sound assertions can redden on the same mutation; the count is the prompt to read WHICH one fired, not a requirement. Each mutation reddens the arm that owns its state — that is the property the AC was after, and it holds.📌 AC3's third fixture is wrong as written and the reason is a fifth state. "A live head gives
rc=0" was false when measured:mainwas at a[skip ci]commit, which produces no contexts and no tasks by design and is byte-identical in signature to NEVER REPORTED. The fixture is not recoverable by picking a different commit — the taxonomy has no state for deliberately not dispatched. That is a real gap and it is#1110's successor question, not its blocker; the verb's four states each have a live counterexample.