Skip to main content
Blog
Deep diveAgent orchestration / Budget enforcement / AWS AgentCore

Designing Durable Coding Agents with Reconciliation Loops

Foxl Code treats autonomy as a reconciliation loop over approved plans, task state, isolated runtimes, budget-scoped credentials, durable lifecycle events, and eventually consistent GitHub effects.

Foxl TeamUpdated 10 min read

Foxl Code agents running against a plan queue, each with a live per-task budget meter and a daily cap guard.
On this page
Reviewed against the shipped implementation and retained tests on July 16, 2026.

An autonomous coding agent is easy to describe badly: give a model a large prompt, let it run for a long time, and wait for a pull request. That description hides the engineering problem. A model invocation is transient. Tasks, approvals, budgets, repositories, and pull requests outlive it.

Autonomy is not a magic prompt. Foxl Code treats it as a reconciliation loop: persist the state the user approved, observe coding runtimes and GitHub, compute the next bounded action, record the result, and repeat. The model participates in that loop, but it is not the source of truth and it does not get to waive the system's authorization or budget checks.

The unit of autonomy is a loop

A single prompt can produce a plan. It cannot make that plan durable across a closed browser, a recycled runtime, a dropped stream, or a redelivered webhook. Those cases need control-plane state that can be loaded again without reconstructing intent from a chat transcript.

desired = loadConfirmedPlans()
observed = loadTasksAndPullRequests()
actions = compare(desired, observed)
applyBounded(actions)
persistOutcomes()

In Foxl Code, the desired state is a Task Document. Each top-level checkbox represents one unit of work intended to produce one pull request. The document has an explicit lifecycle: draft, awaiting_confirmation, confirmed, in_progress, and finally completed or archived. A scheduled tick may act on a confirmed or in-progress document. It may not turn a draft into running work.

The observed state comes from task rows, structured lifecycle events, and GitHub. A task records its runtime status, repository, budget, model, plan ordinal, and pull request URL. The orchestrator can then ask a narrow question: which approved checkbox has no agent, which running task changed, and which plan has reached a terminal state? The user-facing flow is documented in How Foxl Code works.

Desired state must be safe to replay

Reconciliation calls the same operations more than once, so stable identity matters. A Task Document id is derived from the user and document slug. A subtask id is derived from the document id and checkbox ordinal. Publishing the same plan again updates those rows instead of creating another plan, and the upsert preserves any subtask status and linked agent id that already exist.

Confirmation is also idempotent. Confirming an already confirmed document returns success, while trying to confirm work that has started or ended is rejected as a lifecycle error. This makes approval a durable state transition rather than a phrase the model must remember from an earlier message.

This is the same authority distinction discussed in Two Stores, One Conversation: a convenient transcript may help the agent reason, but the typed plan and task rows decide what is allowed to run.

The heartbeat supplies liveness, not memory

A global scheduler selects users who have an enabled loop and either a non-terminal task or an active plan. It fans those users out to a per-user Heartbeat Durable Object. Before invoking the orchestrator, that object applies the interval, quiet-hours, daily-cap, pause, and in-flight gates.

The tick then reloads repository context, installation membership, active plans, and the latest task-event watermark from durable storage. A confirmed plan with an unassigned checkbox bypasses the normal no-progress shortcut, because there is no task event until the first agent has actually been created. Otherwise, a tick with no new persisted progress skips the model invocation.

The orchestrator is normally invoked on the same stable per-user AgentCore session used by chat, but that warm session is an optimization. The plan remains recoverable from the database if the container is reaped. The loop also prevents overlapping heartbeat ticks for one user and eventually releases a stuck in-flight marker.

This design creates an important contract for new event sources: a state change must either advance the event watermark or create a durable condition that bypasses the no-progress shortcut. A wake-up alone is not sufficient if the tick reloads the same cursor. Otherwise, stored truth can be correct while convergence waits for some later event.

A terminal is not a database

Coding runtimes can produce two very different classes of output. Structured events such as started, github_event, and finished change the task lifecycle. PTY output is a high-frequency stream of ANSI bytes used to render an interactive terminal. Treating both as equally durable made the live stream back up behind storage writes and still did not produce a reliable terminal replay.

TaskDO now persists structured events to Durable Object storage and D1, then broadcasts them to connected clients. PTY output and periodic PTY snapshots are deliberately ephemeral: they are broadcast live without joining the durable event log. The latest bounded screen tail is stored separately so a completed or disconnected task can show its final visible state.

A reconnect can therefore reconstruct task status and persisted GitHub effects. When a snapshot reached TaskDO, it can also render the bounded final screen. It cannot promise a byte-for-byte terminal transcript. Intermediate spinners, alternate-screen updates, or output emitted while no subscriber was connected may be absent. The Tasks page is a view over the control plane, not the authority for whether work completed. See the task lifecycle reference for the durable states.

Stream termination is also ambiguous. A closed stream does not prove that a runtime failed, because the runtime may have pushed a branch before the connection disappeared. Foxl waits through a grace period before synthesizing a failure for a task that is still marked running. A later, verified GitHub webhook can still move that task to review or merged.

Budget is carried as a signed capability

