Skip to main content
atoll-runner runs one claimed issue at a time with Codex. Atoll controls assignment, project access, repository authorization, and lease ownership. The runner keeps machine-local checkout bindings and recovery evidence.

Install and activate a runner

You do not need an Atoll source checkout. Install the released CLI package and confirm that it contains both commands:
On macOS, confirm that git --version succeeds. Apple Git can remain blocked until you review and accept the Xcode Command Line Tools license. That legal and administrator action must be completed by the machine operator. Create a named profile for the agent and organization, then find the opaque repository reference authorized for the project:
Bind the authorized reference to the repository’s absolute local checkout:
Before the first start, there is no hosted runner row to pause. Remove every eligible assignment from this agent, or use a dedicated setup agent with no assignments. Then run one bounded real cycle to initialize local state and register the row:
This cycle is safe only under that no-work precondition. Binding creates local identity but does not create runner state or server presence; doctor cannot fully pass yet, and --dry-run does not register the runner. When the runner appears, immediately pause it in Workspace Settings → Runners and verify that the fleet row shows Paused. Now complete the read-only checks:
The paused dry run should return no candidate with blocked_reason: intake_paused. For unattended macOS operation, install and verify the user service:
The setup is ready when doctor passes, required bindings validate, Codex is authenticated, the service is loaded, the optional loopback page responds, and there is no unexpected current or retained job. Resume new work in hosted Atoll only after these checks. Then confirm that the fleet row shows Connected and Accepting, and that local status reports connected with active intake. Resuming can immediately claim eligible assigned work. One machine can run several named profiles, including profiles for different organizations. Each process and macOS service is fixed to one profile, agent identity, and organization. Give each enabled diagnostics UI a unique port:
Enable the dashboard on one service only. It shows machine-local process, presence, intake, and current-job status for every configured runner profile. Detailed diagnostics and repository actions remain scoped to the profile that hosts it. Additional dashboards are optional and require different ports. Do not run competing profiles for the same Atoll agent identity. Atoll permits one current local runner installation per authenticated agent. One profile can bind several repositories to different absolute checkouts. The runner creates its owned worktrees under private runner state; the released CLI does not accept a custom worktree root or per-issue working directory.

Prerequisites

Use a CLI build that includes the real Codex worker. Configure a named Atoll profile with an agent key and organization. The package pins both @openai/codex-sdk and @openai/codex to 0.153.4. The pinned runtime must have valid Codex authentication. An unrelated global Codex installation is not a fallback. Use saved Codex login credentials. The worker does not inherit API keys, runner tokens, proxy credentials, or arbitrary environment variables from its parent process. Your issue’s project must have exactly one currently verified repository. You need a local non-bare Git checkout whose origin matches that repository. Credential-bearing HTTPS origins are rejected. Use a credential-free origin and a Git credential helper for repository access. Zero or multiple verified repositories block execution. A local binding does not select a preferred repository or grant project access. Configured external Git clean, smudge, or process filters block worktree preparation. This includes global Git LFS filter configuration, even when the selected repository does not use LFS. Use a checkout and Git configuration without these external filters. The runner does not disable required filters or change your Git configuration. Runner Git commands disable hooks and filesystem-monitor commands to protect the primary checkout.

Bind a local checkout

Use the opaque repo_ref from the project’s authorized repository mapping. Pass the issue UUID to verify that project’s mapping during binding.
The binding stores the canonical checkout path on this machine. The runner checks repository authorization again before dispatch. A saved binding cannot override a revoked mapping or changed origin. To remove an unused binding:
Removal does not delete a checkout or change the project’s server mapping. A binding used by the current job cannot be removed.

Check and run

For unattended macOS operation, install an explicit user LaunchAgent:
Service installation is macOS-only. It writes one user-owned 0600 plist in ~/Library/LaunchAgents/ with canonical Node and runner entry paths, the selected profile, and optional loopback UI arguments. The plist contains no API key, Codex token, lease token, environment variables, checkout path, or shell command. An unknown or changed plist is rejected instead of overwritten. Package installation is passive and never installs or starts this service. The documented upgrade path is service uninstall, package upgrade, doctor, then service install; pause hosted intake first when a no-work maintenance window is required. service status is read-only and reports only bounded installation, load, plist, executable, PID, and last-exit fields. service uninstall bootstraps down the exact label and removes only the exact owned plist. It leaves runner configuration, journals, tokens, logs, branches, and worktrees in place. Status and uninstall validate local ownership and remain available after profile or key removal; install remains authenticated. doctor and status report bounded Codex availability, version, and authentication health without account output or credentials. These checks do not submit a model turn. --dry-run is a read-only dispatch check, not proof that a model turn or worktree preparation has succeeded. Before turn intent, the runner resolves the server’s default ref to an exact commit and prepares an owned branch and worktree under its local storage. It preserves the primary checkout, including unrelated changes, and verifies that snapshot again before terminal reporting and lease finalization. Codex runs with workspace-write, approval policy never, no added writable directories, and sandbox network access disabled. The task can change files in its owned worktree. It does not automatically create a PR, merge, deploy, or publish. Manage pause and resume in hosted Workspace Settings → Runners. Intake is read-only in localhost. Legacy local pause and resume commands return RUNNER_INTAKE_HOSTED_ONLY and do not change server state. Pause does not cancel a held lease. The current worker can finish or reconcile that lease. Loss of lease ownership aborts the model stream. The parent closes its process-local intake gate immediately on SIGINT or SIGTERM. The current child has up to 30 seconds to drain; only a journaled PID with the same process-start identity can receive SIGTERM and, after a further 10 seconds and a final identity check, SIGKILL. A wall-clock gap over 30 seconds is treated as a possible wake: the child is stopped, durable lease and mutation state is reconciled, and local intake reopens only after that pass. A wake may release a worker only to report and finalize an already durable result; a stop never releases a worker. No hosted pause request is sent. Protocol errors are reported as bounded RUNNER_PROTOCOL_MISMATCH failures and leave the pending journal available for replay or operator reconciliation.

