agent-orchestrator/backend/internal/cli
Harshit Singh Bhandari 5fff98087a
feat(import): rewrite-side legacy → rewrite first-boot import (#314)
* feat(import): rewrite-side legacy → rewrite first-boot import

Port the legacy-side TS reader (AgentWrapper #2144/#2129) to Go and run the
migration inside the rewrite as an opt-in import, per the FINAL v2 plan. Reads
the legacy flat-file store (~/.agent-orchestrator) read-only and writes the
rewrite's own SQLite DB via the native storage layer; legacy files are never
touched, and a re-run skips existing rows, so a declined or failed import loses
nothing.

What's included:
- internal/legacyimport: Go reader + field mappers (issue #247). Lifecycle
  double-decode (lifecycle key or statePayload+stateVersion:"2"),
  role/orchestrator detection, sessionPrefix fallback (first 12 chars of id),
  8→4 activity-state map, per-harness resume-id selection, permission/harness
  remap, and the claude transcript slug + relocation to the rewrite's
  orchestrator worktree path ({DataDir}/worktrees/{id}/orchestrator/{prefix}-orchestrator).
- store.ImportSession: verbatim session insert (explicit id/num, ON CONFLICT
  DO NOTHING) so the orchestrator lands at id "{prefix}-orchestrator", num 0.
- `ao import`: explicit, idempotent import with --from/--dry-run/--yes/--json.
  Refuses while a live daemon owns the run-file (the daemon is sole writer; the
  import runs offline, matching the #2129 reference).
- First-boot opt-in: `ao start` offers the import before launching the daemon
  when legacy data is present and the rewrite DB has no projects yet. Declining
  or any failure is non-fatal; a non-interactive boot prints a hint instead of
  auto-importing.

Scope (gist §6): all projects + per-project settings, and the single
non-terminated orchestrator session per project (claude-code/codex/opencode;
aider skipped with a note). Workers are not imported (they respawn fresh).

Resume-id mapping (#247 §2.2): agent_session_id carries claudeSessionUuid /
codexThreadId / opencodeSessionId by harness. codexModel and
restoreFallbackReason have no rewrite column, so they are dropped and surfaced
as import notes — codex resumes from the thread id alone, the rest is forensic.

Gate: `go build ./... && go test -race ./...` green (1423 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(import): resolve golangci-lint errcheck/gocritic/nilerr findings

- start.go: check fmt.Fprint* returns in the first-boot import path
- project.go: combine same-typed return params (gocritic paramTypeCombine)
- claude.go: use a pathExists helper so a missing transcript source is a normal
  skip, not an err-then-return-nil (nilerr)
- importer.go: fold best-effort transcript relocation into a switch so the
  non-fatal path no longer returns nil from an error branch (nilerr)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(import): resolve transcript dest path like the daemon; harden lifecycle parse

Code-review follow-ups on the legacy importer:

- claude.go: compute the Claude transcript DESTINATION slug from the
  symlink-resolved orchestrator worktree path (new resolvePhysical, mirroring
  gitworktree.physicalAbs), not the literal path. The daemon resolves that cwd
  through physicalAbs before `claude --resume` runs, so a literal-path slug
  missed the resume bucket whenever any component of AO_DATA_DIR was a symlink
  (custom data dir, macOS /tmp→/private/tmp, symlinked $HOME) — the orchestrator
  would have resumed without its prior context. Source slug now uses the same
  resolver for symmetry.
- orchestrator.go: accept a numeric stateVersion (JSON 2 → float64) as well as
  the string "2" when falling back to statePayload, so a V2 record carried only
  in statePayload is not misparsed as stateless.
- orchestrator.go: build the dropped-resume-metadata note as a joined list
  instead of string concatenation.

Tests: added a symlinked-data-dir dest-slug test and a numeric-stateVersion
fallback test. Gate green: `go build ./... && go test -race ./...` (1425).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 19:54:51 +05:30
..
client.go feat(config): persist per-project agent config and resolve it at spawn (#154) 2026-06-08 21:35:29 +05:30
client_test.go Add `ao spawn` + `ao project add` (spawn a real worker end-to-end) (#77) 2026-06-02 18:39:13 +05:30
completion.go fix(cli): harden daemon control surface and stop CLI from writing the store 2026-06-01 01:55:14 +05:30
doctor.go fix(codex): reliable activity signals — session-flag hooks, trust bypass, no_signal watchdog (#170) 2026-06-11 10:36:45 +05:30
doctor_test.go fix(codex): reliable activity signals — session-flag hooks, trust bypass, no_signal watchdog (#170) 2026-06-11 10:36:45 +05:30
dto_drift_e2e_test.go fix(sessions): stop AO hook files from making every worktree permanently dirty (#169) 2026-06-11 11:06:45 +05:30
e2e_test.go feat(cli): enrich ao doctor (#90) (#99) 2026-06-03 16:50:54 +05:30
exitcode_test.go test(cli): pin ExitCode mapping (usage=2, runtime=1, nil=0) 2026-06-01 02:38:25 +05:30
hooks.go fix(codex): reliable activity signals — session-flag hooks, trust bypass, no_signal watchdog (#170) 2026-06-11 10:36:45 +05:30
hooks_test.go fix(codex): reliable activity signals — session-flag hooks, trust bypass, no_signal watchdog (#170) 2026-06-11 10:36:45 +05:30
import.go feat(import): rewrite-side legacy → rewrite first-boot import (#314) 2026-06-18 19:54:51 +05:30
import_test.go feat(import): rewrite-side legacy → rewrite first-boot import (#314) 2026-06-18 19:54:51 +05:30
orchestrator.go feat: add light backend CLI commands (#98) 2026-06-03 16:18:00 +05:30
orchestrator_test.go feat: add light backend CLI commands (#98) 2026-06-03 16:18:00 +05:30
output.go feat(backend): add cobra cli foundation 2026-06-01 01:44:43 +05:30
pr_ref.go feat: ao session claim-pr + spawn --claim-pr wiring (#101) 2026-06-06 00:01:03 +05:30
process.go refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30
process_unix.go refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30
process_windows.go refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30
project.go feat: add workspace project registration foundation (#165) 2026-06-10 16:10:14 +05:30
project_test.go feat(cli): add ao project ls/get/rm (#90) (#91) 2026-06-03 04:46:55 +05:30
review.go feat(review): configurable AO code review backend (V1) (#192) (#197) 2026-06-15 01:17:17 +05:30
review_test.go feat(review): configurable AO code review backend (V1) (#192) (#197) 2026-06-15 01:17:17 +05:30
root.go feat(import): rewrite-side legacy → rewrite first-boot import (#314) 2026-06-18 19:54:51 +05:30
root_test.go refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30
send.go fix: prefix ao send messages with sender session (#85) 2026-06-02 21:39:21 +05:30
send_test.go fix: prefix ao send messages with sender session (#85) 2026-06-02 21:39:21 +05:30
session.go fix(sessions): stop AO hook files from making every worktree permanently dirty (#169) 2026-06-11 11:06:45 +05:30
session_test.go fix(sessions): stop AO hook files from making every worktree permanently dirty (#169) 2026-06-11 11:06:45 +05:30
spawn.go fix(sessions): remove agent rules spawn path (#159) 2026-06-09 00:10:28 +05:30
spawn_test.go fix: 7 bugs from discussion #149 smoke walk (envelope, spawn, CDC, observer) (#153) 2026-06-07 07:35:46 +05:30
start.go feat(import): rewrite-side legacy → rewrite first-boot import (#314) 2026-06-18 19:54:51 +05:30
status.go refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30
stop.go refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30
stop_test.go refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30
version.go refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30