Skip to main content

Migrating from 0.2.x to 0.3.0

ferrus 0.3.0-alpha.1 replaces the single-project .ferrus/STATE.json file with a SQLite-backed runtime (ferrus.db) and a global project registry under ~/.ferrus/projects/. It also adds multi-task scheduling, goose and opencode as new experimental backends, and a batch /run command. This page covers what changed and how to move an existing 0.2.x project forward.

What changed

0.2.x0.3.0
Runtime state in .ferrus/STATE.json (+ STATE.lock)Runtime state in SQLite (ferrus.db), one row per task
One active task per projectMultiple tasks in flight at once, bounded by max_parallel_tasks
/task runs the loop for the one active task/task queues a task for the scheduler; /run [--limit N] queues a whole batch of ready milestones at once
create_task MCP toolenqueue_task (role-scoped); create_task kept as a compatibility alias on unfiltered servers
/reset resets the single global state to Idle/reset force-resets every resettable task (anything not Complete) and clears its scoped artifacts
Task artifacts directly under .ferrus/ (TASK.md, REVIEW.md, …)Task artifacts scoped per task: .ferrus/tasks/<task-id>.md, .ferrus/runs/<task-id>/*.md
No project registry~/.ferrus/projects/<project-id>/ holds project.toml, ferrus.db, and logs
Backends: Claude Code, Codex, Qwen Code (experimental)Adds goose (experimental, local-model friendly) and opencode (experimental, supervisor/reviewer only)
ferrus.toml [limits] had no dispatch capNew max_executor_dispatches bounds executor respawns per work phase

None of this changes how you drive a task day to day — /task, /spec, /check, /consult, /ask_human all behave the same from the operator's seat. What changed is what's underneath.

Upgrade steps

1. Update the binary

cargo install ferrus --locked
# or re-run install.sh / install.ps1 to pull the latest release

2. Run the migration command

From an existing 0.2.x project directory:

ferrus migrate # alias: ferrus upgrade

This is safe to run on an already-migrated or brand-new project — it's a one-time, idempotent registration step. It:

  • registers the project in ~/.ferrus/projects/<project-id>/ and creates ferrus.db;
  • writes .ferrus/project.toml pointing at the registry entry;
  • creates .ferrus/tasks/ and .ferrus/runs/;
  • if a legacy .ferrus/STATE.json is present, imports its active task (including paused Consultation / AwaitingHuman state and counters) into a SQLite task row, and copies non-empty legacy artifacts (TASK.md, REVIEW.md, SUBMISSION.md, QUESTION.md, ANSWER.md, CONSULT_REQUEST.md, CONSULT_RESPONSE.md) into the new scoped layout;
  • migrates the selected spec/milestone into the project's runtime state;
  • removes the legacy STATE.json and STATE.lock files;
  • normalizes any indexed/duplicate legacy MCP server entries in .claude/mcp-supervisor.json, .claude/mcp-executor.json, .claude/settings.local.json, .qwen/settings.json, and .codex/config.toml.

3. Re-run register if needed

If your project predates role-scoped MCP configs (a single .mcp.json for Claude Code, from before 0.2.6), ferrus migrate won't rewrite that file — re-run registration to regenerate the current per-role config:

ferrus register --supervisor claude-code --executor codex

This is also the moment to opt into goose or opencode if you want to add a local-model backend — see Supported agents.

4. Verify

ferrus doctor

Confirms .ferrus/project.toml, the global registry entry, task/run artifacts, and the ferrus.db schema all agree, and reports any interrupted runs or expired leases. Follow up with:

ferrus recover --dry-run

to preview any pending recovery work without mutating state, then ferrus recover (add --worktrees to also clean up orphaned task worktrees) to apply it.

5. Review ferrus.toml

Nothing in ferrus.toml is removed or renamed, so an existing file keeps working. Two additions are worth setting deliberately for 0.3.0:

[limits]
max_parallel_tasks = 1 # raise this to run tasks concurrently
max_executor_dispatches = 6 # new: bounds executor respawns per work phase

Leaving max_parallel_tasks = 1 reproduces the exact 0.2.x single-task behavior — SQLite-backed instead of file-backed, but otherwise identical.

Rollback

Because ferrus migrate copies rather than destructively rewrites task artifacts (aside from removing the now-unused STATE.json/STATE.lock), downgrading to a 0.2.x binary on an already-migrated project is not supported — the 0.2.x binary doesn't know how to read ferrus.db. Keep a backup of .ferrus/STATE.json before migrating if you need a safety net, or migrate on a branch first.

Multi-task behavior is opt-in

Existing single-task workflows are unaffected: with max_parallel_tasks = 1 (the default), only one task has an executor running at a time, exactly like 0.2.x. Raise max_parallel_tasks and start using /run --limit N once you're ready to have the scheduler work through several ready milestones in parallel — see HQ commands and the state machine.