v0.9.0

coleam00/Archonv0.9.0Aug 17, 2026by Wirasm

AI Summary

A significant update introducing workflow composition capabilities, dry-run simulation, and packaged workflows, alongside breaking changes to node capabilities.

Key Highlights

  • Workflows can now declare inputs, returns, and be simulated end-to-end without contacting providers.
  • Packaged workflows allow a self-contained folder structure for workflows and their dependencies.
  • Introduction of `archon workflow run --dry-run` to simulate workflow execution without spending resources.
  • Breaking changes regarding ambient skills and MCP servers for Claude and Codex nodes.

Breaking Changes

  • Claude workflow nodes no longer inherit ambient skills and MCP servers; they must be explicitly declared.
  • Included command bodies must now resolve and may only reference their own workflow.

New Features

  • Workflow signatures with `inputs:`, `returns:`, and `with:` support for sub-runs.
  • Dry-run simulation for workflows.
  • Packaged workflows in `.archon/workflows/<pack>/<workflow>/`.
  • Codex workflow nodes no longer advertise ambient skills automatically.
  • Workflow-level `modelReasoningEffort:` and `webSearchMode:` options now properly reach Codex.
  • Agent instructions consolidated into `AGENTS.md`.

Full Release Notes

Workflows become properly composable: a workflow can now declare the arguments it takes and the result it returns, ship as one self-contained folder, and be simulated end to end without contacting a provider. Alongside that, a workflow node's capabilities become what its YAML declares rather than whatever happens to be configured on the operator's machine — see Breaking before upgrading.

### Breaking

