Skip to content

Migration: v0.3.x → v0.4.0

Status (2026-05-17): Migration complete as of v0.4.2. All 8 standalone plugin repositories — animus-protocol, animus-plugin-template, animus-subject-linear, and the five provider plugins (animus-provider-{claude,codex,gemini,opencode,oai}) — are live under launchapp-dev, each tagged v0.1.0 with green CI. The CLI surfaces below (animus plugin install <owner/repo>, animus plugin new) are live in v0.4.2 of the in-tree CLI.

Animus v0.4.0 is a major release with several breaking changes that ship together intentionally. This guide walks you through every breaking change with concrete before/after examples and the exact commands to run.

TL;DR: rename .ao/.animus/, rename AO_* env vars → ANIMUS_*, update any .mcp.json files referencing ao.task.* (or any ao.*) to animus.*, install plugin providers from their new standalone repositories, and flip pack ids in any custom workflow YAML.

The migration is small in absolute terms because the v0.3.x install base is small. v0.4.0 is the inflection point where Animus commits to itself as an ecosystem rather than a single tool — the rename and the plugin extraction land together because both are foundational.

What's breaking

ChangeImpact
Full rename to animus (no ao.* aliases)Every .mcp.json, env var, config dir, pack id, JSON envelope reference must be updated
Plugin discovery requires --sha256 on --url installsanimus plugin install --url <url> calls without --sha256 are rejected
Plugin discovery $PATH scanning is opt-inIf you relied on $PATH discovery, set ANIMUS_PLUGIN_PATH or pass --include-system-path explicitly
Template registry commit pinninganimus init no longer silently follows main; use --update-registry to fetch latest
Bundled YAML skills moved to animus.core-skills packIf you referenced bundled skills like code-review directly in your overlays, ensure animus.core-skills is in your [[packs]] list
SKILL.md frontmatter: Animus runtime fields now under animus: namespaceCustom skills using tool_policy/extra_args/env at top-level frontmatter must move them under animus:
Subject backend plugin modelNative animus task is one backend among many. Workflows can declare subject_type: to operate over Linear/Jira/etc. Existing workflows without subject_type: keep using the native task backend with no change
In-tree provider crates moved to standalone repositoriesIf you path-depended on crates/ao-provider-*, switch to crates.io published crates from the new standalone repos

1. Rename .ao/.animus/

Per-project. Animus reads from .animus/ exclusively in v0.4.0; the old .ao/ is ignored.

bash
# In each project repo
mv .ao .animus

Verify:

bash
ls .animus/
# Should show: config.json, workflows/, plugins/, ... (whatever was in .ao/)

Commit the rename if your .ao/ was checked in:

bash
git add -A
git commit -m "migrate: .ao/ -> .animus/ for Animus v0.4.0"

2. Rename scoped runtime state

Per-machine. Animus stores per-repo runtime state under ~/.ao/<repo-scope>/ in v0.3.x and ~/.animus/<repo-scope>/ in v0.4.0.

bash
mv ~/.ao ~/.animus

If you have multiple repos and the daemon is running, stop it first:

bash
animus daemon stop  # in each project where it's running
mv ~/.ao ~/.animus
animus daemon start --autonomous  # restart in each project

3. Rename environment variables AO_*ANIMUS_*

Update your shell config:

bash
# zsh
sed -i '' 's/AO_/ANIMUS_/g' ~/.zshrc
# bash
sed -i 's/AO_/ANIMUS_/g' ~/.bashrc

Then reload:

bash
exec $SHELL

The complete list of renamed env vars (51 total):

