CI-red corrective loop must distinguish infra failures from job failures (auto-retry CI on infra flake, don't park) #377
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
toasterson/Anima!377
Loading…
Reference in a new issue
No description provided.
Delete branch "claude/wi-019f9698-ci-red-corrective-loop-must-distinguish"
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?
Anima work item
019f9698-6f38-70d3-a1f8-468e966dabc2.CORRECTION 2026-07-25 14:50 — read before the rest
The closing note of the earlier revision claimed that, with
default_ci_stepsfixed, "a fresh 101 is no longer plausible and the one WI-328 saw could only have been historical."That was wrong. Fresh 101s are not merely plausible; they are essentially the entire failure population. Measured on
solstice_prodfor the last 24 hours:Root cause of the 23:
.solstice/job.shinstalled protoc from jammy (3.12), which rejects theoptional string project_id = 1;added tothread.protoby5c1dd37on 2026-07-11. The lane's last green run was the same day. Fixed in PR #369.The 1 remaining failure (
9beabe2f, commit333fcbaf) was a VM that failed to boot —Dependency failed for /boot/efi, GPT errors — i.e. the second infra class this work item already describes.So the sharpest statement of the problem is: not one native CI failure in the last 24 hours was caused by the code under test, and every implementer handed "CI red, fix your build" was told something false. That is a stronger mandate for the classification work below than the original two anecdotes.
Confirmed cost
Seven work items auto-parked in 24 hours via
auto-parked: CI-red failed dispatch cap reached, five of them Anima:WI-A through WI-D is the entire ACP escalation slice, parked in one day for reasons unconnected to its code. Sessions were burned first — one ran 2200s chasing the phantom.
Note this also revises the WI-328 story below: that
exit_code=101was most likely a real, current protoc failure rather than a replayed three-week-old verdict. The stale-verdict defect (fix 5) is still worth implementing — commit-scoping is correct regardless — but it should no longer be presented as the confirmed explanation of what WI-328 saw. The wrong conclusion came from checking the Forgejo Actions lane, which was green throughout, instead of the native lane, which was 100% red.Problem
When a native-dispatch Solstice CI job fails at the harness/infrastructure layer (the build never ran), the CI-red corrective loop treats it identically to a real code failure. It (1) counts the failure toward the park cap, and (2) re-dispatches the implementer with an infra error as "corrective context" — which the implementer cannot act on. If the implementer then produces no new commit, no new CI run ever fires and the WI silently stalls in
workingindefinitely.The protoc evidence above generalises this: a toolchain failure baked into the runner image is neither an infra flake nor a code failure, and it is invisible to both classifications. It must land in the same "not the implementer's problem" bucket.
Observed (2026-07-24)
WI-A
019f83ce-6c98-7113-a424-1be8d78685fb(escalation backend). Both CI attempts died at VM SSH bring-up under archibald I/O load — the job script never executed:jobresult.v1:success=false exit_code=1,summary="ssh connect failed: SSH operation failed: connect 127.0.0.1:42993: Connection reset by peer (os error 104)".solstice.ci_red_redispatched. Manual intervention (clear thesolstice_dispatchrow + resetfailed_dispatch_count) was required to get another CI run.Even a healthy fleet will hit transient VM-bring-up flakes; the loop must absorb them, not stall.
Root cause (code)
crates/anima-server/src/solstice.rs(JobResult handling, ~L140–260):success=false, callsWorkItemRepo::increment_failed_dispatch_count(pool, wi, cap)(cap =ANIMA_SCHED_FAILED_DISPATCH_CAP, default 3) and re-dispatches the implementer with the log tail. There is no branch on why the job failed.reconcile_missing_ci(crates/anima-server/src/scheduler.rs) only re-dispatches WIs whose head has nosolstice_dispatchrow — so once a (failed) dispatch row exists for the head, nothing re-triggers CI for that commit.The
JobResultalready carries enough to classify: an infra failure has a harnesssummary(ssh connect failed…, VM never booted) and the steps never ran, vs. a job failure where the steps ran and returned non-zero.Proposed fix
infra_failure(harness/VM/SSH/runner error — build did not run),toolchain_failure(steps ran but died in environment setup, before the project's own code was compiled), andjob_failure(the project's build/test returned non-zero). Prefer an explicit signal from the runner/orchestrator over string-matchingsummary.infra_failureortoolchain_failure: do NOT incrementfailed_dispatch_count, and do NOT re-prompt the implementer. Auto re-dispatch the same commit's CI with exponential backoff, up to a separate cap. Onlyjob_failuregoes through the implementer-corrective loop + park cap.CI infrastructure failing/CI toolchain broken) — never park it as if its code failed.solstice_dispatchrow.commit_shaequals the work item's current head. If there is no CI result for the current head, say exactly that rather than reaching backwards.commit_shaand the run's timestamp alongside any CI text put in front of an agent.Acceptance criteria
Files
crates/anima-server/src/solstice.rs(result handling / classification / re-dispatch)crates/anima-server/src/dispatch.rs(re-dispatch CI vs implementer; head-revision scoping)crates/anima-server/src/scheduler.rs(reconcile_missing_cipredicate)crates/anima-solstice/src/results.rs+jobresult.v1shape (explicit failure-class signal)Related
019f9980— pin CI verdicts to the commit under test, and surface native vs Actions lane health separately.019f9079micro-VM executor SPIKE (removes the I/O-saturation root of the flakiness).019f8602reopen WIs when orphaned sessions fail (same family) — itself one of the five parked.019f903c-eeb1-7e71-928b-e248db024384— fleet observability.019f98b5-2ccd-7d80-9842-ec3e395b0d12— a failed turn reads as an empty one.5b0c31cfbbto1f6f390e1d1f6f390e1dtob3b765a2bfb3b765a2bfto12846323af12846323aftob67112c5a7WIP: CI-red corrective loop must distinguish infra failures from job failures (auto-retry CI on infra flake, don't park)to CI-red corrective loop must distinguish infra failures from job failures (auto-retry CI on infra flake, don't park)