feat(import): rewrite-side legacy → rewrite first-boot import#314
Open
harshitsinghbhandari wants to merge 2 commits into
Open
feat(import): rewrite-side legacy → rewrite first-boot import#314harshitsinghbhandari wants to merge 2 commits into
harshitsinghbhandari wants to merge 2 commits into
Conversation
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>
- 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Implements the rewrite-side importer from the FINAL v2 plan: migration lives in the rewrite and runs as an opt-in import (not a legacy-side cutover). The rewrite reads the legacy flat-file store (
~/.agent-orchestrator) read-only and writes its own SQLite DB via the native storage layer, so nothing can be stranded and nothing is irreversible: legacy files are never modified and a re-run skips existing rows.Ports the already-built legacy-side TS reader (AgentWrapper #2144 / issue #2129) to Go; field mapping per ReverbCode #247.
What's included
internal/legacyimport— Go reader + pure field mappers:lifecyclekey, orstatePayload+stateVersion:"2"), with stringified-nested-field coercionsessionPrefixfallback (first 12 chars of project id){DataDir}/worktrees/{id}/orchestrator/{prefix}-orchestrator(verified againstgitworktree.managedPath)store.ImportSession— verbatim session insert (explicit id/num,ON CONFLICT(id) DO NOTHING) so the orchestrator lands at{prefix}-orchestrator, num 0; leaves the next store-generated worker at num 1 with no collision.ao import— explicit, idempotent import. Flags:--from,--dry-run,--yes(non-interactive),--json. Refuses while a live daemon owns the run-file.ao startoffers the import before launching the daemon when legacy data is present and the rewrite DB has no projects yet: `Found existing AO projects and sessions. Import them now? [Y/n]`. Declining or any failure is non-fatal; a non-interactive boot prints a hint to runao importrather than auto-importing.Scope (gist §6, locked)
All projects + per-project settings, and the single non-terminated orchestrator session per project (
claude-code/codex/opencode;aiderskipped with a note). Workers are not imported (they respawn fresh). The Claude transcript is relocated so a claude-code orchestrator resumes with context; codex/opencode resume by the global id carried inagent_session_id.Decisions settled (per brief)
ao importrefuses if a daemon is live.codexModel/restoreFallbackReason: no rewrite column exists (a session has a singleagent_session_id). Both are dropped and surfaced as import notes.agent_session_idcarriescodexThreadIdfor codex; the codex adapter resumes from the thread id alone, andrestoreFallbackReasonis forensic-only.first_signal_at: backfilled fromactivity_last_at(mirrors migration 0010) so imported orchestrators don't flip tono_signal.Tests / gate
Local gate green:
cd backend && go build ./... && go test -race ./...— 1423 tests pass. New coverage: pure mappers (permission/harness remap, lifecycle double-decode, state map, resume-id, prefix fallback, timestamp fallbacks), transcript slug + relocation idempotency, end-to-endRun(import / idempotent re-run / dry-run / no-data),ImportSessionverbatim+idempotent against a temp DB, and CLIao import(no-data / json import / daemon-running refusal).🤖 Generated with Claude Code