v0.3.xv0.4.0
AO_CONFIG_DIRANIMUS_CONFIG_DIR
AO_PLUGIN_PATHANIMUS_PLUGIN_PATH
AO_RUNNER_SCOPEANIMUS_RUNNER_SCOPE
AO_RUNNER_CONFIG_DIRANIMUS_RUNNER_CONFIG_DIR
AO_NOTIFY_WEBHOOK_URLANIMUS_NOTIFY_WEBHOOK_URL
AO_NOTIFY_BEARER_TOKENANIMUS_NOTIFY_BEARER_TOKEN
AO_USER_IDANIMUS_USER_ID
AO_ASSIGNEE_USER_IDANIMUS_ASSIGNEE_USER_ID
AO_CLAUDE_BYPASS_PERMISSIONSANIMUS_CLAUDE_BYPASS_PERMISSIONS
AO_SKIP_RUNNER_STARTANIMUS_SKIP_RUNNER_START
AO_WORKFLOW_RUNNER_BINANIMUS_WORKFLOW_RUNNER_BIN
AO_MCP_SCHEMA_DRAFTANIMUS_MCP_SCHEMA_DRAFT
AO_RUNNER_BUILD_IDANIMUS_RUNNER_BUILD_ID
AO_TEST_ENV_CAPTUREANIMUS_TEST_ENV_CAPTURE
AO_TEST_ARGS_CAPTUREANIMUS_TEST_ARGS_CAPTURE
(others — see naming-contract.md for the full list)

Update CI: check .github/workflows/, GitLab CI YAML, Jenkinsfiles, etc. for AO_* references.

4. Update .mcp.json for the renamed MCP tools

Every MCP tool name was renamed ao.<group>.<verb>animus.<group>.<verb>. 58 tools total.

If your .mcp.json looks like:

json
{
  "mcpServers": {
    "ao": {
      "command": "ao",
      "args": ["mcp", "serve"]
    }
  }
}

Update to:

json
{
  "mcpServers": {
    "animus": {
      "command": "animus",
      "args": ["mcp", "serve"]
    }
  }
}

(The CLI binary itself was already animus in v0.3.x; what changes is the server name in the config and the tool namespace your AI client calls.)

If your AI client (Claude Code, Codex, Cursor) hardcodes tool names anywhere, update them. The most common renames:

v0.3.xv0.4.0
ao.task.createanimus.task.create
ao.task.listanimus.task.list
ao.task.statusanimus.task.status
ao.workflow.runanimus.workflow.run
ao.workflow.listanimus.workflow.list
ao.daemon.healthanimus.daemon.health
ao.daemon.startanimus.daemon.start
ao.daemon.eventsanimus.daemon.events
ao.queue.listanimus.queue.list
ao.queue.enqueueanimus.queue.enqueue
ao.plugin.listanimus.plugin.list
ao.plugin.callanimus.plugin.call
ao.requirements.createanimus.requirements.create
ao.memory.getanimus.memory.get
ao.memory.appendanimus.memory.append
... (58 total)...

The pattern: every ao.<group>.<verb> becomes animus.<group>.<verb>. Mechanical sed replace works:

bash
# Update any .mcp.json in your home dir
find ~ -name '.mcp.json' -not -path '*/node_modules/*' -exec sed -i '' 's/"ao\./"animus./g' {} +

5. Update workflow YAML pack ids

In your custom workflow YAML, pack ids ao.task, ao.review, ao.requirement become animus.task, animus.review, animus.requirement. Workflow refs follow the same pattern.

Before:

yaml
[[packs]]
id = "ao.task"
activate = true

workflows:
  - id: standard-workflow
    phases:
      - workflow_ref: ao.task/standard

After:

yaml
[[packs]]
id = "animus.task"
activate = true

workflows:
  - id: standard-workflow
    phases:
      - workflow_ref: animus.task/standard

