ReviewOS

stacks/uplink

feat(engine): fall back to the other engine when one is rate limited

#13
Open glennmichael123 opened this 8 days ago ·
8 days ago

Part of #4. Comes after every other Codex issue: it is the payoff for the seam, not part of building it.

Why it is worth building

The user is on a phone, possibly over satellite, and a reply is the only thing they see. "Claude is rate limited" is a dead end when a second agent is installed and idle.

What is there today

finish() (app/Uplink/uplink.ts#L343) already branches on result.authFailure before replying, so the plug point exists. Nothing above authFailure classifies a failure: a Claude weekly-limit message and a Codex 429 are both just text in result.text.

Proposal

  1. Add a failure classification to EngineResult, alongside authFailure: enough to tell "rate limited" from "the task failed". Codex's own vocabulary for this is visible in the binary (rate_limit_exceeded, usage_limited, budget_limited, workspace_owner_credits_depleted), and Claude's is its limit message.
  2. Add tried: Set<EngineId> to Job (app/Uplink/uplink.ts#L46) so a two-sided limit cannot ping-pong.
  3. On a rate-limited result with an untried engine available, re-begin() the same job against it and say so in the reply, because a silent engine switch would make the answer's style change for no visible reason.

Requirements

  • Never retry a task that did work. A rate limit mid-run may have left files changed. Fall back only when the run produced no tool use, or say plainly in the text that the second agent is starting over.
  • Do not carry the session id across. The other engine cannot resume it.
  • One fallback per job. No loops.
  • Off by default, or clearly visible in the reply. A user who asked for Codex should not silently get Claude.

Acceptance

  • A test proves a rate-limited first engine leads to one run on the second and one reply that names the switch.
  • A test proves a job that already ran tools is not silently retried.
  • A test proves both engines rate limited produces one reply, not a loop.

Sign in to comment on this issue.