Skip to main content
Version: Next

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.