Run Lifecycle (Decider)
Relevant source files
The following files were used as context for generating this wiki page:
The Run Lifecycle manages the transitions of an agentic process from a GitHub issue to a pull request. It implements the "decider pattern" using pure functions to handle state transitions, event generation, and side-effect requests. This design keeps the core logic independent of the database, network, and system clock.
Sources: apps/server/src/runs/decider.ts:4-15, AGENTS.md:65-67
Architecture Overview
The lifecycle follows a unidirectional data flow where commands trigger events, events evolve the state, and reactions trigger external side effects.
The system separates pure logic from stateful execution. The outbox worker performs effects after events are stored.
Sources: apps/server/src/runs/decider.ts:5-12, apps/server/src/worker/effects.service.ts:25-29
State Machine
A run progresses through several states, categorized into working and terminal states.
Run States and Sections
| Section | States | Description |
|---|---|---|
| Needs You | awaiting_approval | The agent proposed a plan or opened a PR needing review. |
| Working | queued, planning, implementing, checking, publishing | Active agent processing or automated validation. |
| Done | pr_opened, failed, cancelled | Final states for a specific run lifecycle. |
Sources: apps/web/src/run-list.ts:7-11, apps/server/src/runs/decider.ts:31-48
Lifecycle Transitions
Logic Components
Decider Functions
The core logic resides in three primary functions within decider.ts:
decide(run, command): Validates a command against the current snapshot and returns a list of resulting events. If the command is invalid for the current state, it throws aDomainError.evolve(run, event): A reducer that applies an event to the existing snapshot to produce a new state.react(event, run): Identifies which side effects (e.g., starting a sandbox, posting a GitHub comment) are required based on the new event and state.
Sources: apps/server/src/runs/decider.ts:76-212, apps/server/src/runs/decider.ts:241-258
Commands and Effects
Commands represent external intent, while Effects represent required actions.
| Command Type | Associated Effect | Purpose |
|---|---|---|
StartRun | start_stage(plan) | Initiates a new run from a GitHub issue. |
ApprovePlan | start_stage(implement) | Moves run from plan review to code execution. |
CompleteStage | run_checks / post_comment | Handles transition after agent finishes work. |
RecordChecks | publish | Triggers PR opening if automated checks pass. |
Fail / Cancel | stop_sandbox | Cleans up resources on termination. |
Sources: apps/server/src/runs/decider.ts:50-74, apps/server/src/runs/decider.ts:241-258
Implementation Details
Effect Execution
The EffectsService performs the actual asynchronous work requested by the decider. Before executing an effect, it re-validates the run's current state to ensure the effect is still relevant, as effects are delivered "at least once".
Run Snapshots
The RunSnapshot represents the current data state of a run. It includes:
budgetUsdandcostUsd: Tracking financial limits.revisions: History of plans and human feedback.branch: The git branch name, formatted asopificia/issue-{number}-{id_prefix}.sessionId: The UUID of the current active sandbox session.
Sources: apps/server/src/runs/decider.ts:26-44, apps/server/src/runs/decider.ts:129-131
Budget Enforcement
The EffectsService checks the remaining budget before starting a new stage. If the remaining budget is less than MIN_STAGE_BUDGET_USD (currently 0.50 USD), the run fails immediately to prevent overspending.
Sources: apps/server/src/worker/effects.service.ts:22, apps/server/src/worker/effects.service.ts:66-70
Timeline and UI Representation
The frontend transforms the stream of lifecycle events into a human-readable timeline grouped by stages: Plan, Implement, and Result.
- Stage Headers: Display durations (e.g., "6m 12s") and status markers (Running, Failed).
- Tool Folding: Consecutive
agent.toolevents are grouped into a single row to reduce noise. - Pinned Events: Critical events like
plan.approved,pr.opened, andrun.failedare never folded and remain visible.
Sources: apps/web/src/run/conversation.tsx:289-322, apps/web/src/timeline.test.ts:51-64, apps/web/src/timeline.test.ts:108-115
Timeline Duration Logic
Stage durations are calculated based on the timestamps (at) of the events. A running stage calculates duration from its start time to the current clock time.
Sources: apps/web/src/run/conversation.tsx:275-278, apps/web/src/timeline.test.ts:60-65