- **Claude workflow nodes no longer inherit ambient skills and MCP servers.** A Claude node now sees exactly the skills and MCP servers its YAML declares, plus the native tools Archon injects for the run. User, project, and plugin MCP configuration on the operator's machine no longer reaches workflow nodes: a node that relied on an ambiently configured server must declare it with `mcp:`, and one that relied on an ambient skill must list it under `skills:`. Declared skills are now also checked before spend — a skill that is installed but unreachable from the node's enabled `settingSources` fails up front instead of silently going missing, while built-in and plugin-qualified (`plugin:skill`) names warn and are left for the SDK to resolve. Direct Claude chat, `CLAUDE.md`, and built-in, filesystem, and inline agents are unchanged. (#2535)
- **An included command body must resolve, and may only reference its own workflow.** `command:` and `loop.command` files inside an `include:` block are now resolved, validated, namespaced, and input-bound during load-time composition, recursing through nested `loop_group` bodies. Two previously tolerated shapes now fail: a body that references a node id outside the included workflow (it could bind whatever the caller happened to have) is rejected, and a body that cannot be resolved or read fails a fresh execution instead of warning and being skipped. Declare an input and pass it with `with:` instead of reaching outward. A paused loop still resumes from its persisted prompt snapshot even if its command file is later deleted. Included command bodies gained the other half of this trade: they can now use `$INPUTS.<name>`, which Phase 1 rejected outright. (#2534, #2532)

### Added

- **Workflow signatures — `inputs:`, `returns:`, and `with:` on sub-runs.** A workflow can declare the named arguments it accepts and which node's output is its result, and a `workflow:` sub-run node can bind those arguments by name with `with:` instead of passing a single opaque `input:` string. Values arrive as `$INPUTS.<name>` in prompts and command bodies, and as `INPUTS_<UPPER_SNAKE>` in `bash:`/`script:` node environments. Missing required inputs and undeclared arguments are rejected before any worktree, clone, or provider cost, and `returns:` gives a stable author-chosen result channel in place of guessing the terminal sink. Workflows without `inputs:` keep their existing passthrough behavior byte for byte. (#2523)
- **`archon workflow run <name> --dry-run` simulates a workflow without spending anything.** An in-memory simulator walks the DAG — routing, `when:` conditions, strict `$node.output` references, gates, and loops — and prints a deterministic human or `--json` trace. No provider is contacted and nothing is persisted: no run row, events, sessions, or worktrees. Node outputs can be stubbed, approval behavior chosen, and code nodes actually executed with `--exec-code` (they are stubbed by default). (#2530)
- **Packaged workflows — one folder holds a workflow and everything it uses.** `.archon/workflows/<pack>/<workflow>/` is discovered as a package in both repo and home scope, and bare `command:` / `script:` references in that workflow's YAML bind to its own `commands/` and `scripts/` directories instead of the shared trees. Copying a workflow to another repo is now copying one folder rather than a scavenger hunt across three. Packaged resources are embedded in binary builds, and the Web API's workflow GET/PUT/DELETE lifecycle preserves the layout. Flat and one-level grouped YAML layouts are unaffected. (#2528)

### Changed

- **Codex workflow nodes no longer advertise ambient skills automatically.** Workflow commands and prompts now invoke installed Codex skills explicitly with `$skill-name`, an empty `skills: []` declaration is valid, and unsupported YAML fields warn honestly instead of implying support. Direct Codex chat is unchanged. This is a behavioral guard rather than filesystem isolation, and Codex MCP configuration remains additive to ambient servers. (#2498, #2533)
- **A malformed `settingSources` entry can only narrow Claude's scope, never widen it.** An unrecognized entry previously left the key unset, falling through to the permissive `['project', 'user']` default — so `settingSources: [projct]`, written to lock a node to project scope, silently granted full user-scope ambient access. Unrecognized entries are now dropped and logged, and workflow validation shares one parser with execution so the two can no longer disagree about a node's effective sources. (#2535)
- **Agent instructions live in one file.** `AGENTS.md` — which Codex, Pi, and OpenCode read natively — had drifted about six months behind `CLAUDE.md`. It is now the canonical instruction file, with `CLAUDE.md` reduced to a one-line pointer that Claude Code resolves as an import. No rule changed in the move. (#2497)

### Fixed

- **A schema upgrade can no longer crash-loop on an index that predates its column.** An index or `COMMENT ON COLUMN` placed beside its table body names a column that does not exist yet on an upgrade, because `CREATE TABLE IF NOT EXISTS` is a no-op there and the column only arrives in the later additive `ALTER TABLE` block. Since the schema applies in one transaction and re-throws, a single such statement aborted the whole apply and crash-looped every boot — on Postgres this broke startup and every CLI invocation for installs upgrading past #2414, and on SQLite it meant `new SqliteAdapter(...)` simply could not open a database created before `parent_conversation_id` existed. Every index and column comment now sits below the additive block. A new `check:schema-upgrades` CI job proves the current schema applies cleanly on top of every schema vintage ever released, re-applies idempotently, and lands on exactly the fresh-install shape. (#2513, #2552)
- **Workflow-level `modelReasoningEffort:` and `webSearchMode:` reach Codex.** Both had been schema-declared, loader-validated, and documented as working workflow-level options since March, but the executor never read them off the workflow definition — a workflow declaring either parsed cleanly, warned about nothing, and ran at the `config.yaml` or SDK default. Declaring either on a non-Codex node now produces the same loud capability-mismatch warning every other unsupported field produces. (#2559)
- **The container write-back no longer mis-decodes hostile or merely unusual filenames.** The two overlay walk scripts — the one place a container run writes the live root — decoded each entry by forking `basename`, `dirname`, and `sed`, which produced three reachable defects: a whiteout marker under a `-`-prefixed directory was planted as a literal `.wh.*` file instead of deleting its target (no adversary required, just a leading dash); a whiteout whose name ended in a newline deleted a different, innocent file and could write outside the live root while reporting success; and a TAB inside a filename or symlink target forged the positionally-decoded fields after it, presenting an escaping symlink to the approval gate as safe. Shell parameter expansion replaces the forks, closing all three and cutting forks per entry from 8 to 4 for a regular file and 6 to 1 for a whiteout. (#2561)
- **Archon no longer guesses the default branch.** `getDefaultBranch` ended its fallback chain by checking whether `<remote>/main` merely existed and returning it if so. On a repository shaped like this one — `dev` is the default and `main` is the release branch — a run silently cut its worktree from `main` and targeted its pull request at `main`, with nothing erroring at the moment it happened. When `<remote>/HEAD` is not a symbolic ref it now throws, naming `--base`, `worktree.baseBranch`, and the codebase `default_branch` field. (#2503)
- **`WORKFLOW_ID` is delivered to `bash:` and `script:` node subprocesses.** It substituted into the node body but was missing from the environment bag, unlike `ARTIFACTS_DIR`, `STATE_DIR`, and `LOG_DIR` sitting beside it — so a heredoc'd `python3` or `node` block reading `os.environ` failed inside the nested interpreter with a bare `KeyError` and no hint that its siblings worked fine. The `e2e-deterministic` fixture that demonstrated the wrong way to consume an output reference was corrected too. (#2511)
- **A dry run of the state migration no longer creates a database.** `scripts/migrate-state-dir.ts` consulted the codebase registry unconditionally, and the SQLite adapter connects lazily and applies the full schema on first use — so a read-only run wrote a 704 KB database while printing `Dry run — nothing was moved`. (#2557)


---

Discussion thread — questions, or paste YAML that broke: https://github.com/coleam00/Archon/discussions/2574