0031: Failed tickets retry when the work branch moves, at most twice, and evictions are a record, not a count
- Status: Accepted (backfilled 2026-10-06: records a decision already built)
- Date: 2026-10-06
- Related: 0001 (Orion owns what happens to a ticket)
Contextβ
A ticket fails for two kinds of reason. Its own code is wrong, or something
outside it is: CI without a tool it needs, a sibling ticket that has not
landed, a conflict with work that landed after it started. On a real project
every ticket that failed for an outside reason sat in orion-failed until a
person relabelled it, and the watch stopped behind them.
A queue that keeps retrying, though, spends money on a real defect over and over. And the planner's own refusals (breaker trips, fix rounds, stranded runs) need the same protection: a ticket evicted again and again is a ticket that needs a person.
Decisionβ
Retry when the base moves. The fix for an outside cause arrives as a
change to the work branch. So once the work branch has moved since a ticket
failed, the watch requeues it: one more attempt against the new base, never
against the base it already failed on (internal/watch/retry.go). A missing
prerequisite therefore resolves itself: the ticket waits in orion-failed
until the prerequisite lands, then runs.
At most twice (maxFailedRetries), then the ticket stays failed and is
named under NEEDS YOU. Two is the same ceiling the eviction ledger uses.
The record lives in ~/.orion/state/failed-retries.json.
Evictions are recorded, not derived (internal/queue/ledger.go). The
signals an eviction is decided from are not equally durable: fix rounds live
in a workspace, breaker trips are keyed by worktree, a failed settle leaves
only a dirty worktree. Counting them afresh on every pass would drift as
worktrees are cleaned up, and drift toward forgetting. So each eviction is
written down with its reason, and a ticket evicted twice is held for a person
on the third attempt (EscalateAfter). When the ledger and the tracker
disagree, the tracker wins.
Consequencesβ
- A whole class of failures clears without a person: missing prerequisites, flaky shared checks, conflicts with newer work.
- A real defect costs at most two extra runs before a person is told.
- The ledgers are files in
~/.orion, consistent with 0004; deleting one forgets history, so it is never cleaned automatically.
Alternatives rejectedβ
- Retry on a timer. A failure caused by the base fails again on the same base; time alone fixes nothing.
- Unlimited retries. Indistinguishable from a loop on a real defect.
- Count evictions from the evidence each time. Drifts toward forgetting, so a ticket that should go to a person is retried instead.