Open local setup and diagnostics

Open the printed http://127.0.0.1:4735 URL. Use run --ui --ui-port 4736 or ui --port 4736 if needed. There is no LAN bind option. Do not expose the page through a proxy or tunnel. A port conflict or missing frontend asset does not stop headless runner operation. Without --ui, the runner opens no UI port. --dry-run cannot be combined with --ui. Select the existing Atoll credential profile with --profile at process start. The page shows that profile; it cannot switch a live installation or accept credentials. Use atoll auth to repair a profile, then reload configuration. The diagnostics-only command can show malformed or unreadable runner config without resetting it or dispatching work. The page has five sections: An authorized repository remains visible when unbound. Enter its local checkout path and select Bind checkout. This uses the canonical runner config writer; it does not require an assigned issue. Symlink inputs are resolved before saving. A saved canonical path that later points elsewhere fails revalidation. Wrong origins, missing paths, unsafe filters, or insufficient filesystem permissions block eligibility. Validation does not change the primary checkout. A project with multiple verified repositories can have local bindings, but issue dispatch remains blocked until the upstream routing contract identifies exactly one repository. Local setup never selects a default. A revoked mapping leaves any local binding visible as inert diagnostic data. Refresh, Reload config, Revalidate, and Run preflight read current state without submitting a Codex turn. Remove binding changes only local configuration. Bind/remove are blocked while the runner has a current job. Policy reads and preflight become stale after 60 seconds. The page refreshes while visible and idle; it does not replace text while you edit a binding. The worker independently rechecks authority, runtime, and repository identity before execution. A green diagnostic row does not reserve work or grant access. Atoll failure invalidates repository eligibility immediately on failed readback. Previously persisted runner self state may remain visible as stale context. Local config, jobs, and logs stay available. Authentication failures direct you to profile repair; Codex failures direct you to runtime/auth preflight. There are no local intake, terminal, process-control, arbitrary file-write, model retry, or branch/worktree deletion controls. Cleanup-ready is a read-only state, not deletion authorization. The existing runner loop owns safe lease reconciliation; uncertain model outcomes remain quarantined.

Recovery and retained evidence

The runner keeps one active recovery journal under ~/.atoll/runner/ and stores lease tokens separately with restricted permissions. It persists the thread ID before it accepts turn-start evidence, and persists completion before lease finalization. Raw model messages, reasoning, commands, and tool payloads are not copied into the runner journal or lifecycle comments. A definitive Codex turn.failed event is saved as a bounded failure result and finalized as failed. Raw error text is not included in lifecycle reports. A dead worker after turn intent without durable completion becomes uncertain_outcome. The SDK cannot prove whether an interrupted submitted turn ran. The runner therefore does not submit another turn, create another thread, or resume the same prompt automatically. Keep the evidence for operator reconciliation. A valid attention-resume claim can use only the exact retained thread with matching runner ownership and worktree evidence. Invalid routing stops the resume; it never starts a fresh thread as a fallback. Report-only and acknowledgement-only recovery do not run Codex again. Lifecycle comments contain bounded completion information and real thread/host source metadata. The comment API does not support idempotent creation. If a comment response is lost, recovery reads back the exact prior report. If it cannot find that report, progress stops for operator reconciliation instead of sending a possible duplicate. Terminal branches, worktrees, and local thread evidence remain available for inspection. The runner does not delete them automatically. An uncertain outcome or active writer is not eligible for cleanup.

Manual local smoke

The real Codex path is an opt-in developer harness available only in an Atoll source checkout. It is not included in the installed npm CLI. Loopback remains the normal default. Repository owners may run one bounded production validation turn from a clean source checkout with an exact canonical origin, organization, project, authenticated agent, issue, runner host and instance, and the live project repository mapping’s opaque repo_ref and repository full name. The supplied repo_ref is checked against live issue routing and must never be hard-coded. The smoke derives the canonical source root from its own script, requires a clean source checkout, rebuilds the candidate in the invocation, and records the source root, source HEAD, and worker-entry SHA-256 in bounded mode-0600 evidence. Exact production admission performs zero presence PUT/upsert operations for either connected or disconnected targets. Registration and refresh belong to separate operator setup outside this invocation. The invocation reads the connected runner identity before claim and rechecks it immediately before the claim; a disconnected target fails closed. The runner does not explicitly select a model. The Codex runtime and local Codex configuration supply model selection. @openai/codex and @openai/codex-sdk are pinned to package/runtime version 0.153.4; this version is not a model name. Use the internal AH-2088 production smoke runbook for the owner-only command and cleanup procedure. For the normal loopback fixture profile, use a clean fixture profile with no current or conflicting retained job, against a local Atoll base URL, from packages/cli:
The smoke runs one real Codex turn through the existing runner. It creates and retains its owned local branch and worktree while preserving the primary checkout. Its mode-0600 evidence contains only fixture issue, runner host, lease generation, retained branch/worktree, base/head SHA, thread ID, terminal state, and timestamps, and is written outside the checkout. A timeout is always failure; retained journal state is authoritative. The harness never pushes or deletes retained state, publishes, deploys, or installs a service. Codex Desktop visibility is a separate operator observation; automated tests use a fake SDK and do not spend Codex usage.