CLI Reference
Run AI-powered workflows from your terminal.
Prerequisites
Section titled “Prerequisites”-
Clone the repository and install dependencies:
Terminal window git clone https://github.com/coleam00/Archoncd Archonbun install -
Make CLI globally available (recommended):
Terminal window cd packages/clibun linkThis creates an
archoncommand available from anywhere. -
Authenticate with Claude:
Terminal window claude /login
Note: Examples below use archon (after bun link). If you skip step 2, use bun run cli from the repo directory instead.
Forge operations
Section titled “Forge operations”Use archon forge resolve --data <json> for an explicit remote, archon forge checks --data <json> for a qualified PR, and workitem.view, pr.view, pr.create, pr.edit-body, pr.ready or comment.upsert for the rest. Reads return structured observations; writes report whether they were applied and verified, refused, applied but unverified, or left with an unknown outcome. Pass a request carrying authored text with --data-file <path> so it stays out of argv. See Forge operations for request shapes, plugin configuration, credentials and audit behavior. The bundled SDLC pack still uses gh by default; set ARCHON_SDLC_FORGE=forge to read and write through the plugin instead.
Quick Start
Section titled “Quick Start”# List available workflows (requires a git repo, a registered folder project, or --folder on first use)archon workflow list --cwd /path/to/repo
# Run a workflow (auto-creates isolated worktree by default)archon workflow run assist --cwd /path/to/repo "Explain the authentication flow"
# Explicit branch name for the worktreearchon workflow run plan --cwd /path/to/repo --branch feature-auth "Add OAuth support"
# Opt out of isolation (run in live checkout)archon workflow run assist --cwd /path/to/repo --no-worktree "Quick question"Note: Workflow and isolation commands normally require running from within a git repository (running from subdirectories automatically resolves to the repo root). A non-git directory also works if it’s a registered folder project — or on first use by passing --folder, which registers it and runs in place. The version, help, chat, setup, serve, and doctor commands work anywhere.
Commands
Section titled “Commands”trigger
Section titled “trigger”Start workflows from deployment bindings and inspect durable resource admission. A server with ARCHON_TRIGGER_HOST set hosts the same bindings without these commands. See workflow triggers for configuration, capacity, overlap policy, and recovery requirements.
| Command | Behavior |
|---|---|
trigger fire --config <file> | Record a timer receipt and drain its configured host. |
trigger drain --host <host-id> | Prepare persisted starts and admit eligible queued work. |
trigger list [--limit <1-1000>] | List recent safe receipt summaries (default 50). |
trigger inspect <receipt-or-request-id> | Show source identity, current disposition, blockers, and recovery guidance. |
trigger execute <request-id> --host <host-id> | Explicitly retry an admitted pending execution using the engine claim. |
trigger withdraw <request-id> | Withdraw untouched queued work. |
trigger recover-preparation <receipt> <binding> --owner <owner-id> --yes | Reset preparation only after verifying its recorded owner has stopped. |
trigger schedule <install|remove> --config <file> | Manage a native macOS LaunchAgent with an explicitly configured interval. |
trigger whoami | Print the Archon user ID for your CLI identity, to use as a binding’s runAsUserId. |
chat <message>
Section titled “chat <message>”Send a message to the orchestrator for a one-off AI interaction.
archon chat "What does the orchestrator do?"Interactive setup wizard for credentials and configuration.
archon setup # writes ~/.archon/.env (home scope, default)archon setup --scope project # writes <cwd>/.archon/.env insteadarchon setup --force # overwrite instead of merging (backup still written)archon setup --spawn # open in a new terminal windowFlags:
| Flag | Effect |
|---|---|
--scope home | Write to ~/.archon/.env (default). Applies to every project. |
--scope project | Write to <cwd>/.archon/.env. Overrides user scope for this repo only. |
--force | Overwrite the target file wholesale instead of merging. A timestamped backup is still written. |
--spawn | Open setup wizard in a new terminal window. |
Write safety: archon setup never writes to <cwd>/.env — that file belongs to you. The wizard always targets one archon-owned file chosen by --scope, merges into existing content (so user-added keys survive), and writes a timestamped backup before every rewrite (e.g. ~/.archon/.env.archon-backup-2026-04-20T09-28-11-000Z).
Default assistant + chat model: after you pick the default assistant, the wizard offers an optional default chat model for it — a short curated list (e.g. sonnet/opus/haiku for Claude) plus an “Other…” free-text entry. Press Enter to keep the SDK default. Your selection is recorded in ~/.archon/config.yaml as defaultAssistant (plus assistants.<provider>.model when you chose a model) — the same write as archon ai default <provider> [<model>] — and re-running setup shows the current model. Pi skips the model prompt because its backend/model pair is chosen earlier in the wizard.
doctor
Section titled “doctor”Verify your Archon setup. Runs a checklist of common failure points: Claude binary spawn, Codex binary resolution (env → config → vendor → autodetect, reporting which source resolved), gh CLI auth, Pi auth (when Pi is configured as default), OpenCode runtime SDK presence, database reachability, workspace writability, bundled defaults, folder-project detection (contained repos, when run from one), telemetry state, AI credentials (connected provider count, best-effort), and adapter token pings (Slack/Telegram, best-effort).
archon doctorarchon doctor --full # also probe the OpenCode runtime SDK even when it isn't the configured assistantThe Codex check skips (never fails) when Codex isn’t the configured assistant anywhere and no OpenAI credential is connected, so Claude-only users aren’t nagged about a binary they’ll never use. The OpenCode check only probes that the embedded runtime SDK module resolves — it never boots the runtime (which spawns a child process and binds a port) — and skips unless OpenCode is the configured assistant or --full is passed.
Exit code 0 if all checks pass or are skipped; 1 if any critical check fails. Adapter pings degrade to skip on network errors — a flaky connection does not flip the result red.
Also runs automatically at the end of archon setup (optional).
plugin
Section titled “plugin”Install and manage plugins published on GitHub. A plugin is owner/repo[/path], the directory holding its archon-plugin.json; a version is a tag. Two kinds install: forge plugins and workflow packs.
archon plugin install coleam00/Archon/plugins/forge-github # forge: latest releasearchon plugin install coleam00/Archon/plugins/forge-github@<tag> # a specific releasearchon plugin install owner/repo[/path] # workflow pack: default branch headarchon plugin install owner/repo[/path]@<tag> # workflow pack: a tagarchon plugin update <id>[@<tag>]archon plugin remove <id>archon plugin copy <id> # workflow pack into ./.archon/workflows/archon plugin listinstall refuses an already-installed plugin (use update) and a file it did not install. Every check, including the manifest’s compatibility.archon range, runs before anything is written. Without @<tag>, the manifest at the default branch head decides the kind: a workflow pack installs that commit, and a forge plugin installs its latest release, because its executables exist only as release assets. See Forge operations for what a forge install downloads and where it writes.
A workflow pack installs complete at one commit. The command fetches the tag, or the default branch head, with git fetch --depth 1 into a private repository, reads the plugin directory of that commit, and refuses the pack if that directory holds a symlink, a submodule, a path that escapes it or a file name containing \ or :, if an entrypoint is missing, or if another installed pack has the same owner and name. Git’s credential setup applies to the fetch, but the manifest is first read unauthenticated from raw.githubusercontent.com, so a private repository cannot be installed. The tree is written to ARCHON_HOME/plugins/packs/<id>/<commit>/ and then the receipt to ARCHON_HOME/plugins/installed/<id>/receipt.json, so a reader sees either the previous complete install or the new one. update replaces the tree and prints the old and new commit; remove deletes the receipt and that tree. Nothing updates in the background. Installed entrypoints run as owner/plugin:entrypoint; see Installed workflow packs for the pack layout and how runs resolve them.
copy writes the installed tree to .archon/workflows/<name>/ at the root of the repository you run it in (or --cwd); in a folder project, the directory itself. It refuses when that directory exists. The copy is an ordinary project workflow pack from then on: you own and edit it, and update or remove do not touch it.
auth github
Section titled “auth github”Connect the current CLI user’s GitHub identity via the GitHub device flow, so workflow commits, PR comments, and pushes attribute to you instead of the bot.
archon auth githubOnly meaningful on multi-user installs running GitHub App mode (GITHUB_APP_ID + GITHUB_APP_CLIENT_ID) with TOKEN_ENCRYPTION_KEY set — solo GITHUB_TOKEN installs don’t need it and the command exits with an explanatory error. Your CLI identity is resolved from ARCHON_USER_ID (explicit override) or $USER / $USERNAME, mapped to a stable Archon user via the cli platform identity.
The command prints a verification_uri and a one-time user_code; visit the URL, enter the code, and authorize. On success the access/refresh tokens are stored encrypted (AES-256-GCM) in Archon’s database. Exit code 0 on success; 1 if per-user GitHub is disabled, the identity can’t be resolved, the code expires, or authorization is denied.
Manage per-user AI-provider credentials (API keys + subscriptions) and model-tier config. CLI identity is resolved from ARCHON_USER_ID (explicit override) or $USER / $USERNAME, mapped to a stable Archon user via the cli platform identity — the same as auth github.
The credential subcommands (key set, login, list, logout) work on any install — the vault is auto-provisioned. CLI identity is resolved from ARCHON_USER_ID or $USER/$USERNAME. The config subcommands (tier, alias, default) are ungated — they write ~/.archon/config.yaml and need no identity.
# --- Provider credentials (any install — vault auto-provisioned) ---archon ai key set <vendor> # connect an API key (masked prompt or piped stdin — never argv)archon ai login <vendor> # connect a subscription via OAuth (anthropic, openai, or github-copilot)archon ai list # list connected credentials (metadata only, no secrets)archon ai logout <vendor> # disconnect a credential
# --- Model tiers + aliases + default assistant (ungated config) ---archon ai tier set <small|medium|large> <provider> <model> [--effort <effort>] [--scope user|install]archon ai tier list [--json] # show configured tiers (install + yours) vs built-in defaultsarchon ai tier unset <small|medium|large> [--scope user|install]archon ai alias set <@name> <provider> <model> [--effort <effort>] [--scope user|install]archon ai alias list [--json] # show @custom aliases (install + yours)archon ai alias unset <@name> [--scope user|install]archon ai default <provider> [<model>] [--scope user|install] # set the default assistant (+ optional chat model)
# --- Provider concurrency caps ---archon ai capacity [list] [--json] # provider attempts holding concurrency.providers slotsarchon ai capacity release <attempt-id> # release one whose owner process you verified is goneCredential ids are vendor-keyed (anthropic, openai, github-copilot, plus the Pi backends like openrouter); legacy claude/codex/copilot are accepted and normalized with a printed notice. ai login supports subscription login for anthropic, openai (ChatGPT/Codex), and github-copilot. The openai login is an Archon-owned PKCE flow (#1924): authorize in the browser, then paste the authorization code or the full localhost:1455 redirect URL back at the prompt — nothing needs to listen on that port. The API key is never read from argv (it would leak into shell history): pipe it (echo "$KEY" | archon ai key set openrouter) or type it at the masked prompt.
ai tier, ai alias, and ai default edit the same tiers: / aliases: / defaultAssistant config you can hand-write in ~/.archon/config.yaml (see Configuration) or edit from the console AI Settings page. An unknown provider exits non-zero; tier unset removes the override so the tier falls back to its built-in preset — claude and codex only; for any other default provider the tier is left unset and must be reconfigured before use. The full per-user setup walkthrough is in Per-user credentials and AI Settings.
ai default <provider> [<model>] (default chat model). The optional <model> sets the default chat model alongside the assistant. At --scope install it writes assistants.<provider>.model in ~/.archon/config.yaml (omitting the model leaves that field untouched). At --scope user the provider and model are written atomically to your prefs row — archon ai default pi --scope user clears any previous model pin, since a pin is only meaningful for the provider it was set with. Your chat model applies to direct chat only; workflow nodes keep resolving the large tier. The model may also be an @alias or tier keyword.
--scope user (per-user overrides). On any of the config subcommands, --scope user writes your personal prefs row in Archon’s database instead of the shared config.yaml. Your tiers/aliases/default override the install config for runs and chats you start — nobody else’s. It needs a resolvable CLI identity (ARCHON_USER_ID or $USER) but no TOKEN_ENCRYPTION_KEY (model names aren’t secrets). ai tier list / ai alias list show both scopes, marking your overrides with [just you]. The same scopes are editable in the console as the “This install / Just me” toggle on AI Settings.
telemetry status
Section titled “telemetry status”Show the current anonymous telemetry state: whether it is enabled, the opt-out reason if not, the install UUID, the active PostHog host, and the key source.
archon telemetry statusUseful for verifying that an opt-out env var (DO_NOT_TRACK=1, ARCHON_TELEMETRY_DISABLED=1, CI=true, POSTHOG_API_KEY=off) is being picked up. Inspecting status never creates a telemetry-id file while opted out.
telemetry reset
Section titled “telemetry reset”Rotate the persisted anonymous install UUID at ~/.archon/telemetry-id. The previous ID is overwritten and not recoverable.
archon telemetry resetExit code 0 on success; 1 if the ID file cannot be written.
workflow list [name]
Section titled “workflow list [name]”List workflows available in the target directory, or inspect one workflow by name.
# Compact discovery outputarchon workflow list --cwd /path/to/repo
# Machine-readable output for scriptingarchon workflow list --cwd /path/to/repo --json
# Exact, untouched description for one candidatearchon workflow list archon-fix-github-issue-codex --full --jsonDiscovers flat, one-level grouped, and exact <pack>/<workflow>/ packaged layouts from .archon/workflows/ and ~/.archon/workflows/, plus bundled defaults. See Global Workflows.
The default output is a compact discovery view. Descriptions of at most 160 Unicode code points are returned unchanged. Longer descriptions are whitespace-normalized and reduced to their first sentence when it fits, or to preview content of at most 160 code points at a word boundary. Human-readable output appends [truncated] after a shortened description. JSON keeps the description text separate from its truncation state: descriptionTruncated is true when content was omitted. Fetch the full description before choosing or launching a workflow when that field is true.
The optional name uses the same exact, case-insensitive, suffix, and substring resolution as workflow invocation. Ambiguous names fail instead of choosing a workflow; exact names copied from the discovery output are deterministic.
Flags:
| Flag | Effect |
|---|---|
--cwd <path> | Target directory (required for most use cases) |
--json | Output machine-readable JSON instead of formatted text |
--full | Return exact authored descriptions; combine with name for one workflow’s full detail |
With --json, outputs { "workflows": [...], "errors": [...] }. Each workflow has a description and descriptionTruncated boolean. --full returns the exact authored description and sets descriptionTruncated to false. Optional fields (provider, model, effort, webSearchMode, parseWarnings) are omitted when not set on a workflow. A workflow written with the deprecated modelReasoningEffort: reports its value as effort, which is what it is translated to at load — unless it also declares effort:, which wins. Each parseWarnings entry is a full warning message naming a key the engine dropped or deprecated, the workflow or node where it was found, and what to write instead — see Unknown keys. Discovery errors remain in the top-level errors array when a name filters the workflow list. If the named workflow is missing or ambiguous, the failure envelope is { "ok": false, "error": "...", "errors": [...] }, retaining errors found during the same discovery.
workflow run <name> [message]
Section titled “workflow run <name> [message]”Run a workflow with an optional user message.
# Basic usagearchon workflow run assist --cwd /path/to/repo "What does this function do?"
# With isolationarchon workflow run plan --cwd /path/to/repo --branch feature-x "Add caching"
# Supplying a workflow's declared inputs (one flag per input)archon workflow run review-block --cwd /path/to/repo \ --input diff="$(git diff)" --input style=terse "focus on the auth changes"
# Rebind only the large tier for this runarchon workflow run issue-to-pr --cwd /path/to/repo \ --model large=openai/gpt-5.6 "fix #2481"
# Load a saved sparse layer, then replace only its large tier for this runarchon workflow run issue-to-pr --cwd /path/to/repo \ --config ./config.minimax.yaml \ --model large=openai/gpt-5.6 "fix #2482"
# Continue the exact estate left by a terminal runarchon workflow run archon-ship --cwd /path/to/repo \ --adopt 6d5066ca-47b4-4ee8-8d1d-2f3db8039190 "finish the delivery"Progress events (node start/complete/fail/skip, approval gates) are written to stderr during execution. The workflow-start output also names the absolute local path of the run’s JSONL transcript. Use workflow logs when you have a run ID and want the content rather than the path.
If the workflow’s YAML declares keys the engine ignores, a warning naming each one is written to stderr before the run starts. This matters to --detach --json callers: --json silences all logging, so stderr is the only channel left, and it keeps stdout to exactly the JSON payload.
Note that a real run emits a JSON payload only under --detach. Without it, --json suppresses logs but the command still prints human progress to stdout (Running workflow: …), so do not pipe a plain real run --json into a parser. The side-effect-free --dry-run --json mode below is the other exception: it emits exactly one complete trace document. See Unknown keys.
Flags:
| Flag | Effect |
|---|---|
--cwd <path> | Target directory (required for most use cases) |
--workflow-source <path> | Read the workflow, its commands, and its scripts from this directory instead of --cwd. Lets an uncommitted workflow in one checkout run against a different checkout, repository, or folder project, with no commit, push, or merge. Fresh runs only — rejected with --resume, because a resumed run executes the source it already captured. See Running a workflow from another checkout. |
--branch <name> | Explicit branch name for the worktree |
--from <branch>, --from-branch <branch> | Start-point for the new worktree only — unlike --base, it does not change the PR target |
--base <branch> | Per-dispatch base override for a single run. Sets both the worktree cut-from and the PR target ($BASE_BRANCH), and outranks worktree.baseBranch in config plus the codebase default — see Base branch precedence below. The branch must already exist on the remote; a missing one is a hard error, not a fallback. Combine with --from to drive the two separately. Rejected with --no-worktree, --folder, and workflows pinning worktree.enabled: false. |
--no-worktree | Opt out of isolation — run directly in live checkout |
--folder | Register the current non-git directory as a folder project (first use) and run in place — no worktree. Rejects --branch/--from/--base. |
--container | Run a folder project inside an overlay-isolated Docker container instead of in place (writes land in an overlay, not the live root, until an approval-gated write-back). Folder-only; a repo project errors. Requires the runner image (bun run build:runner-image). Pauses docker stop the container; --resume/approve/reject rediscover and restart it. See the Container isolation guide and configuration. |
--input <name>=<value> | Supply one value for the workflow’s declared inputs:. Repeat the flag per input. Splits on the first =, so the value may itself contain =; --input name= supplies an empty string. Omitted inputs take their declared default:. A missing required input or an undeclared name is refused before any worktree, clone, or AI cost, through the same contract a composing with: map goes through. Works with --dry-run (inputs resolve exactly as in a real run). Rejected with --resume (a resume replays the inputs recorded on the run). See Running a workflow that declares inputs. |
--model <name>=<spec> | Rebind one small, medium, large, or existing @alias for this run. Repeat the flag per binding. An Archon agent prefix selects that agent (codex/gpt-5.6-sol); another valid vendor/model ref selects Pi (openai/gpt-5.6); an unqualified model keeps the binding’s current provider; a tier or alias RHS copies that preset. Unspecified names keep their user → repo → global → built-in values. Literal model: pins and nodes that never reference the rebound name do not change. Bare --model <spec> is invalid, there is no run-wide --provider, and the flag is rejected with --resume. Works with --dry-run. |
--config <path> | Load one sparse YAML config layer for this fresh run. Relative paths resolve from the directory named by --cwd, even when it is a repository subdirectory. Values in the file override persistent config and user AI preferences; explicit --model flags then replace only their named bindings. Works with --dry-run and --detach; the parent validates and seals the layer before handing it to a detached child, so later file edits cannot change that launch. Rejected with --resume because a continuation restores the sealed layer recorded when the run started. |
--resume | Resume from last failed run at the working path (skips completed nodes) |
--adopt <run-id> | Start a new run in a terminal run’s exact worktree or branch, with adopted_from_run_id provenance and $ADOPTED_RUN_DIR access. Run-id selection is exact; adoption never infers a run from workflow name or prompt text. |
--supersedes <run-id> | Start in a fresh estate while recording that this run replaces a terminal prior run. Unlike --adopt, it inherits no checkout. |
--quiet, -q | Suppress all progress output to stderr |
--verbose, -v | Also show tool-level events (tool name and duration) |
--detach | Run in a detached background child and return immediately. The child does all the work; find it later with workflow runs/workflow get. For workflow run, human output names both files and the --json acknowledgement carries runId, transcriptPath (the structured per-run JSONL), and logPath (the detached child process’s stdout/stderr capture). These paths are intentionally distinct. Use workflow logs <run-id> --follow for execution events and workflow wait <run-id> when a host needs the next terminal or gate transition. Also available on approve/reject/resume; their acknowledgement differs — see Detached control verbs. |
--dry-run | Simulate deterministic DAG control flow in memory. Creates no run, worktree, session, event, artifact, or provider request. |
--stubs <path> | YAML mapping of node ids to scalar or structured outputs for --dry-run. Relative paths resolve from --cwd. |
--stubs-init <path> | Write a complete stub scaffold for the expanded workflow and exit. Refuses to overwrite an existing file. Relative paths resolve from --cwd. |
--default-stubs | Fill reachable nodes omitted from --stubs with schema-valid placeholders. Explicit stubs still win; without this flag, a missing reachable stub remains an error unless the node declares trigger_rule: all_done. |
--exec-code | During --dry-run, execute trusted bash:/script: nodes locally instead of requiring stubs. Default is no code execution. |
--pause-at-gates | During --dry-run, stop at the first approval gate instead of auto-approving it. |
Per-run config files
Section titled “Per-run config files”A run config is an ordinary YAML file selected explicitly for one invocation. It is useful for reusable choices such as config.minimax.yaml, but it is not a registered profile and does not change .archon/config.yaml.
tiers: large: { provider: pi, model: minimax/MiniMax-M3 }env: BENCH_MODE: "1"The layer is sparse: omitted settings keep their normal lower-layer values. In --config ./config.minimax.yaml --model large=openai/gpt-5.6, only large is replaced by the flag; small, medium, aliases, and every other omitted setting still fall through. See Run-scoped configuration for the supported keys and fail-fast exclusions.
Running a workflow from another checkout
Section titled “Running a workflow from another checkout”--cwd and --workflow-source answer two different questions: what the run acts on, and where the workflow itself is read from. They are the same directory unless you say otherwise.
# Author in ~/dev/archon (uncommitted), run against a clean checkout elsewherearchon workflow run implement \ --workflow-source ~/dev/archon \ --cwd ~/checkouts/archon-target \ "implement the plan"Every run freezes its workflow source when it starts, whether or not you pass this flag. That means the source directory’s .archon/workflows, .archon/commands, and .archon/scripts, plus your home-scoped ~/.archon/ source and Archon’s own bundled defaults. Everything a static include: can reach is frozen together, so an included global or bundled workflow cannot change shape under a run either. That freeze is what makes the rest predictable:
- The target stays clean. Nothing from the source checkout is written into it, so its
git statusand its validators only ever see its own files. - A run does not change shape while it is running. Edit, move, or delete the source checkout after a run starts and a resume still executes what the run began with. Start a new run to pick up the edits.
- Resume needs no source path. Every continuation —
archon workflow resume <run-id>,archon workflow run <name> --resume,approve, andreject— loads the run’s own captured source, which is why--workflow-sourceis refused alongside--resume.
The captured source lives beside that run’s artifacts, at ~/.archon/workspaces/<project>/workflow-source/runs/<run-id>/, and stays as long as the run’s other output does. It is deliberately not inside $ARTIFACTS_DIR: that directory is the run’s output channel, handed to every node and listed for inspection, and the frozen source is neither. Its manifest records a content digest and source-resolution settings, which the run row also stores. Archon checks those pinned values during initial discovery, on resume, and immediately before every later filesystem-backed command, named script, or composed-source read, hashing the full capture each time. If the capture is missing, changed, or replaced, the read and any container dispatch are refused. Start a fresh run to execute the current workflow.
These checks detect a mismatch present when Archon checks; they do not seal the directory or turn workflow execution into a security sandbox. Another process running as the Archon user can still change a file after a check, and already-started parallel nodes are not cancelled. Inline node bodies and command bodies compiled into an included graph are already in memory. A runtime workflow: child deliberately captures the live authoring source when that child starts, then receives its own pinned capture. If the parent’s recorded authoring directory has moved or been deleted, Archon refuses to start the child instead of capturing a same-named workflow from the target checkout.
The one exception is a run that started before Archon captured source at all. It has nothing recorded to honor, so it resumes against the current source on disk with a warning. A run whose source record exists but cannot be read is not treated that way — it fails, because a run that recorded its source must never quietly execute something else.
An older captured run may have a digest in its run row but no out-of-band copy of its source-resolution settings. Archon still verifies its tree against that digest, reads the settings from the manifest, and on the first resume records them on the run row beside the digest, with a warning. Nothing can prove those settings are the ones the run started with, since the manifest is outside the digest; pinning them closes the window, so a later edit to the manifest is refused like any other change.
Compiled binaries. A binary embeds its bundled workflows, commands, and scripts as constants rather than files. Those get written into the capture alongside everything else, so they are covered by the same digest — which means upgrading Archon between pausing and resuming a run is fine: the run keeps executing the bundled content it started with, and a change to those bytes would be caught like any other.
--container runs get the same treatment: Archon checks the host capture before dispatch, then bind-mounts the capture read-only at the same absolute path inside the container. A named script therefore resolves identically whether a node runs on the host or in the container, and a container recreated on resume gets the mount back. $ARTIFACTS_DIR is bound the same way but read-write, so a node’s outputs land on the host. The read-only bind is not hostile-process containment: native overlay mode has CAP_SYS_ADMIN, and a same-UID host process can still race a check. --workflow-source is refused with --container — a folder project runs in place, so its source and its target are the same directory by definition.
Deterministic dry-run
Section titled “Deterministic dry-run”Use dry-run to test DAG routing, joins, loops, when:, strict output fields, and variable substitution without starting a real workflow:
cat > stubs.yaml <<'YAML'classify: issue_type: bug severity: highinvestigate: "Root cause: stale cache"YAML
archon workflow run triage --cwd /path/to/repo \ --dry-run --stubs stubs.yaml "Issue #2100"
# One complete JSON document, safe to pipe in CIarchon workflow run triage --cwd /path/to/repo \ --dry-run --stubs stubs.yaml --json | jq '.trace, .outcome, .authoredOutcome'Generate a starting fixture when a composed workflow has many nodes, then keep only the values that drive the path you want to test:
archon workflow run deliver --cwd /path/to/repo \ --dry-run --stubs-init fixtures/deliver.yaml
# After editing the load-bearing values in the generated YAML:archon workflow run deliver --cwd /path/to/repo \ --dry-run --stubs fixtures/deliver.yaml --default-stubsThe scaffold is derived from the already-expanded workflow, so included top-level nodes use their flattened ids (for example, review__classify) — while loop_group BODY nodes keep their bare ids even inside an included block (the group node gets the <includeId>__ prefix; its body does not). Prefer --stubs-init over hand-writing keys: the scaffold is the authoritative source for both spellings. Structured output_format values are emitted as YAML objects with native booleans, numbers, arrays, and nested required properties. Loop completion fields are generated as true. If Archon cannot prove that a generated value satisfies its JSON Schema, scaffold generation fails before creating the file.
The stub file must contain one YAML mapping. Each value is either a string or an object. Object stubs are preserved as structured output, so downstream $classify.output.severity references behave like live structured producers. Strict coverage remains the default: a reachable AI, bash, or script node without a stub fails the simulation and appears in missingStubs. The one exception is a trigger_rule: all_done join — it runs whatever its upstream did, so a real run reaches it regardless of stub data. It gets a generated placeholder and is listed in both missingStubs and toleratedMissingStubs, and the absent stub alone never fails the simulation or a workflow test fixture. It is only listed in toleratedMissingStubs once that placeholder exists: a node whose output_format cannot produce one still fails, and stays out of the tolerated list. A loop: node carrying the same trigger rule is tolerated on the same terms, with one addition — the placeholder must also end the loop, which it does by satisfying the node’s own completion channel, so a tolerated loop always completes on its first iteration. The one loop shape it cannot end is an output_format whose only completion channel is a prose until:, because the generated JSON never carries the sentinel; that node still fails and stays out of the tolerated list. Add --default-stubs to fill only omitted reachable nodes with the same schema-aware placeholders used by scaffold generation; explicit values always take precedence. A reachable bash: or script: node that arrived through include: must always be stubbed: the simulation does not deliver the caller’s with: values to it, so a body that reads its inputs fails rather than running. Supplied stubs for unknown or unreachable nodes appear in unusedStubs, while generated placeholders do not. Whole-output references retain their normal lenient behavior, while invalid strict $node.output.field references fail the consuming node exactly as they do in a real run. See Node Output References.
A workflow’s declared inputs: resolve exactly as in a real run: omitted inputs take their declared default:, --input name=value binds a value (visible in the trace’s resolved text), and a missing required input or an undeclared name fails at the invocation gate with the same errors a real run gives — before any trace output. With --exec-code, bash and script nodes also receive the run-level inputs as the same INPUTS_<UPPER_SNAKE> environment variables a real run delivers (a composed block’s own inputs for named scripts are a real-run-only channel).
By default, bash and script nodes are never executed. --exec-code is an explicit opt-in for trusted local workflow code and is the only dry-run mode that can cause code-level side effects. Executed nodes receive $ARTIFACTS_DIR and $STATE_DIR under an ephemeral per-simulation directory in ~/.archon/temp/ (honoring ARCHON_HOME), created before the first executed node and removed when the simulation ends — a dry run writes nothing inside the repository, and a simulation that executes nothing creates no directory at all. Approval nodes auto-complete unless --pause-at-gates is set. Runtime workflow: sub-runs are reported as unsupported instead of being launched. Dry-run is incompatible with lifecycle and isolation flags such as --branch, --no-worktree, --folder, --container, --resume, and --detach.
The ordered trace records each node as completed, stubbed, skipped, failed, or paused, including its reason, resolved text, and safe output. The result reports two independent facts. outcome is the simulation execution result (completed, failed, paused, or cancelled) and still controls the CLI exit code. authoredOutcome is succeeded, failed, or null, derived only when the workflow declares returns: plus a boolean outcome_field: and the fixture supplies that structured result. Generated placeholders from --default-stubs or an unstubbed all_done node do not invent an authored verdict. A completed simulation can therefore report a failed authored outcome. Human output labels these as Simulation outcome: and Authored outcome:; simulation JSON always includes both fields. --stubs-init --json does not run a simulation and instead returns a scaffold acknowledgement with workflow, stubsPath, and nodeCount.
Every node that takes an AI turn also reports which provider and model it will run on, and where each value came from — the same resolution the executor performs, not a second implementation of it. This is how you answer “what will this node actually run on” for a workflow that composes others, since a composed workflow runs with the configuration its own file declares:
STUBBED review__scope (prompt) runs on: codex (node) / gpt-5.6-sol (node) [from review-block] effort: high (node)The origin in parentheses is one of node, model ref (a tier keyword or @alias), workflow, assistant config, or default assistant. [from <name>] names the workflow file a composed node was authored in. A node whose declared provider: disagrees with the provider its model: ref resolves to also reports the warning a real run would emit. --json carries the same values under each trace entry’s resolution object.
This validates deterministic engine wiring; it does not validate model reasoning. It adds no workflow-YAML language surface: YAML coordinates, code computes, and agents judge.
Default (no flags):
- Creates worktree with auto-generated branch (
archon/task-<workflow>-<timestamp>) - Auto-registers codebase if in a git repo
With --branch:
- Creates/reuses worktree at
~/.archon/workspaces/<owner>/<repo>/worktrees/<branch>/ - Reuses existing worktree if healthy
With --no-worktree:
- Runs in target directory directly (no isolation)
- Mutually exclusive with
--branch,--from, and--base
Base branch precedence
Section titled “Base branch precedence”“Base branch” means two things, and by default one flag sets both: the cut-from
(what git worktree add branches off) and the PR target ($BASE_BRANCH, which
bash nodes pass to gh pr create --base). Four sources can supply it, highest first:
| Precedence | Source | Scope |
|---|---|---|
| 1 | --base <branch> | one dispatch |
| 2 | worktree.baseBranch in .archon/config.yaml | the repo |
| 3 | The registered codebase’s stored default branch | the repo |
| 4 | Git auto-detection (origin/HEAD, then origin/main) | the repo |
Levels 2—4 are static per repo, so a run that needs a different base than its
neighbours had to edit config — global, and racy when several runs dispatch at
once. --base is the per-dispatch level, which is what makes parallel multi-base
dispatch (epic slices, A/B variants) config-free.
Scope: the dispatched run only. A workflow: node with isolation: worktree
creates a worktree for its child run, and that worktree is cut using levels 2—4
only — --base and --from do not propagate to sub-run children. A parent
dispatched with --base release/2.0 still branches its isolated children off the
repo’s configured base. See Choosing the child’s
checkout.
Driving cut-from and PR target separately. --from overrides only the
cut-from, so pairing the two flags splits them:
# Branch off release/2.0, but open the PR against devarchon workflow run implement --from origin/release/2.0 --base dev "Backport the fix"--from is handed to git worktree add verbatim, so a remote ref such as
origin/release/2.0 works. Note the sync-before-create step refreshes the
--base branch, not the --from start point — pass a remote ref when the local
copy of the start point may be stale.
Where --base is rejected (rather than half-applied): with --no-worktree,
against a folder project,
and against a workflow pinning worktree.enabled: false. None of these create a
worktree, so the flag could only move the PR target — which would report a base
no worktree was ever cut from.
When an existing worktree is adopted — --branch naming a healthy worktree,
or --resume continuing a prior run — the cut-from is already fixed, so --base
changes only the PR target. Archon warns in both cases.
A continuation keeps the base it started with. A run records its resolved
$BASE_BRANCH when it starts, and every continuation of it — --resume, an approved
gate, or the automatic resume of a parent whose sub-run gate was approved — reports that
value rather than re-running levels 2—4 against whatever config and git say later.
Passing --base again still retargets the PR, as above. A run started before Archon
recorded this re-resolves through levels 2—4 and logs
workflow.dispatch_not_recorded_resolving_live when it does.
Continuing an existing estate
Section titled “Continuing an existing estate”Use structured continuation whenever a workflow must work on an existing branch or pull request. If you have the prior run id, --adopt <run-id> is the authoritative form. Archon reuses the prior worktree as-is. If the prior worktree is gone, Archon reuses a same-repository checkout already holding that branch, or creates one on the exact local branch. It does not fetch, reset, or synchronize the branch; update it first if the remote advanced.
Every node in the new run uses that selected checkout, including bash/script delivery assertions. A branch name written only in the message is model context; it does not move engine-owned nodes to another checkout.
Name Matching:
Workflow names are resolved using a 4-tier fallback hierarchy. This applies consistently across the CLI and all chat platforms (Slack, Telegram, Web, GitHub, Discord):
- Exact match -
archon-assistmatchesarchon-assist - Case-insensitive -
Archon-Assistmatchesarchon-assist - Suffix match -
assistmatchesarchon-assist(looks for-assistsuffix) - Substring match -
smartmatchesarchon-smart-pr-review
If multiple workflows match at the same tier, an error lists the candidates:
Ambiguous workflow 'review'. Did you mean: - archon-review - custom-reviewworkflow status
Section titled “workflow status”Show active workflow runs (running and paused) for the current project. The project is resolved from cwd through the registered checkout, including linked git worktrees. Use --all for install-wide active runs. For full history (all statuses), use workflow runs.
archon workflow statusarchon workflow status --jsonarchon workflow status --all # active runs across all projectsarchon workflow status --verbose # add a per-node summary for each runarchon workflow status --json --verboseIf cwd is an unregistered Git checkout, the command falls back to install-wide active runs and says so. Every successful JSON result carries scopeFallback: true for that fallback and false for a resolved project or explicit --all request. A registry lookup failure fails the command instead of returning install-wide runs under the fallback label. A registered folder project scopes normally; an unregistered non-repository directory is rejected with Not in a git repository before status lookup.
The normal human and JSON views include every active node without fetching each run’s event
history. In JSON, active_nodes is the ordered list of unresolved node starts: node_started adds
an identifier and node_suspended keeps it active, while node_completed, node_failed, node_skipped, and
node_skipped_prior_success remove it. Retries re-add the node in their new start position. This
is node lifecycle state, not evidence that a process owner is alive.
workflow runs
Section titled “workflow runs”List recent runs of every status (completed, failed, cancelled, running, paused) for the current project. The project is resolved from cwd the same way workflow run does. Complements workflow status (which is active-only).
archon workflow runsarchon workflow runs --jsonarchon workflow runs --status failed # filter to one statusarchon workflow runs --limit 50 # cap rows (default 20)archon workflow runs --all # list across all projects (ignore cwd scope)If cwd is not a registered project, the command falls back to a global list and says so — --json carries this as a scopeFallback: true field so a consuming agent never mistakes a global result for a project-scoped one.
The run-list JSON uses the same active_nodes contract as workflow status. The retained singular
fields are compatibility fields: current_step_name and current_step_status are populated only
when exactly one node is active, and are null for zero or concurrent active nodes. total_steps
is always null because lifecycle events do not own a truthful declared DAG total.
The listing shows short 8-character run ids. Every <run-id> command below (get, logs, wait, resume, cancel, abandon, approve, reject) accepts these short ids when run from the project directory: a unique prefix resolves to the full id, an ambiguous prefix errors, and full ids keep working from any directory. Short ids from --all rows belonging to other projects can’t be resolved — use the full id from --json for those.
workflow get
Section titled “workflow get”Show detail for a single run by ID, regardless of status (unlike status, which is active-only). Use it to inspect both how execution progressed and, when declared, the workflow author’s verdict. Exits non-zero when the run is not found.
archon workflow get <run-id>archon workflow get <run-id> --jsonarchon workflow get <run-id> --verbose # add the per-node summaryarchon workflow get <run-id> --json --verboseworkflow status, workflow runs, and workflow get report two independent facts:
- Execution status (
pending,running,paused,completed,failed, orcancelled) controls terminality, resume and cancellation, filters, and CLI exit behavior. - Authored outcome (
succeededorfailed) is the workflow’s declared verdict fromoutcome_field. Human output labels it separately when present. JSON always carries it as the nullableoutcomefield besidestatus.
The axes may disagree without either being rewritten: completed with authored outcome failed
means execution finished normally but the workflow judged the work unsuccessful; failed or
paused with authored outcome succeeded records a successful verdict before execution later
failed or paused. A null outcome means the workflow did not declare one, the selected node has not
authored it yet, or the run predates the field. In that case human output keeps its status-only
presentation. Foreground workflow run uses the same labels when an outcome exists, but its exit
code remains driven by execution success or failure.
Verbose JSON node summaries include execution for newly recorded work. It identifies the
invocation, attempt, provider, requested model and any model the provider reported. Inner retries
share an invocation; each retry has its own attempt. A new loop-group iteration creates new body
invocations. Resuming unfinished work keeps its invocation identity.
The run’s starting checkout appears as checkout_baseline in JSON and as the Start: line
in human output. The engine observes it once, right after the run wins its execution claim
and before the first node, and never rewrites it — a resume keeps it. A Git observation gives
the commit, that commit’s tree, whether the worktree was clean or dirty (with staged,
unstaged, and untracked counts), and, when Archon created the run’s branch, the commit it was
cut from. not_git means the directory is not in a Git repository; unavailable means the
engine could not read it. null means not recorded: the run predates this field or never
started. Node execution records carry the same observation as invocation.checkoutStart and
attempt.checkoutStart, so you can see which commit each node started at.
Pressing Ctrl-C on a foreground run stops it without losing it. The process that owns the
run records why it stopped, and the run stays failed, which is the resumable status: it is
still found by workflow run <name> --resume and accepted by workflow resume <run-id>.
For a failed run, human output adds a Stopped: line naming the interrupt and the
signal, above the usual Error: line. JSON carries the same fact as metadata.stop_reason, an object with the
categorical reason and the signal that arrived. Resuming the run clears it, so a run
that resumed and then completed does not keep reporting an interrupt. Runs that stopped
before this field existed carry no stop_reason.
Each usage observation is either { source: "provider", value: ... } or unavailable. Reasons
separate unsupported reporting, a supported value not reported, unknown capability, non-provider
work and invalid reported numbers. A reported zero stays zero. Historical nodes omit execution
when their rows lack these facts; they do not receive a guessed model or start time.
timing.durationMs is elapsed wall time, not active compute time. Resumed loop durations can
include time spent paused; bare approval retains the duration observed before the pause. Public
records contain at most eight session-ID characters, never the full continuation handle. JSONL
transcripts retain the names node_start, node_complete and node_error; their execution
metadata describes the same fact as the durable node event. Suspended nodes remain active and
appear as running until their gate or wait resolves.
Every workflow get --json shape includes terminal_record. The API detail endpoint,
GET /api/workflows/runs/:runId, exposes the same value as run.terminal_record.
The engine persists this record with the terminal status transition, including detached
runs and failures before a reporting node. It contains observed node states and skip
causes, the selected returns: value or its explicit unavailability, and an artifact
manifest. Historical runs without a record and active resumed runs report null.
An event-query failure exits non-zero with error: "workflow_events_unavailable" in
JSON; it does not masquerade as a missing historical record.
The artifact manifest records paths, sizes, and typed metadata observed at termination.
It survives later file deletion, but does not preserve file contents or contain previews.
Its limitations array identifies missing roots, unreadable entries, invalid metadata,
and excluded links. Files may change during the scan, especially during cancellation:
this is an observation, not an atomic filesystem snapshot. The separate leave-behind
file listing reflects the filesystem when you query it.
leave_behind.artifactFiles lists the files a person or an agent would open, capped at
200 for display. Each directory contributes its own files before its subdirectories,
sorted by name, so a run’s top-level reports lead the list. It excludes the engine’s
own $ARTIFACTS_DIR/.archon/ child — the typed-artifact listings and node-output
spills the engine writes for itself, which on a long run outnumber the reports.
Nothing leaves the list quietly: leave_behind.artifactFilesOmitted reports how many
engine-internal files were skipped (internalFiles), whether the display cap was
reached (truncated), and any directory the walk could not read (unreadable). Human
output prints the same facts under the file list. The console run page leaves out the
same child and nothing else, so both show a workflow’s own dotfiles.
Human output includes Transcript: <path>. Every successful JSON shape includes the
same value as transcript_path, including verbose node summaries and raw events. A
historical run whose storage location can no longer be resolved remains inspectable and
reports null (human output says (unavailable)) instead of guessing a path from the
current directory.
For both commands, --json --verbose adds a nodes array. Nodes are ordered by the
first appearance of each node in the deterministically ordered event stream. Every
entry includes nodeId and state; nodes with a start event include the original ISO
startedAt, and terminal nodes with both start and end events include durationMs.
Completed nodes may include an outputPreview, truncated after 200 characters with
ASCII ..., while failed nodes include error (or Unknown error when none was
recorded).
Add --events to --json --verbose to return raw events rows instead of nodes for
debugging. Raw events are not the recommended integration surface.
workflow logs
Section titled “workflow logs”Print the run’s existing JSONL transcript, or follow it as rows are appended:
archon workflow logs <run-id>archon workflow logs <run-id> --followWithout --follow, the command copies the snapshot that exists at invocation time to
stdout and exits. With --follow, it announces the resolved local path on stderr, waits
for a live run’s file to appear, and streams appended bytes until the run becomes
completed, failed, or cancelled. A paused run is still live: the follower stays
attached across approval gates and resumes, reading the same file. Each resume appends one
workflow_resume row (the run’s first execution writes workflow_start), and each gate
approval or rejection appends a gate_decision row with the gate’s step, the
decision, and the operator’s comment or rejection reason in content.
Stdout is the transcript’s exact JSONL, with no log messages or wrapper document. Each
line is one persisted event and fields may be added over time, so consumers should parse
the fields they need and tolerate others. --json is invalid because the output is
already JSONL and a live stream cannot satisfy the CLI’s one-document JSON contract;
--events is also limited to workflow status/get.
A missing or empty snapshot exits 1; for a live run the diagnostic points to
--follow. Follow mode waits while the run is live, performs a final read after a
terminal status, and exits 1 if a terminal run never produced content or if the file
shrinks. Stopping the follower only stops the reader. It never resumes, cancels, or
otherwise changes the run.
Transcripts can contain user prompts, tool inputs, and retained subprocess output. Treat access to the local file as access to the run’s input and execution data.
workflow wait
Section titled “workflow wait”Block until a run reaches a state it will not leave on its own — it finished, parked
on a gate awaiting a response, paused for an outside action, or lost its execution
owner while still non-terminal — then print what it needs. This is the intended
partner of --detach --json: take the runId from the launch ack and wait on it,
instead of polling workflow get in a loop.
The response a gate is waiting for does not have to come from a person. An
orchestrating agent can supply it with workflow respond just as a reviewer can; the
engine only reports that one is owed, and who answers is the waiting host’s business.
For an action-required wait, the output carries attention.kind: "action_required",
the authored message, and the paused node id. Complete the action, then run
archon workflow resume <run-id>; use archon workflow abandon <run-id> if the run
should not continue.
archon workflow wait <run-id>archon workflow wait <run-id> --jsonarchon workflow wait <run-id> --json --timeout 900There is no default timeout. A run reports when it is done, and a wait that ended on
its own clock would be answering a question only the run can answer. --timeout <seconds> is there for a host that needs an upper bound anyway.
Exit codes describe the command, not the run.
| Exit | Meaning |
|---|---|
0 | The run said something — it finished (completed, failed, or cancelled), is waiting for a response, needs an outside action, or lost its execution owner. The status is data on stdout. |
3 | The timeout passed with the run still live. The --json payload carries observedStatus. |
1 | The wait itself failed — unknown run id, database unreachable, or output that could not be delivered. |
A failed or cancelled run is still exit 0: mapping run state onto the process
exit code would make a legitimately cancelled run look like a broken command.
Owner loss is also exit 0: the wait obtained a typed answer, but Archon did not
invent a terminal status or change the run. Its JSON result is owner_lost with the
persisted non-terminal observedStatus and no attention or terminal status field.
After verifying that the run’s work has stopped, release its persisted state with
archon workflow abandon <run-id>.
Live-owner detection is local to the host running workflow wait. With a shared remote
PostgreSQL database, owner_lost means no owner endpoint is reachable on this host; the
run may still be executing on another host. Check the owning host before abandoning it;
abandon prints the host the run recorded and says when it is not this one.
--json emits one document. On a wake it carries the attention value:
{ "ok": true, "action": "wait", "runId": "…", "result": "attention", "attention": { "kind": "terminal", "runId": "…", "status": "completed", "at": "…" } }attention.kind is one of:
terminal— the run finished;statusiscompleted,failed, orcancelled.awaiting_response— a gate is waiting for a decision.respondTonames the run and node where that response is recorded. That run is not always the one you waited on: a parent blocked on aworkflow:sub-run wakes when the chain below it reaches a gate, andrespondTo.runIdis the child you answer. A parent blocked on a child that is merely still running wakes nobody.action_required— the run needs the outside action described bymessage. Once it is complete, resumerunId; use the node id to identify the paused workflow step.unreadable— the run is parked but cannot describe itself (corrupt gate metadata, a gate type this build does not know, a sub-run pointer with no row).detailsays which.
Two pauses deliberately do not wake a waiter, because neither is owed a response: a
gate that has already been approved or rejected and is awaiting auto-resume, and a
wait: node whose timer or event has not fired.
Once the wait is watching, it says so once on stderr — one plain sentence, or the
same envelope with "result": "waiting" and the status it attached on under --json:
{ "ok": true, "action": "wait", "runId": "…", "result": "waiting", "observedStatus": "running" }Until that line the command is completely silent, so a host cannot tell a watch that has begun from one still resolving the id. It is on stderr precisely so stdout keeps carrying exactly one document. A run that already has something to say answers on the first read, and never prints it.
The run id may be the short prefix printed by workflow runs. Once the wait returns,
inspect the run normally with workflow get <run-id>.
workflow resume
Section titled “workflow resume”Resume a failed or paused workflow run. Re-executes the workflow, automatically skipping nodes that completed in the prior run.
archon workflow resume <run-id>archon workflow resume <run-id> --json # validate + ack only; does NOT re-execute inlineIn --json mode the command is a non-blocking control-plane ack: it validates the run is resumable and reports its state but does not re-execute inline (execution streams output to stdout, which would corrupt the JSON). To actually drive a resumable run to completion, use the blocking form or workflow resume <run-id> --detach.
When you already hold a run id, prefer that exact-id form. workflow run <name> --resume --detach selects the newest resumable run of that workflow in the current checkout, which is a different question — from another worktree it correctly finds nothing, and in a checkout with several historical runs it expresses less than the id you already have. Keep the name form for the case you actually mean: “the latest failed run of this workflow, here.”
Adding --detach inverts that: the child is re-invoked without --json, so it takes the inline path and does re-execute the run — just outside your shell. The ack carries continues: true to say so. See Detached control verbs.
workflow cancel
Section titled “workflow cancel”Stop a running workflow. Every cancel surface (this command, /workflow cancel in
chat, the Web UI’s Cancel action, POST /api/workflows/runs/{runId}/cancel, the Slack
Cancel button, and the chat agent’s manage_run tool) does the same thing, decided by
who owns the run:
- The process handling the cancel executes the run (a run the server started, cancelled
from that server): the run is marked
cancelledand its executor stops at its next status check. - Another live process owns it (a
--detachrun or a trigger-started run): cancel contacts that process, terminates its host process tree, confirms termination, and only then records the run ascancelled. - No owner answers, or the owner cannot be stopped: cancel fails and leaves the run
unchanged. When no owner answers, it prints what the run recorded (host, pid, last
activity) and points you at
abandon. A foregroundarchon workflow runor a run executing in another Archon server answers but cannot be stopped from here; interrupt the foreground command, or cancel the run on the server that executes it.
archon workflow cancel <run-id>archon workflow cancel <run-id> --jsonRefusing is deliberate: cancelled releases the run’s worktree lock and resource slot,
and a database transition cannot prove that host work stopped. After verifying that the
owner process is gone, use workflow abandon <run-id>.
A workflow: sub-run normally executes inside its root run’s process, and the root’s row
keeps the worktree lock and resource slot. When no owner answers for the sub-run itself
but one answers for its root, cancel records cancelled and the root’s executor stops the
sub-run at its next status check. When no owner answers for the root either, cancel
refuses and points at abandon, as for any other run. A sub-run resumed on its own (after a
durable wait or a scheduled resume) has its own owner, and cancel stops that owner like
any other run’s. Only a running run can be
cancelled; abandon a paused or failed run instead.
After termination is confirmed, cancel records cancellation through the same run-tree
operation as abandon. Cancelling a parent therefore cancels every non-terminal
descendant and can report the same cascade failures or blocked parent described below.
workflow abandon
Section titled “workflow abandon”Discard a workflow run by marking it cancelled. cancelled releases the run’s
worktree lock and resource slot, so abandon first asks the run’s live-owner endpoint on
this host:
- An owner answers: abandon stops it through the same path as
workflow cancel(proves the owner, terminates its process tree, waits), then recordscancelled. - No owner answers: abandon records
cancelledand prints what the run recorded: the host and pid of the process that last executed it, and its last activity. If that host is not this one, or the owner ran as another user on this host, it says so: abandon can only reach owners on its own host running as its own user. Nothing decides the run is dead from its age or pid. - An owner answers but cannot be stopped: abandon fails with the reason and leaves
the run unchanged. This includes a foreground
archon workflow run(interrupt it in its terminal) and a run executing inside a live Archon server. Cancel a server-executed run from that server (its Web UI Cancel action,POST /api/workflows/runs/{runId}/cancel, or/workflow cancel <run-id>in its chat): the server executes the run, so it cancels it at the executor’s next status check.
archon workflow abandon <run-id>archon workflow abandon <run-id> --json--json adds an owner object: { "outcome": "stopped", "pid": … }, or
{ "outcome": "no_owner_answered", "thisHost", "recordedHost", "recordedPid", "recordedUid", "lastActivityAt" }.
Sub-run trees (#2121 Phase 2): abandoning a parent that spawned workflow: sub-runs cascade-cancels every non-terminal descendant (children and grandchildren; already-terminal runs are left alone). These are database transitions, not process termination; an in-flight host command can continue until it returns. If part of the tree could not be reached, the command reports the count so you know descendants may still be alive. Conversely, abandoning a child that its parent is paused-and-blocked on strands that parent (nothing re-fires the auto-resume hook); the command surfaces the blocked parent’s run id so you can resume it (which fails the sub-run node cleanly) or abandon it too.
workflow approve
Section titled “workflow approve”Approve a paused workflow run at an interactive approval gate. Optionally provide a comment that is available to the workflow via $LOOP_USER_INPUT.
Sub-run child gates (#2121 Phase 2): when a workflow: sub-run pauses at its own gate, the parent run pauses “blocked on child”. Approve (or reject) the child by its own run id — the id shown in the parent’s block message — not the parent’s; the parent auto-resumes when the child completes. A child gate is the exception: it works for a 1:1 sub-run, but a child that pauses inside a fan_out: expansion fails the node instead — a parent has one approval slot and cannot hand it to N children, so gate before or after the fan-out node rather than inside a child of it. approve/reject against the parent’s id while it’s blocked on a child are refused with a redirect to the child id.
Interactive-loop gates — finalize vs iterate: when the gate paused on an iteration where any declared completion channel fired (workflow get <run-id> --json → .metadata.approval.completionSignaled is true), approving with no comment accepts the completion — the node finalizes from the already-computed output on resume, with no re-run. Approving with a comment runs another iteration using it as $LOOP_USER_INPUT. When no completion condition met, both forms run another iteration.
archon workflow approve <run-id>archon workflow approve <run-id> "Looks good, proceed"archon workflow approve <run-id> --comment "Looks good, proceed"archon workflow approve <run-id> --json # record approval + ack; does NOT auto-resume inlineIn human mode approve/reject auto-resume the run inline. In --json mode they record the decision and return an ack without resuming (the run is left resumable for a backgrounded resume/run --resume).
Detached control verbs
Section titled “Detached control verbs”approve, reject, and resume accept --detach. The parent validates the run
read-only with the same preconditions the operation itself enforces, so a
wrong-status, missing-context, child_workflow-blocked, already-resolved, or
no-working-path run is refused synchronously and nothing is spawned. The parent then
hands the whole command to a detached child that owns all state mutation in its own
process group. A shell that dies mid-flight can no longer wedge the run.
The parent also waits out the child’s startup window before acking, so a child that
dies before it starts the run surfaces as an error carrying the tail of its log rather
than as a success you only discover was false minutes later. A run that simply finishes
inside that window — a short workflow, or one that fails on its first node — is acked
normally; its outcome belongs to the run, and workflow get <run-id> reports it.
archon workflow approve <run-id> --detacharchon workflow approve <run-id> --detach --json--detach --json deliberately differs from bare --json. Bare --json records the
decision and withholds the inline auto-resume (you drive continuation separately).
--detach --json spawns a child that takes the ordinary inline path, so the run is
driven onward — approve’s auto-resume, reject’s on_reject rework, resume’s re-run —
just outside your shell. The ack carries continues: true to say so:
{ "ok": true, "runId": "…", "action": "approve", "detached": true, "continues": true, "workflowName": "assist", "logPath": "~/.archon/logs/detached-run-<id>.log"}Read continues to decide whether your automation still owns continuation. logPath
is null when the log file could not be opened — the child still runs, but its output
is discarded, so do not assume a string. This acknowledgement has no transcriptPath;
use its runId with workflow logs <run-id> or workflow get <run-id> to locate the
transcript. Precheck failures follow each verb’s existing error contract:
{ ok: false } under --json, a thrown error otherwise.
workflow reject
Section titled “workflow reject”Reject a paused workflow run at an approval gate. Optionally provide a reason that is available to the workflow via $REJECTION_REASON.
archon workflow reject <run-id>archon workflow reject <run-id> --reason "Needs more tests"archon workflow reject <run-id> --jsonworkflow cleanup
Section titled “workflow cleanup”Delete old terminal workflow run records from the database.
archon workflow cleanup # Default: 7 daysarchon workflow cleanup 30 # Custom thresholdworkflow reset-sessions
Section titled “workflow reset-sessions”Clear persisted per-node AI sessions for a workflow — the cross-run memory stored by
nodes that opt in via persist_session (or workflow-level persist_sessions: true).
Use it when a workflow should forget its prior conversation and start fresh.
archon workflow reset-sessions <workflow-name> --yes # ALL scopes (cross-scope wipe)archon workflow reset-sessions <workflow-name> --scope <key> # one scope onlyarchon workflow reset-sessions <workflow-name> --node <id> --yes # single node, still all scopes → needs --yesarchon workflow reset-sessions <workflow-name> --scope <key> --jsonFlags:
| Flag | Required | Description |
|---|---|---|
--scope | No | Scope key to reset — typically the conversation UUID. Omitting it wipes every scope and requires --yes. |
--node | No | Restrict the reset to a single node id. |
--yes | Only for cross-scope wipes | Confirm a wipe across all scopes (when --scope is omitted). |
--json | No | Machine-readable output ({ success, deleted }). |
Extra positional arguments are rejected rather than silently reinterpreted — use --node <id>
to filter by node, since this is a destructive command.
workflow event emit
Section titled “workflow event emit”Emit a workflow event directly to the database. Primarily used inside workflow loop prompts to record story-level lifecycle events.
archon workflow event emit --run-id <run-id> --type <event-type> [--data <json>]<run-id> accepts either the full ID or an unambiguous prefix from workflow runs.
A prefix resolves only from the originating registered project’s directory, including
its worktrees; use the full ID elsewhere.
Flags:
| Flag | Required | Description |
|---|---|---|
--run-id | Yes | Full workflow run ID, or an unambiguous prefix used from the originating registered project’s directory or one of its worktrees; use the full ID elsewhere |
--type | Yes | Event type (e.g., ralph_story_started, node_completed) |
--data | No | JSON string attached to the event. Invalid JSON prints a warning and is ignored. |
Node-state events, including completion, failure, skip, and resume-cache changes, print
Event persisted only after the database write succeeds. Storage failures exit with code 1.
Other events print Event submitted (best-effort); check server logs if they appear missing.
Exit code: 0 after persistence or best-effort submission, 1 when a required argument is missing, the event type is invalid, run-ID prefix resolution fails, or a node-state write fails.
isolation list
Section titled “isolation list”Show all active worktree environments.
archon isolation listGroups by codebase, shows branch, workflow type, platform, and days since activity.
Includes worktrees created for workflow: sub-run children that declared isolation: worktree
(branch archon/task-<parentRunId8>-<nodeId>-<hash>-child-<n>) — they are tracked and cleaned
up exactly like top-level run worktrees. Avoid cleanup/complete on one while its run tree
is still resumable: a resume reuses the child’s recorded worktree and fails if it has been
removed.
isolation cleanup [days]
Section titled “isolation cleanup [days]”Remove stale environments.
# Default: 7 daysarchon isolation cleanup
# Custom thresholdarchon isolation cleanup 14
# Remove environments with branches merged into the base branch (also deletes remote branches)archon isolation cleanup --merged
# Also remove environments whose PRs were closed without mergingarchon isolation cleanup --merged --include-closedMerge detection uses three signals in order: git branch ancestry (fast-forward / merge commit),
patch equivalence (single-commit squash-merge via git cherry), and GitHub PR state via the
gh CLI. A squash merge of more than one commit is invisible to git here, so PR state is what
recognises it. A merged or closed PR counts only while neither the worktree’s HEAD nor the
local branch has anything past the PR’s head commit: run branch names get reused, and new
commits on a reused branch or a detached HEAD are not covered by the old PR, so that
environment is kept. The same goes for a merge git detects: the worktree’s HEAD must be
merged into the base too, not just the branch. A PR head commit pushed from somewhere
else is fetched from the remote before that comparison; if the fetch fails, the environment is
kept and reported as a failed merge check. The gh CLI is optional — if absent, only git
signals are used. If gh is installed but the lookup fails (auth, rate limit), the environment
is kept and reported as PR state lookup failed. The scheduled
sweep uses the same three signals, so both paths agree on what counts as merged. Each
codebase’s output names the base ref the comparison actually used — the configured
worktree.baseBranch, or the git-detected default branch when that is unset.
Both git signals read the local branch ref. When that ref is gone but the worktree remains,
the PR decides, checked against the worktree’s HEAD; if no PR answers for the branch either, the environment is kept and reported
as merge state unverifiable rather than removed on an unverified guess.
By default, branches with a CLOSED PR are skipped. Pass --include-closed to clean
those up as well. Branches with an OPEN PR are always skipped.
An environment a workflow run can still claim is never removed by either mode,
regardless of age or merge state — the same live-run lock the scheduled sweep applies.
That covers running, pending and paused runs, and also failed runs: a failed run
stays resumable, and removing its environment deletes the local branch that
workflow run --resume and --adopt need. Stale environments with quiet
conversations still pass the activity filter; the live-run lock is what keeps them on
disk until no run can claim them.
validate workflows [name]
Section titled “validate workflows [name]”Validate workflow YAML definitions and their referenced resources (command files, MCP configs, skill directories).
archon validate workflows # Validate all workflowsarchon validate workflows my-workflow # Validate a single workflowarchon validate workflows my-workflow --json # Machine-readable JSON outputChecks: YAML syntax, DAG structure (cycles, dependency refs), command file existence, MCP config files, skill directories, provider compatibility, tier/alias model refs, and supported static environment reads in inline or named exec sources. An unprovided INPUTS_* read is an error when the workflow declares an input contract; other unprovided Python or JavaScript/TypeScript environment reads warn. See Variables for the supported forms and fixes. For bundled and global workflows, validation rejects @custom model aliases because they are not portable across projects; use small, medium, large, or a literal provider model string instead. Returns actionable error messages with “did you mean?” suggestions for typos.
Exit code: 0 = all valid, 1 = errors found.
validate commands [name]
Section titled “validate commands [name]”Validate command files (.md) in .archon/commands/.
archon validate commands # Validate all commandsarchon validate commands my-command # Validate a single commandChecks: file exists, non-empty, valid name.
Exit code: 0 = all valid, 1 = errors found.
Removed: continue <branch> [message]
Section titled “Removed: continue <branch> [message]”archon continue inferred a run from a branch name and injected a prose preamble of git history, pull-request text, and prior artifacts. Continuation that inherits prior work is identified by exact run id instead:
archon workflow runs --open # find the prior run idarchon workflow run archon-ship --adopt <run-id> "resolve the remaining review finding"workflow run <name> --adopt <run-id> reuses a terminal run’s exact worktree or branch, records adopted_from_run_id provenance, and exposes prior artifacts through $ADOPTED_RUN_DIR. The orchestrating agent owns choosing the next workflow and writing the input; see --adopt <run-id>.
complete <branch> [branch2 ...]
Section titled “complete <branch> [branch2 ...]”Remove a branch’s worktree, local branch, and remote branch, and mark its isolation environment as destroyed.
archon complete feature-autharchon complete feature-auth --force # bypass safety checksFlags:
| Flag | Effect |
|---|---|
--force | Skip safety checks |
Use this after a PR is merged and you no longer need the worktree or branches. If GitHub has deleted a squash-merged branch, first prune its remote-tracking ref so the local clone reflects that deletion:
git fetch --prune origin # replace origin with the configured remote when neededarchon complete feature-authCompletion verifies its patches are already on the configured or detected remote default branch before removing it. Accepts multiple branch names in one call.
Start the web UI server in the foreground. The same command works from a binary install and from a source checkout. Only the source of the web UI differs.
Binary installs download a pre-built web UI tarball from the matching GitHub release on first run, verify its SHA-256 checksum, and extract it. Later runs use the cached copy.
Source checkouts serve the web UI you build yourself, at packages/web/dist. Run bun run build:web from the repo root before your first archon serve, and again after frontend changes. Nothing is downloaded, so --download-only is refused, and a missing build stops the command with the build command to run instead of serving an empty page.
# Start web UI server (binary installs download it on first run)archon serve
# Override the default portarchon serve --port 4000
# Download the web UI without starting the server (binary installs only)archon serve --download-onlyFlags:
| Flag | Effect |
|---|---|
--port <port> | Override server port (default: 3090, range: 1–65535) |
--download-only | Download and cache the web UI, then exit without starting the server. Binary installs only |
The downloaded web UI is cached at ~/.archon/web-dist/<version>/. Each version is cached independently, so upgrading the binary automatically downloads the matching web UI.
skill install [path]
Section titled “skill install [path]”Install the bundled archon-cli skill into both .claude/skills/archon-cli/
(Claude Code) and .agents/skills/archon-cli/ (Codex). The command overwrites
existing files so both destinations match the current Archon binary. It also
removes the retired archon and manage-run skill directories.
# Install into the current directoryarchon skill install
# Install into a specific projectarchon skill install /path/to/projectThe unified skill covers workflow execution, run management, setup, configuration,
authoring, and prompt guidance. It is also installed automatically during
archon setup.
version
Section titled “version”Show version, build type, and database info.
archon versionGlobal Options
Section titled “Global Options”| Option | Effect |
|---|---|
--cwd <path> | Override working directory (default: current directory) |
--quiet, -q | Log warnings and errors only (the default for every command except archon serve) |
--verbose, -v | Show debug-level logs on stderr (on stdout for archon serve) |
--json | Output machine-readable JSON (workflow list, status, runs, get, wait, and the write commands approve/reject/abandon/resume). Implies log suppression so stdout is exactly the JSON payload. |
--timeout <seconds> | For workflow wait: give up after N seconds and exit 3. Omitted means wait indefinitely. |
--follow | For workflow logs: wait for the transcript and stream appended rows until the run ends. |
--events | With verbose JSON workflow status/get, return raw event rows instead of ordered node summaries. |
--help, -h | Show help message |
A command’s stdout carries only its output, so archon workflow list --full > out.txt
captures the listing and nothing else. Engine logs go to stderr, and by default only
warnings and errors appear. --verbose (or LOG_LEVEL=debug) adds debug logs, still on
stderr. --json and workflow logs print no logs at all.
archon serve is the exception: its logs are its output, so it logs at info (or debug with --verbose) on stdout,
as the server does when started directly.
Workflow definition problems, such as deprecated or unknown keys, are reported by
archon validate workflows, inline under the workflow in workflow list, and on stderr
before workflow run starts. A file that fails to load is listed with its error by
workflow list and validate workflows, and workflow run names the error. These problems
are logged only at debug, not for every workflow a command happens to discover. An invalid
value for an optional workflow field (for example a malformed tags: block) is dropped with
a warning log, because no other report names it.
Working Directory
Section titled “Working Directory”The CLI determines where to run based on:
--cwdflag (if provided)- Current directory (default)
Running from a subdirectory (e.g., /repo/packages/cli) automatically resolves to the git repository root (e.g., /repo).
When using --branch, workflows run inside the worktree directory.
Commands and workflows are loaded from the working directory at runtime. The CLI reads directly from disk, so it picks up uncommitted changes immediately. This is different from the server (Telegram/Slack/GitHub), which reads from the workspace clone at
~/.archon/workspaces/— that clone only syncs from the remote before worktree creation, so changes must be pushed to take effect there.
Environment
Section titled “Environment”At startup, the CLI strips all Bun-auto-loaded CWD .env keys and nested Claude Code session markers from process.env, then loads two archon-owned env files with override: true. Keys in archon-owned files pass through to AI subprocesses — no allowlist filtering.
On startup, the CLI:
- Strips
<cwd>/.env*keys +CLAUDECODEmarkers fromprocess.env(viastripCwdEnv). Emits[archon] stripped N keys from <cwd> (...)when N > 0. - Loads
~/.archon/.env(user scope). Emits[archon] loaded N keys …when N > 0 andARCHON_VERBOSE_BOOT=1orLOG_LEVEL=debug/traceis set. - Loads
<cwd>/.archon/.env(project scope, overrides user scope). Same verbosity gate as step 2. - Auto-enables global Claude auth if no explicit tokens are set.
<cwd>/.env is never loaded — it belongs to the target project. See Configuration Reference: .env File Locations for the full three-path model.
Database
Section titled “Database”- Without
DATABASE_URL(default): Uses SQLite at~/.archon/archon.db— zero setup, auto-initialized on first run - With
DATABASE_URL: Uses PostgreSQL (optional, for cloud/advanced deployments)
Both work transparently. Most users never need to configure a database.
Examples
Section titled “Examples”# One-off AI chatarchon chat "How does error handling work in this codebase?"
# Interactive setup wizardarchon setup
# Quick question (auto-isolated in archon/task-assist-<timestamp>)archon workflow run assist --cwd ~/projects/my-app "How does error handling work here?"
# Quick question without isolationarchon workflow run assist --cwd ~/projects/my-app --no-worktree "How does error handling work here?"
# Plan a feature (auto-isolated)archon workflow run plan --cwd ~/projects/my-app "Add rate limiting to the API"
# Implement with explicit branch namearchon workflow run implement --cwd ~/projects/my-app --branch feature-rate-limit "Add rate limiting"
# Branch from a specific source branch instead of auto-detected defaultarchon workflow run implement --cwd ~/projects/my-app --branch test-adapters --from feature/extract-adapters "Test adapter changes"
# Approve or reject a paused workflowarchon workflow approve <run-id> "Ship it"archon workflow reject <run-id> --reason "Missing test coverage"
# Check worktrees after work sessionarchon isolation list
# Clean up old worktreesarchon isolation cleanup