Migration Guide: Legacy → Projects v0
This guide explains how to migrate a legacy single-project AOF vault to the new Projects v0 layout.
Overview
AOF now supports multi-project workflows through the Projects v0 layout. This migration tool helps you safely transition from the legacy single-project structure to the new multi-project structure.
Layout Changes
Legacy layout:
<vault-root>/ tasks/ backlog/ ready/ ... events/ views/ state/Projects v0 layout:
<vault-root>/ Projects/ _inbox/ project.yaml tasks/ artifacts/ state/ views/ cold/ events/ (if present in legacy) tasks.backup-<timestamp>/ (backup of legacy layout)Migration Command
aof migrate-to-projects [--dry-run]What it does
For fresh installs (no legacy directories present):
- Creates
Projects/_inbox/with required structure andproject.yaml - Exits early (nothing to migrate)
For existing vaults with legacy layout:
- Creates backup: Moves legacy directories (
tasks/,events/,views/,state/) into a timestamped backup directory. - Creates Projects/_inbox/: Bootstraps the new project structure with all required directories.
- Migrates tasks: Copies entire
tasks/directory from backup intoProjects/_inbox/tasks/, preserving all subdirectories, companion dirs, and non-markdown files. - Updates frontmatter: Adds
project: "_inbox"to top-level task cards only (tasks/<status>/*.md). Companion directories, nested files, and non-markdown files are preserved unchanged. - Migrates other data: Copies
events/,views/,state/if they existed.
Options
--dry-run: Report planned actions without making changes. Use this first to see what will happen.
Example
# Preview the migrationaof migrate-to-projects --dry-run
# Perform the migrationaof migrate-to-projectsOutput
🔄 Migrating to Projects v0 layout...
✅ Migration complete!
Backup: /path/to/vault/tasks.backup-2026-01-15T12-00-00-000Z Migrated directories: tasks, events, views, state Updated tasks: 42
💡 Next steps: 1. Verify migrated tasks in Projects/_inbox/tasks/ 2. Test your workflows with the new layout 3. If needed, rollback with: aof rollback-migrationNote: The “Updated tasks” count reflects only top-level task cards (.md files in status directories). Companion directories, nested files, and non-markdown files are preserved but not counted.
Rollback Command
aof rollback-migration [--dry-run] [--backup <dir>]What it does
- Finds backup: Uses the latest
tasks.backup-*directory unless you specify one. - Renames _inbox: Moves
Projects/_inbox/to_inbox.rollback-<timestamp>to avoid conflicts. - Restores legacy layout: Moves directories from backup back to vault root.
Options
--dry-run: Report planned actions without making changes.--backup <dir>: Specify an explicit backup directory to restore from (relative to vault root).
Example
# Rollback using the latest backupaof rollback-migration
# Rollback from a specific backupaof rollback-migration --backup tasks.backup-2026-01-15T12-00-00-000Z
# Preview rollbackaof rollback-migration --dry-runOutput
🔙 Rolling back migration...
✅ Rollback complete!
Restored directories: tasks, events, views, state
⚠️ Warnings: • Renamed _inbox to _inbox.rollback-2026-01-15T14-30-00-000Z
💡 Next steps: 1. Verify legacy tasks/ directory restored 2. Resume normal operations with legacy layoutIdempotency
- Re-running migration after success is safe. If legacy directories are already gone and
_inboxexists, it reports “Already migrated” and does nothing. - Task frontmatter updates: Only processes task cards that don’t already have
project: "_inbox"in their frontmatter (skips already-migrated tasks).
Task Frontmatter
The migration adds project: "_inbox" to all task frontmatter. It does not validate the full schema, so legacy tasks with missing fields (e.g., old tasks without createdAt) will still migrate successfully.
Hierarchical Projects (Optional)
Projects v0 now supports optional hierarchical organization via the parentId field in project.yaml. This is purely organizational and does not affect task inheritance or routing:
id: my-subprojecttitle: My SubprojectparentId: parent-project-id...Key points:
parentIdis optional and can be added to any project manifest at any time- The linter will warn if
parentIdreferences a non-existent project - The linter will error on circular parent references (e.g., A→B→A)
- Child projects do not inherit any behavior from parents; this is purely for organization
- Create child projects using:
aof create-project <id> --parent <parentId>
Backup Location
Backups are created at <vault-root>/tasks.backup-<timestamp> where the timestamp is ISO-8601 format with colons/dots replaced by hyphens for filesystem safety.
Example:
tasks.backup-2026-01-15T12-00-00-000ZTroubleshooting
Migration fails midway
If the migration process is interrupted, you can safely re-run it. The tool is designed to be idempotent.
Rollback doesn’t find a backup
If you get “No backup directory found”, check:
- Are there any
tasks.backup-*directories in your vault root? - If not, you may need to restore from your own backups.
Tasks missing after migration
Check:
- The backup directory:
ls tasks.backup-*/tasks/ - The migrated location:
ls Projects/_inbox/tasks/ - If files are in the backup but not migrated, re-run migration (it will copy missing files).
Need to migrate to a different project (not _inbox)
The current tooling only supports migration to _inbox. For multi-project splits, you’ll need to manually organize tasks after migration using the aof project commands (to be implemented in TASK-001).
Related Commands
aof project create(upcoming): Create new projects beyond_inbox.aof project lint: Validate project structure and manifests.aof lint: Validate tasks within a project.
Gate to DAG Migration (v1.1 to v1.2)
AOF v1.2 replaces linear gate-based workflows with DAG-based hops. This section explains what changed and what you need to do.
What changed
In v1.1, workflows used sequential gates — linear checkpoints where specific roles reviewed or approved work before advancing. In v1.2, workflows use DAG hops — a directed acyclic graph where each hop can have multiple predecessors, conditions, rejection strategies, and parallel fan-out/join patterns.
Key differences:
- Gates were strictly linear (implement -> review -> qa -> done)
- DAG hops support branching, parallelism, and conditional activation
WorkflowConfig(gates) is replaced byWorkflowDefinition(hops)project.yamlusesworkflowTemplatesinstead ofworkflow
Automatic migration
Existing tasks with gate-based workflows are automatically migrated on load. AOF performs lazy migration when a gate task is read from disk:
- Gate sequences are converted to linear DAG hops with
dependsOnchains canReject: truegates become hops withrejectionStrategy: originwhenexpressions are converted to condition DSL where possible (unparseable expressions are skipped with a warning)
No manual intervention is required for existing tasks.
What you need to do
- Existing tasks: Nothing. Auto-migration handles them transparently.
- New tasks: Use DAG format. Define workflows with
hopsanddependsOn, notgates. - Project configuration: Replace
workflowwithworkflowTemplatesinproject.yaml. Use the--workflowCLI flag to apply templates when creating tasks. - Custom gate definitions: Rewrite as DAG workflow templates. See Workflow DAGs for patterns and examples.
Gate source code
Gate evaluation code (src/dispatch/gate-*.ts, src/schemas/gate.ts, src/schemas/workflow.ts) is kept but deprecated. It serves as a safety net during the migration period and will be removed in v1.3.
See Also
- Task Format — Task file structure and frontmatter schema
- Workflow DAGs — Full DAG workflow documentation