# Architecture

## ARC Delegate lifecycle

The `arc_delegate` extension translates worker lifecycle phases into the
runner's three sandbox modes and forwards `runner-routing-v4`. Analyze remains
local in the active Pi parent. The extension adds canonical Implement class and
authorization validation plus the unchanged fail-closed Deploy gate. The runner
remains the source of truth for actual model selection.

Explore, Research, and Plan map to read-only analyze mode. Verify maps to
read-only review. Implement and Deploy map to write-capable implement. Implement
requires an approved class and `implement_authorized: true`; Deploy separately
requires `deploy_authorized: true`.

## Purpose

ARC Pi is a distribution layer, not a source fork. It composes upstream Pi with
ARC policy and the external ARC runner so Pi can remain the acceptance parent
while bounded workers execute ARC Delegate lifecycle phases. The parent is
intentionally thin: automatic work uses **runner-routing-v4** with phase and,
for Implement, workload class; explicit diagnostic routes remain available.

## Components

```text
operator
   |
   v
bin/arc-pi
   |  isolated PI_CODING_AGENT_DIR + gpt-5.6-sol/high defaults
   v
upstream Pi parent
   |-- defaults/AGENTS.md              stable parent policy
   |-- skills/arc-orchestrator/        progressive orchestration guidance
   |-- prompts/orchestrate.md          reusable /orchestrate prompt
   |-- ARC orchestrator extension
   |       |-- arc_delegate (+ session-runs registry writes)
   |       |-- /arc-login + /arc-auth-status
   |       |-- /arc-doctor
   |       |-- /arc-runs
   |       |-- /arc-report
   |-- ARC session-monitor extension
   |       |-- /arc-monitor (human)
   |       |-- arc_monitor_status (snapshot only)
   |       `-- library: getSessionMonitorState / watchSession
   |-- ARC background-terminals extension
   |       |-- arc_terminal_start / status / list / kill
   |       `-- /arc-terminals human dashboard (list, detail, kill, hide)
   |-- ARC subagents extension
   |       |-- subagent_spawn / wait / cancel / check / list
   |       `-- /subagents current-session status and result inspection
   |-- bin/arc-pi-monitor               operator/outer-agent CLI
   |       |-- arc-pi monitor …         thin forwarder
   |       `-- arc-pi monitor tui       forwards to tui/ (Rust delegation viewer)
           v
      bin/arc-orchestrator             discovery wrapper
           v
      external arc-orchestrator CLI  routing, sandbox, locking, traces
           |                           runner-routing-v4
           |-- automatic: phase + mode (+ workload_class for Implement)
           |   parent-local Analyze + Explore / Research / Plan / Verify / Deploy worker chains
           |-- explicit --route aliases (no inherited fallback)
           `-- authorized parent ships git/gh directly (no mechanical routes)
