Skip to content

feat(core,cli): add ao migrate (V3 inventory + plan, dry-run only)#1717

Draft
harshitsinghbhandari wants to merge 1 commit into
mainfrom
feat/ao-migrate-v3-dry-run
Draft

feat(core,cli): add ao migrate (V3 inventory + plan, dry-run only)#1717
harshitsinghbhandari wants to merge 1 commit into
mainfrom
feat/ao-migrate-v3-dry-run

Conversation

@harshitsinghbhandari
Copy link
Copy Markdown
Collaborator

Summary

Replaces ao migrate-storage with a single ao migrate command. Inventories the AO storage tree, detects identity-system drift accumulated since PR #1466, and prints a step-by-step V3 migration plan plus a structured JSON record.

Execution is intentionally gated in this PR. ao migrate --execute and ao migrate --rollback exit 1 with a feedback notice pointing users at a tracking issue. The intent is to collect real-world dry-run output from a few users before any disk-mutating code lands. Execution unlocks in v0.6.1.

Why now

Hash usage and identity drift are documented in detail at ~/.ao/agent-orchestrator/ao-131/ao-hashes-report.html (forensic report from a real user disk: 8 projects, 153 leaked observability dirs, two competing project-ID formats, four prefix-collision schemes, three live tmux conventions, ~12 dead exports). PR #1466 introduced the V2 storage layout but left every other identifier layer fragmented. This is the missing other half.

What V3 changes (proposed; only --dry-run lands here)

Layer Today V3
projectId V1 bare basename + V2 {name}_{hash10} coexist V2 format applied uniformly; V1 entries re-keyed
Prefix collision 4 allocators (one produces ao2, no dash) One canonical allocator
tmuxName 3 conventions in real metadata Always = sessionId
Orchestrator {prefix}-orchestrator-N numbered Singleton {prefix}-orchestrator; archive on re-spawn
Observability ~/.agent-orchestrator/{hash}-observability/ (leaks) projects/{id}/observability/
Worktrees Some at ~/.worktrees/, some in projects/ All in projects/{id}/worktrees/
Session counter Scan disk + git ls-remote per spawn .next-session-id.json, scan as reconciler

What this PR ships

  • packages/core/src/migration/v3.tsinventoryV3() + planV3() (pure, no FS writes)
  • packages/cli/src/commands/migrate.ts — Commander wiring with gated --execute / --rollback
  • 17 unit tests covering 6 fixture disk states (clean V2, V1+V2 mix, dual-registered, observability leak, stranded worktrees, numbered orchestrators)
  • Removes packages/cli/src/commands/migrate-storage.ts (43 LOC); internal V1→V2 helpers stay in core/src/migration/storage-v2.ts
  • Updates warnAboutLegacyStorage in startup-preflight.ts to point at ao migrate --dry-run
  • Public API additions in @aoagents/ao-core: inventoryV3, planV3, formatBytes + types

What this PR does NOT ship

  • File moves, renames, or config rewrites
  • Marker / progress / rollback files
  • Schema version bumps (still reads V2)
  • Auto-trigger via ao update
  • Identity.json / .next-session-id.json file creation
  • Dead-export deletions

All of the above land in v0.6.1 once feedback from this dry-run is collected.

Sample output (run against a sandbox copy of a real disk)

ao migrate v3 (dry-run · 0.5.0)
Scanned:  /tmp/ao-sandbox-home/.agent-orchestrator
At:       2026-05-07T13:39:03.660Z

Inventory
  Projects:                  8
    V1 bare-basename:        4
    V2 hashed:               4
  Sessions:                  74
  Worktrees:                 0
  Observability dirs:        162 (34.6 MB)
  Stranded worktrees:        0
  Bare hash dirs:            1
  .migrated dirs:            1
  Live tmux sessions:        12
  Same-repo duplicates:      1

