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.x | 0.3.0 |
|---|---|
Runtime state in .ferrus/STATE.json (+ STATE.lock) | Runtime state in SQLite (ferrus.db), one row per task |
| One active task per project | Multiple 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 tool | enqueue_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 cap | New 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 createsferrus.db; - writes
.ferrus/project.tomlpointing at the registry entry; - creates
.ferrus/tasks/and.ferrus/runs/; - if a legacy
.ferrus/STATE.jsonis present, imports its active task (including pausedConsultation/AwaitingHumanstate 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.jsonandSTATE.lockfiles; - 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.