```

### Launcher

`bin/arc-pi` sets `PI_CODING_AGENT_DIR` to `ARC_PI_HOME` (default `~/.arc-pi`) unless the operator provides a separate directory. It starts Pi with `openai-codex`, `gpt-5.6-sol`, and high thinking unless matching command-line flags or environment overrides are present. It also loads the runner path saved by setup and forwards all user arguments without shell interpolation.

Quit/resume hints stay branded for operators: the launcher injects `scripts/rewrite-resume-hint.mjs` via `NODE_OPTIONS --import` so upstream's `To resume this session: pi --session …` line is rewritten to `arc-pi`. This is hint-only; the child process, banner, and help text remain upstream Pi.

### Setup

`scripts/setup.mjs` merges missing defaults into the isolated `settings.json` and `models.json`, registers this package and its `themes/` directory by absolute source path, appends one marked ARC policy block to `AGENTS.md`, and optionally stores an executable ARC runner path in `arc-pi-launcher.json`. The model defaults pin supported OpenAI Codex GPT models to a 272K context window while existing user overrides remain authoritative. Existing user values and unrelated package entries are retained.

### Pi package

`package.json` exposes the TypeScript extensions, skill directory, and prompt directory through Pi's package manifest. Pi loads TypeScript directly and supplies its extension API packages at runtime through peer dependencies. `dist/` is generated for tests and packaging inspection, but runtime installation does not depend on a prebuild.

### Extensions

`package.json` registers four TypeScript extensions. All use Pi's native `ExtensionAPI`.

The orchestrator extension registers `arc_delegate`, the parent-local
`arc_task_artifact`, plus diagnostics and authentication commands. Automatic policy passes
`--routing-policy runner-routing-v4 --phase <phase>` and omits
`--backend`/`--route`/`--worker-model`/`--fallback`; Implement adds
`--workload-class`, and Deploy adds its separate explicit authorization.

`arc_delegate` validates a strict contract, maps automatic policy or diagnostic aliases to canonical runner argv, invokes only the package-local wrapper with an argument array, propagates cancellation, enforces `ARC_PI_DELEGATE_TIMEOUT_MS`, and caps stdout and stderr independently. Failed worker processes throw so Pi records a failed tool result. On start / heartbeat / end it also writes a per-session run registry under `~/.arc-pi/session-runs/` so monitors can attribute runners without TTY heuristics.

Task artifacts are an explicit opt-in boundary. A supplied safe `task_slug`
allows only parsed worker evidence whose status is exactly `completed` to append
to `docs/<task_slug>/<phase>.md`; malformed, failed, blocked, unknown, cancelled,
and Deploy results never write. The parent-local tool supports the same bounded
evidence shape plus Analyze. Artifact files are append-only, use nonblocking,
no-follow file opens, reject non-regular targets before opening, and require the
generated `docs` and task-directory components to be real directories.
Cancellation rolls back only the attempted append, removing a new first-write
file and newly created empty directories. All artifact operations sharing a
`docs` root are serialized, including operations for different task slugs.
Worker session completion is recorded separately before artifact persistence,
so artifact I/O errors reach the caller without rewriting a successful worker
run as failed. These checks assume the
project root is on a trusted local filesystem without a hostile process
concurrently swapping path components; they do not claim a security boundary
for adversarial or network filesystems.

`/arc-login` and `/arc-auth-status` are user-only extension commands. They invoke official Provider CLIs with shell-free argument arrays, use the same configured binaries as ARC worker execution, and reduce status to four redacted classifications. They do not register model providers, add model-callable tools, run at startup, or write authentication state.

The session-monitor extension is library-first ([ADR 0006](adr/0006-session-monitor-library-first.md)). Shared helpers expose `getSessionMonitorState` (snapshot) and `watchSession` (idle-complete loop). Surfaces are thin: `/arc-monitor` for humans, `arc_monitor_status` for the model (snapshot only), and `bin/arc-pi-monitor` / `arc-pi monitor` for operators and outer agents. There is no model-callable watch tool. Soft intervene means `recommend_cancel` evidence only; outside the Pi parent the monitor never SIGTERM/SIGKILL workers.

The background-terminals extension is an independent, in-memory session
runtime for local non-interactive commands. Its four `arc_terminal_*` model
tools and `/arc-terminals` human dashboard share a manager that enforces
per-session concurrency and bounded history. Each command runs in a detached
process group with stdin closed (Unix-only; Windows is rejected). Memory and
tool results retain UTF-8-bounded, sanitized stream tails (U+2028/U+2029
normalized to newlines); complete stdout and stderr go to mode-0600 files
inside a mode-0700 temporary directory, capped per stream
with backpressure. Settlement reaps any surviving descendants and queues one
bounded, sanitized follow-up that Pi stores in conversation history. Session
shutdown suppresses further follow-ups, terminates all process groups with
TERM/KILL escalation, and removes spill directories. After that cleanup
completes, the session's terminal registry records are dropped so a later
reopen of the same session ID cannot expose stale commands, statuses, tails, or
deleted spill paths; failed spill-directory removals stay tracked independently
so a later shutdown can retry them. History eviction removes a settled
terminal's spill directory as well. `/arc-terminals` rejects embedded line
separators and keeps unknown-query notifications single-line. Registry records
and spill files are not written to Pi sessions, the ARC run registry, or the
delegation viewer, so that runtime state cannot resurrect; they remain distinct
from `arc_delegate` jobs. Working directory checks assume a trusted local
filesystem without a hostile process concurrently swapping path components
(TOCTOU); they do not claim a security boundary for adversarial or network
filesystems.

The ARC subagents extension is a separate, session-scoped in-process child
runtime for disjoint work that is already safe to parallelize. Its public
surface remains the five `subagent_*` tools plus `/subagents`, with nonblocking
spawn and at most four active children per parent session. Each child gets an
independently resolved cwd/model context, a fixed built-in tool allowlist, and
an isolated resource loader with no extension resources or tools. Consequently
children cannot receive nested subagent, ARC delegation, background-terminal,
decision, ask-user, or monitor tools. Results, history, errors, status metadata,
and automatic completion follow-ups are bounded and sanitized; a successful
`subagent_wait` claim suppresses duplicate delivery, while a cancelled wait
leaves the one automatic follow-up available. Parent-session shutdown performs
bounded cancellation and forgets the child registry. This isolation is a
resource/tool and policy boundary, not an OS sandbox: allowlisted local bash
executes on the trusted host with the parent process's user permissions.

### Provider CLI authentication

Cursor Agent and Claude Code are Subscription-backed Workers rather than Pi Providers. Cursor owns `cursor-agent login` and its credential store; Anthropic owns `claude auth login` and its credential store. In TUI mode, ARC Pi supplies cancellation and temporary bounded output so browser or device instructions remain visible without entering the conversation or ARC traces. The command then performs one explicit status probe and discards raw output after classification.

This boundary intentionally differs from ARC Pi's isolated Pi configuration: Provider CLI authentication is shared with each operator's normal vendor CLI because Cursor has no documented alternate authentication home. See `CONTEXT.md` and `docs/adr/` for the canonical language and rationale.

### Wrapper and runner

`bin/arc-orchestrator` contains no model-routing logic. It resolves the real runner in this order:

1. `ARC_ORCHESTRATOR_BIN` (including a saved explicit setup path promoted by `arc-pi`);
2. the exact installed `@andysolomon/arc-orchestrator` dependency;
3. `arc-orchestrator`, `arc-orchestrator-runner`, or `arc-runner` on `PATH`, excluding this wrapper itself;
4. known sibling/local checkout layouts.

The installed dependency requires Bun 1.2 or newer and fails with an actionable diagnostic when Bun is absent. Explicit paths are checked for executable permission and self-recursion before launch.

The external runner owns worker model selection under runner-routing-v4,
read/write sandboxing, per-project write locking, availability-only fallback,
structured output, and local traces.

## Runtime data

By default, operator state is isolated under `~/.arc-pi/`:

- `settings.json` — Pi defaults and package registration;
- `models.json` — OpenAI Codex GPT context-window overrides;
- `AGENTS.md` — parent policy;
- `arc-pi-launcher.json` — launcher-only explicit runner path;
- `arc-pi-install.json` — last applied ARC Pi package version and installation root;
- `sessions/` and other Pi-managed state;
- `session-runs/<session-id>/` — orchestrator-owned run registry (start/heartbeat/end) read by the monitor library and CLI. Records additionally carry the launch `phase`, approved `workloadClass`, background `jobId`, and the latest admitted `liveActivity` snapshot so the delegation viewer ([ADR 0007](adr/0007-delegation-viewer-reads-persisted-live-snapshots.md)) can render the lifecycle DAG out of process.

ARC runner traces remain in the runner's default `~/.arc-orchestrator/traces/` unless its environment changes that location. Provider CLI credentials remain in vendor-owned user stores outside `~/.arc-pi`; login output is transient and is not appended to sessions or traces.

## Trust boundaries

The operator trusts the ARC Pi package and upstream Pi with local user permissions. Pi extensions and allowlisted subagent bash execute on the trusted host and can run arbitrary local code with those permissions; child prompt restrictions and the no-extension resource boundary are not an OS sandbox. The wrapper trusts only an explicitly configured or discovered executable. The runner, not the extension, enforces ARC worker sandbox and write serialization. The parent must independently inspect important output and diffs before acceptance.