A budget written only into the agent prompt is advisory. For relay-routed tasks, Foxl Code turns the approved task envelope into a short-lived signed credential. The gateway mints it only after checking the user's live GitHub installation membership and confirming that the selected repository belongs to that installation.

{
  sub: userId,
  task_id: taskId,
  model_id: modelId,
  max_budget_usd: cap
}

The JWT also carries a purpose-specific issuer and audience plus issued-at and expiry claims. Its lifetime is capped at two hours. The coding runtime may present it to the relay, but it cannot use the credential to select another task, another user, another model, or a higher ceiling.

On every model request, the relay verifies the signature and claims, loads the task under the token subject, rejects terminal tasks, and enforces the pinned model. It then atomically reserves the request's estimated maximum cost against settled spend plus all other in-flight reservations. If the reservation would cross the ceiling, the request is rejected before the provider is called. When usage becomes known, the reservation is released and the actual amount is settled.

Reservation is necessary because a read-then-write budget check is not safe under parallel requests. Two streams can both observe the same remaining balance and both start. A conditional database update serializes admission, so concurrent calls share one ceiling rather than each receiving it.

The credential is a spending boundary, not an escrow account or a guarantee of graceful shutdown. Provider usage still has to be reported and settled. When the relay refuses another call, runtime finalization attempts to preserve and push useful work, but a process crash or network failure can prevent that cleanup. The user controls are described in budgets and tiers.

GitHub is an eventually consistent peer

Opening a pull request is a distributed side effect. GitHub may accept the request while the runtime loses the response, and webhook deliveries must be handled idempotently. The safe response is reconciliation, not assuming that one HTTP response represents the final truth.

Each task uses a deterministic branch named from the task-id suffix. Finalization pushes that branch and tries to create a pull request. If GitHub reports that a pull request for the head already exists, the runtime queries the open pull requests for that exact head and reuses the existing URL and number. Repeating finalization for the same task therefore does not require a second pull request.

Incoming webhooks are HMAC-verified and recorded under GitHub's delivery id. Pull request events are matched by installation, repository, and the exact task suffix encoded in the branch. The resulting updates assign a lifecycle state and pull request URL, so processing the same transition again has the same outcome. The full repository contract is covered in the GitHub integration documentation.

This is replay tolerance, not exactly-once delivery. A unique delivery record prevents duplicate log rows, but handlers must still be safe when a delivery is processed again. Correlation is strongest for Foxl's canonical branch format. A noncanonical branch falls back to weaker ownership heuristics and should not be treated as an exact task key.

Failure boundaries

The plan upsert, confirmation transition, deterministic branch, pull request recovery, and lifecycle assignments are designed for replay. Task creation itself is not currently an exactly-once operation. The task endpoint allocates a new task id, starts the runtime, and then links that id to the plan ordinal.

If a response is lost after runtime acceptance but before the plan link is durably visible, retrying the spawn can allocate another task. Because the second task has another id, it also has another branch key. Eliminating that window requires a server-side idempotency key for the approved plan ordinal, not another instruction in the model prompt. Until then, duplicate-run detection and operator cleanup remain part of the failure model.

The other boundaries follow the same rule. PTY bytes cannot establish lifecycle truth. A heartbeat cannot grant approval. Runtime success cannot prove that GitHub recorded a pull request. A signed relay token cannot meter a provider call that bypasses the relay. Keeping these claims narrow makes recovery behavior testable.

Operational tradeoffs

  • Durability versus stream fidelity. Persisting lifecycle events keeps the controller recoverable. Not persisting every terminal byte avoids storage backpressure, but terminal replay is intentionally incomplete.
  • Polling versus reaction time. A scheduler makes recovery independent of browser presence and missed callbacks. Event-triggered wake-ups reduce delay, but they are best-effort; the periodic loop remains the backstop.
  • Soft loop cap versus hard task cap. The autonomous-loop daily cap is a gate based on tick accounting. It is separate from the relay's transactional reservation of model spend for one task.
  • Reasoning versus enforcement. The orchestrator can decide which approved action is useful. Confirmation, tenant scope, model pinning, terminal-state checks, and relay budget admission remain deterministic server decisions.
  • Relay enforcement versus direct paths. Atomic budget reservation applies to model calls that cross the relay. Enterprise direct-to-Bedrock and provider-key paths cross a different accounting boundary; a relay credential cannot cap traffic it never observes.
  • Simple key operations versus blast radius. The gateway and relay share signing material for these credentials. That simplifies verification and rotation, but it creates one trust domain: compromise or rotation affects every active token in it.

The current guarantee

Foxl Code's autonomy guarantee is eventual, bounded convergence after explicit approval. A confirmed plan can be rediscovered without the original browser or chat turn. Structured task transitions survive reconnects. Relay-routed model calls are pinned to a task, model, user, expiry, and cumulative budget. Canonical GitHub branches let duplicate pull request effects be recovered and attributed to the correct task.

The system does not guarantee a complete terminal transcript, exactly-once task spawn, successful cleanup after every cutoff, immediate convergence, or relay-level spend enforcement for traffic that bypasses the relay. Those are not footnotes to autonomy. They are the boundaries that make the word operationally meaningful.

References and further reading

  1. How Foxl Code worksDocumentation
  2. Autonomous loop and task reconciliationDocumentation
  3. Budgets and plan limitsDocumentation