Per-project issues
  agent-orchestrator [v1-bare]
    • Project "agent-orchestrator" uses bare-basename layout; would re-key to "agent-orchestrator_eac5b0b202".
    • Session "ao-orchestrator-1" uses a numbered orchestrator suffix; should normalize to "ao-orchestrator".
    • Session JSON has doubled-prefix tmuxName "ao-ao-orchestrator".
    • orchestrator.json has legacy storageKey-prefixed tmuxName "66c66786e971-agent-orchestrator-ao-orchestrator-8".
    • orchestrator.json runtimeHandle.data.workspacePath points at legacy ~/.worktrees/ tree.
  …

Plan (would execute these in order if unlocked)
  1. Re-key V1 bare-basename projects to V2 format (4)
  2. Detect same-repo dual registrations (1)
  4. Write identity.json into each project directory (8)
  5. Write .next-session-id.json per project (8)
  …

Execution is gated in v0.6.0.
Share this plan at: https://github.com/ComposioHQ/agent-orchestrator/issues/new...

Test plan

  • pnpm typecheck — full monorepo, clean
  • pnpm lint — 0 errors (49 pre-existing warnings unrelated to this PR)
  • pnpm --filter @aoagents/ao-core test — 1124 passed, including the new 17 V3 tests
  • pnpm build — full monorepo, clean
  • Smoke test against a sandbox copy of a real ~/.agent-orchestrator (162 obs dirs, 8 projects, mixed V1/V2, numbered orchestrators, same-repo duplicate) — output matches expectations
  • ao migrate --execute exits 1 with feedback notice
  • ao migrate --rollback exits 1 with feedback notice
  • ao migrate-storage rejected with "unknown command" error

Follow-up issues to file (not in this PR)

  • Tracking issue for dry-run output collection (link from the gated message)
  • v0.6.1: unlock --execute after feedback review
  • v0.6.2: auto-trigger via ao update
  • Dead-export deletion PR (manifest is in plan output)

🤖 Generated with Claude Code

Replaces ao migrate-storage with a single ao migrate command that
inventories the AO storage tree and prints a step-by-step V3 migration
plan plus a structured JSON record.

Detects identity-system drift accumulated since PR #1466:
  - V1 bare-basename projectIds vs V2 hashed
  - doubled-prefix tmux names (ao-ao-orchestrator)
  - storageKey-prefixed legacy tmux names in metadata
  - numbered orchestrators (ao-orchestrator-1..N)
  - legacy ~/.worktrees/ paths inside session JSON
  - root-level *-observability dir leak (153 dirs on real disks)
  - stranded ~/.worktrees/ leaves
  - same-repo dual registrations
  - lingering storageKey fields in config.yaml

Execution is gated in v0.6.0: --execute and --rollback exit 1 with a
feedback notice. The intent is to collect real-world dry-run output
before any disk writes land. Execution unlocks in v0.6.1.

The ao start legacy-storage warning now points at ao migrate --dry-run.

Public API in @aoagents/ao-core: inventoryV3, planV3, formatBytes plus
their types.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Test Coverage Report

Metric Value
Lines covered 418/662
Lines not covered 244/662
Overall coverage 63.1%
Per-file breakdown
File Coverage
packages/cli/src/commands/migrate.ts 27/147 (18.4%)
packages/cli/src/lib/startup-preflight.ts 114/183 (62.3%)
packages/cli/src/program.ts 55/56 (98.2%)
packages/core/src/migration/v3.ts 222/276 (80.4%)

Uncovered lines

  • packages/cli/src/commands/migrate.ts: L54-L57, L59-L61, L63-L67, L69, L71-L80, L82, L84-L89, L93-L94, L96-L98, L101-L119, L122-L132, L135-L141, L144-L165, L168-L179, L182-L188, L191-L197
  • packages/cli/src/lib/startup-preflight.ts: L32-L56, L64-L79, L95-L103, L125-L133, L151, L155-L156, L164, L202, L225-L226, L240-L242
  • packages/cli/src/program.ts: L57
  • packages/core/src/migration/v3.ts: L173-L175, L177, L207, L237, L240, L243, L246, L255, L311, L349, L377-L378, L386, L415-L416, L436-L437, L466, L497-L500, L537, L539, L544-L546, L548, L561, L595, L628, L637, L668-L669, L671, L674, L684-L687, L819, L828, L832, L839, L842, L849, L935, L995, L1005, L1028-L1030

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.

1 participant