feat(import): dashboard-surfaced legacy import offer + daemon API#320
Draft
harshitsinghbhandari wants to merge 6 commits into
Draft
feat(import): dashboard-surfaced legacy import offer + daemon API#320harshitsinghbhandari wants to merge 6 commits into
harshitsinghbhandari wants to merge 6 commits into
Conversation
Wire the trigger around the merged import engine (#314): detect a legacy AO install at daemon start and surface the opt-in in the dashboard rather than the CLI. Backend: - internal/service/importer: controller-facing service wrapping the legacyimport engine. Status() reports availability (legacy data present AND the rewrite DB has no projects yet); Run() executes the import through the daemon's own store, so the daemon stays the sole writer and the run is idempotent. - GET/POST /api/v1/import: ImportController, wired into the daemon's API deps and the code-first OpenAPI spec (regenerated openapi.yaml + schema.ts). - `ao start` is now headless: it no longer prompts on first boot. The CLI `ao import` command is unchanged for explicit offline imports. Frontend: - useImportStatus / useRunImport hooks polling the daemon and triggering the import; a 501/unreachable daemon resolves to "no offer". - ImportOffer banner on the dashboard board with accept/decline. Accept runs the import and invalidates the workspace query so the imported projects and revived orchestrator appear; decline dismisses for the session (data untouched, can import later). Gate: `go build ./... && go test -race ./...` green (1436 tests); golangci-lint clean on touched packages; frontend typecheck + vitest green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…daemon
The dashboard import offer never appeared. Two independent causes:
1. Legacy config.yaml failed to parse, so Status() reported available:false
even on an empty rewrite DB. `legacyProjectConfig.Repo` was typed `string`,
but the real config stores `repo:` as a map ({owner,name,platform,
originUrl}); yaml.v3 raised a *yaml.TypeError and loadLegacyConfig treated
any error as fatal, returning an empty registry -> HasLegacyData false ->
offer hidden. The field was dead (origin is re-resolved via git).
- Retype Repo as a capture-only *yaml.Node so the structured block parses.
- Make loadLegacyConfig tolerant of *yaml.TypeError: keep yaml.v3's partial
decode, hard-fail only on real read/syntax errors.
- Regression tests: realistic config (repo map, multiple projects) asserts
HasLegacyData==true and fields parse; a deliberate type mismatch survives
as a partial parse; a genuine syntax error stays fatal.
2. A manually-started daemon (`ao start` in a terminal) was never discovered:
port discovery ran only inside the spawn path, so the renderer stayed on its
compiled default base URL (localhost:3001 -> IPv6, can hit an unrelated
server). Add always-on discovery from ~/.ao/running.json (read on app ready
+ poll), independent of spawning; it targets 127.0.0.1, sidestepping the
IPv6 collision, and stays passive while we own the daemon process so it never
races the spawn path. New shouldAdoptDiscoveredPort helper is unit-tested.
Gate: `go build ./... && go test -race ./...` green (1439 tests); golangci-lint
clean on legacyimport; frontend typecheck + vitest green (148 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per product decision, the legacy import now brings in projects (and their per-project settings) only. Orchestrator-session porting and Claude transcript relocation are removed: workers/orchestrators respawn fresh from the imported projects instead. Backend: - legacyimport: delete orchestrator.go + claude.go (and tests). Run() loops projects only; Store drops GetSession/ImportSession; Options drops DataDir/ClaudeProjectsDir; Report drops the orchestrator/transcript counters. paths.go drops the now-unused projectSessionsDir/isDir/defaultClaudeProjectsDir. - store: remove the import-only ImportSession verbatim-insert method (+test); nothing else used it. - service/importer + daemon wiring: drop the now-unused DataDir. - `ao import` CLI: help text, confirm prompt, and summary no longer mention orchestrators/transcripts. - OpenAPI regenerated (ImportReport loses the orchestrator/transcript fields). Frontend: - schema.ts regenerated; ImportReport type trimmed; offer copy is now "Import projects from your earlier AO?" with matching test assertions. Gate: `go build ./... && go test -race ./...` green (1424 tests); golangci-lint clean on changed packages; frontend typecheck + vitest green (148 tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-wiring # Conflicts: # frontend/src/main.ts
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.
Wires the trigger around the merged import engine (#314): detect a legacy AO install on daemon start and surface the opt-in in the dashboard, not as a CLI prompt. Reuses the #314 engine verbatim; this is the detection + API + dashboard wiring around it.
Plan reference: https://gist.github.com/harshitsinghbhandari/c5d3233b36fcc4726ec9d7373fd0254a
What changed
ao start/ detection (headless)ao startno longer prompts on first boot. It only launches the daemon.internal/service/importerwraps thelegacyimportengine with two operations:GET /api/v1/import(status) andPOST /api/v1/import(run), reflected into the code-first OpenAPI spec;openapi.yamlandfrontend/src/api/schema.tsregenerated.ao importCLI command is unchanged for explicit offline imports.Dashboard offer (where the user sees it)
useImportStatus/useRunImporthooks poll the daemon and trigger the import. A 501/unreachable daemon resolves to "no offer" rather than an error.ImportOfferbanner on the dashboard board: "Import projects and orchestrator from your earlier AO?" with Import / Not now./api/v1/import, then invalidate the workspace query so the imported projects + revived orchestrator appear.Notes
Testing
cd backend && go build ./... && go test -race ./...— green (1436 tests).golangci-lint(v2.12.2) clean on touched packages.typecheck+vitestgreen; newImportOfferand importer service/controller tests added.🤖 Generated with Claude Code