Skip to content

feat(cli): ao migrate — offline legacy→rewrite SQLite migration (#2129)#2144

Closed
harshitsinghbhandari wants to merge 5 commits into
AgentWrapper:mainfrom
harshitsinghbhandari:feat/ao-migrate-2129
Closed

feat(cli): ao migrate — offline legacy→rewrite SQLite migration (#2129)#2144
harshitsinghbhandari wants to merge 5 commits into
AgentWrapper:mainfrom
harshitsinghbhandari:feat/ao-migrate-2129

Conversation

@harshitsinghbhandari

Copy link
Copy Markdown
Collaborator

What & why

Implements ao migrate (#2129): a single offline command (run with the rewrite daemon stopped) that ports the legacy flat-file state into the rewrite's SQLite store so users keep their projects and resume their orchestrator after the cutover.

It:

  1. inserts the legacy project registry + per-project settings into the rewrite's ~/.ao/data/ao.db,
  2. inserts each project's single non-terminated orchestrator session (workers respawn fresh; intentionally not migrated),
  3. relocates the orchestrator's Claude transcript so it resumes with context,

creating the DB from a vendored copy of the rewrite schema when it does not exist. Idempotent: a re-run reports everything already-present and writes nothing new.

Build order (all steps completed)

  1. Vendored goose migrations + migrate-db.ts. Copied ReverbCode 0001–0012.sql (pinned @ 43ae7eb) into packages/cli/src/lib/migrations/. Built a minimal goose runner (StatementBegin/End spans, per-file transactions, NO TRANSACTION handling for 0007), stamped goose_db_version rows 1..12 in the exact v3.27.1 format, implemented the >= vendored precondition guard (absent→create, locked→refuse, older→refuse, present≥12→insert, better-sqlite3 missing→refuse), and FK-ordered ON CONFLICT DO NOTHING inserts. Added better-sqlite3 to the CLI's optionalDependencies and a build step copying migrations into dist/.
    • TDD: the integration test was written first (red→green). It asserts MAX(version_id) == 12 and that a cursor-harness insert succeeds — the single highest-value assertion, proving 0007 ran and writable_schema = RESET took effect on better-sqlite3's bundled SQLite. (Implementation note: better-sqlite3 blocks sqlite_master writes in safe mode, so 0007 runs under db.unsafeMode(true).)
  2. Pure project mappers (migrate.ts) recovered from the closed draft feat(cli): add ao migrate to port legacy projects + settings into the rewrite daemon #2127, HTTP/daemon half dropped; added buildProjectRow for the server-side fields migrate now computes itself (repo_origin_url, registered_at, kind, display_name, config).
  3. Orchestrator mapper (migrate-orchestrator.ts). One row per project, verbatim id {prefix}-orchestrator, num = 0, the 8→5 activity-state map, per-harness agent_session_id, terminal/aider skip filters, and double-decoded lifecycle/statePayload.
  4. Transcript relocation (migrate-claude.ts). claude-code orchestrators only; source slug realpath-resolved, destination slug from the literal orchestrator-worktree template.
  5. Command (commands/migrate.ts) with --dry-run and --json, the locked exit-code + summary contract, registered in program.ts next to registerMigrateStorage. Added the @aoagents/ao-cli minor changeset.

Tests / gate

  • New tests: goose runner + cursor-insert + full precondition matrix + checksum drift guard (43ae7eb); project mappers; orchestrator mapper (state map, harness selection, filters, double-decode, prefix fallback); transcript relocation; command-level dry-run / real-run / idempotency / skip-accounting / schema-too-old refusal.
  • Gate: pnpm build && pnpm typecheck && pnpm lint all green. CLI test suite green (the one failing test in a full pnpm test, path-equality.test.ts > expands ~ to HOME, fails identically on a clean tree — a macOS /tmp/private realpath env issue, unrelated to this change; tracker-linear failures are the missing @composio/core SDK in this environment).

⚠️ External dependency to confirm before freezing the pin

The vendored schema pin (43ae7eb / VENDORED_SCHEMA_VERSION = 12) is built as-is but is awaiting the rewrite side's freeze confirmation (spec §15):

  • is 0.10.0's schema >= v12,
  • no NOT-NULL-no-default column added to projects/sessions pre-flip,
  • the Claude transcript path/format in §9 is what 0.10.0 actually reads.

Re-vendor + bump VENDORED_SCHEMA_VERSION only on the freeze owner's signal. The checksum test pins the vendored SQL so it cannot drift silently, and the >= vendored guard refuses anything older.

Closes #2129.

🤖 Generated with Claude Code

harshitsinghbhandari and others added 5 commits June 18, 2026 00:49
Vendored SQLite migrations from aoagents/ReverbCode pinned at commit
43ae7eb for the offline `ao migrate` DB writer (AgentWrapper#2129).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Goose-compatible runner (StatementBegin/End spans, NO-TRANSACTION + 0007
writable_schema via better-sqlite3 unsafeMode), v3 goose_db_version stamping,
the >= vendored precondition guard (absent/locked/older/missing), and
FK-ordered ON CONFLICT DO NOTHING inserts. Adds better-sqlite3 to the CLI's
optionalDependencies for deterministic createRequire resolution and copies the
vendored migrations into dist on build.

Integration tests assert MAX(version_id)==12 and a cursor-harness insert (proves
0007 + writable_schema=RESET took effect) plus the full precondition matrix and a
checksum drift guard pinned to 43ae7eb. Refs AgentWrapper#2129.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
migrate.ts: recovered pure project mappers from the closed draft AgentWrapper#2127
(buildProjectPlan/buildRewriteConfig/mapPermission/mapHarness/isValidRewriteProjectId),
dropping the loopback-REST half now that migrate writes SQLite directly.

migrate-orchestrator.ts: maps a project's single non-terminated orchestrator
record to one sessions row (verbatim {prefix}-orchestrator, num=0), with the
8->5 activity-state map, per-harness agent_session_id selection, the
terminal/aider skip filters, double-decoded lifecycle/statePayload, and
claude-code transcript relocation inputs. Refs AgentWrapper#2129.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
migrate-claude.ts: compute source (realpath-resolved worktree) + destination
(literal orchestrator-worktree template) Claude project slugs via the
claude-code plugin's toClaudeProjectPath, and copy the transcript jsonl.
Idempotent (dest exists -> already-present; source missing -> skipped). Refs AgentWrapper#2129.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire the offline migrator end-to-end: resolve dataDir/dbPath (§4), build the
project rows (git origin, registered_at, display_name, config blob) + read each
project's orchestrator + plan transcript copies, then either preview (--dry-run,
no writes) or run preconditions -> relocate transcripts -> FK-ordered inserts.
Emits the locked summary (human or --json) and the exit-code contract (non-zero
on refusal or any failed row). Registers next to migrate-storage in program.ts,
records start/finish activity events, and adds the ao-cli minor changeset. Refs AgentWrapper#2129.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(cli): unified OFFLINE ao migrate — port legacy projects + sessions into the rewrite (direct-DB), with Claude transcript relocation

1 participant