The well-known workflow refs (each animus.task/*):

v0.3.xv0.4.0
ao.task/standardanimus.task/standard
ao.task/triageanimus.task/triage
ao.task/quick-fixanimus.task/quick-fix
ao.task/refineanimus.task/refine
ao.task/gatedanimus.task/gated
ao.task/ui-uxanimus.task/ui-ux
ao.requirement/plananimus.requirement/plan
ao.review/cycleanimus.review/cycle

Mechanical replace:

bash
# In each project repo, after .ao/ -> .animus/ rename
find .animus -name '*.yaml' -exec sed -i '' \
  -e 's/\bao\.task\b/animus.task/g' \
  -e 's/\bao\.review\b/animus.review/g' \
  -e 's/\bao\.requirement\b/animus.requirement/g' \
  {} +

6. JSON output envelope schema

If you parse --json output, the envelope schema field renamed:

diff
-{ "schema": "ao.cli.v1", ... }
+{ "schema": "animus.cli.v1", ... }

Update any consumers asserting on the schema string.

7. Plugin install: --sha256 is now required for --url

In v0.3.x:

bash
animus plugin install --url https://example.com/plugin/binary  # worked, integrity not verified

In v0.4.0:

bash
# Fails:
animus plugin install --url https://example.com/plugin/binary
# error: --sha256 is required when installing from a URL

# Works:
animus plugin install --url https://example.com/plugin/binary \
  --sha256 a1b2c3d4...  # 64-char lowercase hex SHA-256 of the binary

To compute the SHA-256:

bash
shasum -a 256 /path/to/binary
# or:
curl -sL https://example.com/plugin/binary | shasum -a 256

Plugin install via --path still has optional --sha256. If you provide it, the bytes are verified before install.

8. Plugin discovery no longer scans $PATH

In v0.3.x, animus plugin list and similar commands scanned $PATH for binaries named animus-plugin-* or animus-provider-*. If a malicious binary was on your $PATH, plugin discovery would execute --manifest on it — RCE-shaped.

In v0.4.0, discovery scans only:

  • .animus/plugins/ (project-local)
  • $ANIMUS_PLUGIN_PATH (colon-separated dirs from this env var)

To restore the old $PATH scanning:

bash
# Per command, opt-in:
animus plugin list --include-system-path
animus plugin info --name <name> --include-system-path
animus plugin call --name <name> --method <method> --include-system-path

Or, more typically: move your plugins from random $PATH locations into .animus/plugins/ or set ANIMUS_PLUGIN_PATH=/path/to/plugins.

9. Provider plugins moved to standalone repositories

In v0.3.x the provider crates (Claude, Codex, Gemini, OpenAI-compatible, OpenCode) lived in the Animus core workspace at crates/ao-provider-*/. In v0.4.0 they ship from independent GitHub repositories, each tagged v0.1.0 with green CI:

v0.4.0 standalone repocrates.io
launchapp-dev/animus-provider-claudeanimus-provider-claude
launchapp-dev/animus-provider-codexanimus-provider-codex
launchapp-dev/animus-provider-geminianimus-provider-gemini
launchapp-dev/animus-provider-oaianimus-provider-oai
launchapp-dev/animus-provider-opencodeanimus-provider-opencode

The Animus daemon image bundles them pre-installed. If you build a daemon image from source, you now install providers via animus plugin install rather than building the workspace.

Install sources (v0.4.2)

animus plugin install supports four sources:

bash
# 1. Public GitHub release (resolves latest, downloads, verifies checksum)
animus plugin install launchapp-dev/animus-provider-claude

# 2. Public GitHub release pinned to a tag
animus plugin install launchapp-dev/animus-provider-claude@v0.1.0
# or: animus plugin install launchapp-dev/animus-provider-claude --tag v0.1.0

# 3. Local binary (build it from source, install the binary)
animus plugin install --path ./target/release/animus-provider-claude

# 4. Direct HTTPS URL (--sha256 is required for supply-chain integrity)
animus plugin install \
  --url https://example.com/animus-provider-claude \
  --sha256 a1b2c3d4...

Installed plugins land in ~/.animus/plugins/ and are registered in ~/.animus/plugins.yaml. Override either with --plugin-dir <PATH> or $ANIMUS_PLUGIN_DIR.

To scaffold a brand-new plugin from the template repository:

bash
animus plugin new --kind subject --name jira
# writes ./animus-subject-jira/ as a buildable cargo project

--kind accepts subject, provider, or trigger. --out-dir, --org, --description, --template-version, --template-repo, and --template-path (for offline use) let you customize the scaffold.

If you path-depended on crates/ao-provider-* in your own Rust code, switch to the published crate from crates.io:

diff
-ao-provider-claude = { path = "../animus-cli/crates/ao-provider-claude" }
+animus-provider-claude = "0.1"

Subject backend credentials: use ${VAR} interpolation in workflow YAML

Subject backends (animus-subject-linear, custom backends, etc.) and provider plugins configured through workflow YAML should never embed literal API tokens in .animus/workflows.yaml. Two complementary mechanisms exist in v0.4.x:

  1. Secrets — API tokens, passwords, OAuth keys — are read directly from the daemon's process environment by the plugin that needs them (e.g. animus-subject-linear reads LINEAR_API_TOKEN itself). Set them where you start the daemon.
  2. Non-secret config — team IDs, base URLs, channel allowlists, feature flags — is declared in workflow YAML and interpolated against the daemon's environment at load time.

The workflow YAML loader supports shell-style ${VAR} interpolation in every string scalar, including the :-default and :?error fallback shapes:

yaml
subjects:
  - id: my-linear
    backend: linear
    config:
      team_id: ${LINEAR_TEAM_ID:-default-team-uuid}
      api_url: ${LINEAR_API_URL:-https://api.linear.app/graphql}
      workspace: ${LINEAR_WORKSPACE:?set LINEAR_WORKSPACE for this workflow}

Recommended pattern:

bash
LINEAR_API_TOKEN=lin_api_... \
LINEAR_TEAM_ID=team-uuid \
animus daemon start --autonomous

See docs/reference/configuration.md for full syntax, error semantics, and the recommended secret/non-secret split.

10. Plugin protocol crates moved to animus-protocol repository

In v0.3.x the protocol types lived in crates/orchestrator-plugin-protocol/. In v0.4.0 they live in launchapp-dev/animus-protocol and publish as separate crates:

cratepurpose
animus-plugin-protocolWire types, RPC envelope, error codes, plugin kinds
animus-subject-protocolSubjectBackend trait + Subject schema
animus-provider-protocolProviderBackend trait
animus-plugin-runtimeShared stdio loop helpers (subject_backend_main, provider_main)

If you wrote a custom plugin in v0.3.x, your Cargo.toml changes from path-deps on the core workspace to:

toml
[dependencies]
animus-plugin-protocol = "0.1"
animus-subject-protocol = "0.1"  # if you're a subject backend
animus-plugin-runtime = "0.1"

11. Bundled YAML skills moved to animus.core-skills pack

The 19 skills that previously shipped baked into the animus binary (code-review, implementation, debugging, refactoring, unit-testing, etc.) moved to the animus.core-skills installable pack in early v0.4 work. Current builds no longer auto-install that pack during animus init, and they no longer ship the legacy built-in fallback.

For most users this is invisible — the names resolve the same way. If you wrote a custom pack overlay that referenced a bundled skill name, ensure your pack's pack.toml includes:

toml
[[depends]]
id = "animus.core-skills"

12. SKILL.md frontmatter: vendor namespace animus:

In v0.3.x, custom SKILL.md files could put Animus-specific runtime fields at the top level of the YAML frontmatter:

yaml
---
name: my-skill
description: My custom skill
tool_policy:
  allow: ["task.*"]
extra_args: ["--my-flag"]
mcp_servers: ["my-mcp"]
---

# Skill body...

In v0.4.0 these fields move under an animus: namespace to keep SKILL.md portable across other agent hosts (Claude Code, Codex, Cursor) that don't know about Animus-specific fields:

yaml
---
name: my-skill
description: My custom skill
animus:
  tool_policy:
    allow: ["task.*"]
  extra_args: ["--my-flag"]
  mcp_servers: ["my-mcp"]
---

# Skill body...

Top-level placement of these fields is no longer parsed in v0.4.0 (no deprecation warning — hard cut).

13. Template registry commit pinning

In v0.3.x, animus init cloned the default template registry (launchapp-dev/animus-project-templates) and silently followed main on every subsequent run. If the registry was hijacked or pushed a bad commit, every user got it.

In v0.4.0, the first clone captures the registry HEAD commit hash into ~/.animus/template-registries/<id>/.commit. Subsequent runs verify the local HEAD matches; if they diverge (someone fast-forwarded the cache, the registry rewrote history, etc.) the loader returns a clear error rather than silently falling through.

To re-pin to the latest commit:

bash
animus init --template task-queue --update-registry

The --update-registry flag fetches the latest, captures the new commit hash, and proceeds with init using the new content. Without the flag, init uses whatever's pinned.

14. (Optional) Subjects are now pluggable

The native in-repo subject backend remains available for existing users, but the CLI now routes it through animus subject --kind task instead of a dedicated animus task tree. v0.4.0 also adds the option to plug in external subject sources (Linear, Jira, GitHub Issues, Notion, ...) so workflows can operate over your existing tools without migrating tasks into Animus.

The first reference subject backend, animus-subject-linear, is live (v0.1.0). Install it the same way as a provider:

bash
animus plugin install launchapp-dev/animus-subject-linear

To add a new backend (Jira, Notion, GitHub Issues, Asana, ...), scaffold from the template:

bash
animus plugin new --kind subject --name jira
cd animus-subject-jira
# implement the SubjectBackend trait; cargo build; ship

Once installed, wire it into a workflow:

yaml
# .animus/workflows/custom.yaml
subjects:
  linear-eng:
    plugin: animus-subject-linear
    config:
      team: ENG                                   # non-secret config (or use ${LINEAR_TEAM:-ENG})
    status_map:
      ready: ["Backlog", "Todo"]
      in_progress: ["In Progress"]
      done: ["Done"]

workflows:
  - id: linear-impl
    subject_type: linear-eng    # NEW: declares which backend this workflow operates over
    phases: [...]

Subject backend credentials

Each subject backend plugin reads its credentials directly from the daemon's process environment — they are not declared in workflow YAML. For animus-subject-linear, export LINEAR_API_TOKEN before starting the daemon. For any other plugin, see its plugin.toml [[env]] declarations or its README.

bash
LINEAR_API_TOKEN=lin_api_... animus daemon start --autonomous

${VAR} interpolation in workflow YAML is intended for non-secret, per-environment config (team IDs, base URLs, feature flags, channel allowlists). See docs/reference/configuration.md for the secrets vs. non-secret config distinction.

See the protocol design doc for the full SubjectBackend trait and JSON-RPC method set.

Operational concerns: plugin kill-switches

The v0.4.x plugin surfaces (provider plugins and trigger backend plugins) ship with two environment-variable kill-switches you can flip when an installed plugin is wedging the daemon. Use them as last-resort escape hatches while you investigate — neither one is meant for long-term operation.

  • ANIMUS_DAEMON_DISABLE_TRIGGERS=1 — Skips the trigger plugin supervisor on daemon start and interrupts any in-progress restart backoff sleeps so the daemon stops respawning a flapping plugin immediately. Useful when a trigger plugin is panicking, flooding events, or blocking startup. Daemon restart needed to re-enable.
  • ANIMUS_PROVIDER_DISABLE_PLUGIN=1Removed in v0.4.12. Previously forced SessionBackendResolver to fall back to in-tree provider backends; those backends were extracted to standalone plugins, so the env var has nothing to do and is silently ignored. To quarantine a single provider plugin, uninstall it with animus plugin uninstall --name <name> or move its binary out of discovery, then restart the daemon.

When a trigger plugin exhausts its restart budget or a provider plugin fails its handshake, the log message now points at the relevant env var so operators don't have to read the source during an incident. See docs/reference/configuration.md for the full description.

Quick smoke test after migration

Run these to verify the migration worked:

bash
animus --version                          # should show v0.4.x
animus daemon health                      # responds successfully
animus subject list --kind task           # reads from .animus/, not .ao/
animus mcp serve --help                   # shows MCP serve help
animus plugin list                        # shows installed plugins (none scanned from $PATH)
animus init --help                        # shows --update-registry flag

If animus subject list --kind task returns no tasks but you had tasks in v0.3.x, double-check the .ao/.animus/ rename completed. The daemon does not auto-migrate.

Where to ask for help

Released under the Elastic License 2.0 (ELv2).