- Add optional globalDir parameter to resolveProject(), getDefaultProject(), setDefaultProject(), and clearDefaultProject() for test isolation - Remove ESLint suppression by using void operator for intentionally unused var - Update all tests to use isolated globalDir parameter - Complete tests for default project resolution
20 KiB
Task: KB-620 - Migration and First-Run Experience
Created: 2026-03-31 Size: M
Review Level: 3 (Full)
Assessment: This task affects all existing users on first run post-upgrade, requiring careful handling of auto-migration and backward compatibility. High security considerations around auto-discovery of project directories. Pattern follows existing db-migrate.ts patterns but introduces new central database integration.
Score: 7/8 — Blast radius: 2, Pattern novelty: 1, Security: 2, Reversibility: 2
Mission
Implement the migration and first-run experience that smoothly transitions kb from single-project mode to multi-project mode. When users upgrade to the multi-project version, the system must:
- Auto-detect existing
.kb/directories and automatically register them in the central project registry - Maintain backward compatibility — existing single-project workflows continue working without
--projectflags - Guide new users through an interactive setup wizard when no projects exist
- Ensure safety — migration is idempotent, reversible, and never deletes existing data
This task bridges the legacy single-project architecture with the new multi-project system, ensuring zero disruption to existing users while enabling the new functionality.
Dependencies
- Task: KB-615 (Multi-Project Core Infrastructure) — Must provide:
CentralCore,ProjectRegistry, project registration API at~/.pi/kb/kb-central.db - Task: KB-616 (Per-Project Runtime Abstraction) — Must provide:
InProcessRuntime,ProjectManager, runtime initialization patterns - Task: KB-618 (Dashboard Multi-Project UX) — Must provide: Setup wizard UI components, overview page,
/api/projectsendpoints - Task: KB-619 (CLI Multi-Project Commands) — Must provide:
fn projectsubcommands,--projectflag support, project resolution logic
Dependency Fallback Strategy
Since dependencies may be incomplete, implement these placeholder interfaces in packages/core/src/migration-stubs.ts:
// Stub interfaces until KB-615 completes
export interface CentralCoreStub {
registerProject(name: string, workingDir: string, options?: { isolationMode?: string }): Promise<{ id: string; name: string; workingDirectory: string }>;
listProjects(): Promise<Array<{ id: string; name: string; workingDirectory: string; status: string }>>;
isProjectRegistered(workingDir: string): boolean;
}
export interface ProjectInfoStub {
id: string;
name: string;
workingDirectory: string;
status: "active" | "paused" | "errored";
isolationMode: "in-process" | "child-process";
}
These stubs allow KB-620 to compile and test independently. When KB-615 completes, replace stubs with real implementations.
Context to Read First
packages/core/src/db-migrate.ts— Existing legacy→SQLite migration patterns (detect → migrate → backup)packages/core/src/global-settings.ts— Pattern for~/.pi/kb/directory managementpackages/core/src/db.ts— SQLite database initialization and WAL mode patternspackages/cli/src/bin.ts— CLI entry point, command routing (note:fn initdoes not exist yet)packages/core/src/index.ts— Current exports to understand public API surfacepackages/core/src/db-migrate.test.ts— Test patterns for migration testing (co-located with source)
File Scope
Modified Files
packages/core/src/db-migrate.ts— Extend withdetectExistingProjects(),autoRegisterInCentralDb(),needsCentralMigration()packages/core/src/central-core.ts— Add migration helpers:autoRegisterProject(),getFirstRunState()(or stubs if KB-615 incomplete)packages/cli/src/bin.ts— Addfn initcommand; add migration check on startuppackages/core/src/index.ts— Export new migration functions and types
New Files
packages/core/src/migration.ts—FirstRunDetectorclass,MigrationCoordinatorclass, migration typespackages/core/src/migration-stubs.ts— Stub interfaces for KB-615 dependencies (temporary, removed after KB-615)packages/core/src/migration.test.ts— Tests for auto-migration, first-run detection, idempotencypackages/core/src/backward-compat.test.ts— Tests for backward compatibility layer
Steps
Step 0: Preflight
- Required paths exist (
packages/core/src/,packages/cli/src/) - Dependencies have stubs or implementations
- Can import from
@fusion/corein tests
Step 1: First-Run Detection Logic
Create the detection logic that determines the migration/startup path.
- Create
packages/core/src/migration.tswithFirstRunDetectorclass:detectFirstRunState(): Promise<FirstRunState>— returns one of:"fresh-install"— No central DB, no.kb/anywhere"needs-migration"— No central DB, but.kb/kb.dbexists in cwd"setup-wizard"— Central DB exists but has zero projects"normal-operation"— Central DB exists with projects
detectExistingProjects(cwd: string): Promise<DetectedProject[]>— Walk filesystem looking for.kb/kb.db- Start from
cwd(default:process.cwd()) - Walk up directory tree to find
.kb/kb.db(stop at home or root) - Return:
{ path: string, name: string, hasDb: boolean }
- Start from
hasCentralDb(): boolean— Check~/.pi/kb/kb-central.dbexistsgetCentralDbPath(): string— Returns~/.pi/kb/kb-central.db
- Define types in
packages/core/src/migration.ts:type FirstRunState = "fresh-install" | "needs-migration" | "setup-wizard" | "normal-operation"interface DetectedProject { path: string; name: string; hasDb: boolean }interface MigrationResult { success: boolean; projectsRegistered: string[]; errors: string[] }
- Create
MigrationCoordinatorclass inpackages/core/src/migration.ts:- Constructor takes
centralCore: CentralCoreStub coordinateMigration(): Promise<MigrationResult>— Orchestrates full migration flowregisterSingleProject(projectPath: string): Promise<MigrationResult>— For auto-migrationcompleteSetup(projects: ProjectSetupInput[]): Promise<MigrationResult>— For setup wizard
- Constructor takes
- Write tests in
packages/core/src/migration.test.ts:- Test fresh install detection (no central DB, no local .kb/)
- Test migration-needed detection (local .kb/ exists, no central DB)
- Test setup-wizard detection (central DB exists, empty)
- Test normal operation detection (central DB exists with projects)
- Test project detection walking up directory tree
- Test
MigrationCoordinatororchestration flow
- Run targeted tests:
pnpm test packages/core/src/migration.test.ts
Artifacts:
packages/core/src/migration.ts(new)packages/core/src/migration.test.ts(new)
Step 2: Auto-Migration to Central Database
Extend the existing migration system to handle single-project → multi-project migration.
- Extend
packages/core/src/db-migrate.ts:- Add
needsCentralMigration(cwd: string): boolean— Returns true if:- Central DB doesn't exist AND
cwdhas.kb/kb.db(existing single-project)
- Add
detectExistingProjects(cwd: string): Promise<DetectedProject[]>— Walk up from cwd, find.kb/kb.db - Add
autoMigrateToCentral(existingProjectPath: string, centralCore: CentralCoreStub): Promise<MigrationResult>:- Generate project name from git remote (strip
.git, take repo name) or directory basename - Call
centralCore.registerProject(name, existingProjectPath, { isolationMode: "in-process" }) - Return migration result with registered project ID
- Generate project name from git remote (strip
- Add
- Extend
packages/core/src/central-core.ts(or create stub inmigration-stubs.tsif KB-615 incomplete):- Add
autoRegisterProject(projectPath: string): Promise<ProjectInfoStub>:- Generate name from git remote:
git remote get-url origin→ extract repo name - Fallback to directory basename
- Ensure unique name (append
-Nif conflict) - Register with
isolationMode: "in-process",status: "active" - Return registered project
- Generate name from git remote:
- Add
getFirstRunState(): Promise<FirstRunState>— Delegate toFirstRunDetector
- Add
- Ensure idempotency:
- Re-running migration with existing central DB → no-op
- Re-running migration for already-registered project → skip with info log
- Add
isProjectRegistered(projectPath: string): booleancheck
- Write tests in
packages/core/src/migration.test.ts:- Test auto-migration registers project correctly
- Test idempotent migration (run twice, second is no-op)
- Test project name generation from git remote
- Test project name fallback to directory name
- Test duplicate name handling (appends
-1,-2, etc.)
- Run targeted tests:
pnpm test packages/core/src/migration.test.ts
Artifacts:
packages/core/src/db-migrate.ts(modified — add central migration functions)packages/core/src/central-core.ts(modified — add migration helpers, or stubs file)packages/core/src/migration-stubs.ts(new — if KB-615 incomplete)
Step 3: Backward Compatibility Layer
Ensure single-project users experience zero behavior change.
- Update
packages/core/src/migration.tswithBackwardCompathelper:- Add
resolveProjectContext(cwd: string, projectId?: string): Promise<ResolvedContext>:- If
projectIdprovided → look up in central registry, return that project - If no
projectIdand single project registered → auto-use that project - If no
projectIdand multiple projects → throwProjectRequiredError - If no central DB available → return legacy mode (use cwd directly)
- If
- Add
isLegacyMode(): boolean— Returns true if central DB unavailable - Define
ResolvedContextinterface:{ projectId: string; workingDirectory: string; isLegacy: boolean }
- Add
- Update
packages/core/src/store.ts(if needed) to support backward-compatible initialization:- Ensure
TaskStoreconstructor still works with justrootDir - No breaking changes to existing
TaskStoreAPI
- Ensure
- Write tests in
packages/core/src/backward-compat.test.ts:- Test single project auto-resolution (no --project flag needed)
- Test multiple projects requires explicit selection
- Test legacy mode works without central database
- Test error message when multiple projects and no selection
- Run targeted tests:
pnpm test packages/core/src/backward-compat.test.ts
Artifacts:
packages/core/src/migration.ts(modified — add backward compat helpers)packages/core/src/backward-compat.test.ts(new)
Step 4: CLI Integration — fn init Command and Migration Hooks
Add fn init command and integrate migration into CLI startup.
- Update
packages/cli/src/bin.ts:- Add
fn initcommand handler:- Check if
.kb/exists in cwd → if yes, info message and exit - Create
.kb/directory withkb.db(use existing Database initialization from@fusion/core) - Register project in central DB via
autoRegisterProject() - Output:
Initialized kb project "X" at /path/to/project - Options:
--name <name>to override auto-detected name
- Check if
- Add migration check at startup (before executing commands, after
fn initcheck):// Skip migration check for init command itself if (command !== "init") { const detector = new FirstRunDetector(); const state = await detector.detectFirstRunState(); if (state === "needs-migration") { const cwd = process.cwd(); const centralCore = await createCentralCore(); // or stub const result = await autoMigrateToCentral(cwd, centralCore); console.log(`✓ Auto-registered project: ${result.projectsRegistered[0]}`); } } - Add
--projectglobal flag handling (parse and store for command use)
- Add
- Ensure
fn initremains idempotent:- If
.kb/already exists, skip creation - If project already registered, skip registration (info message: "Project already registered")
- If
- Write integration tests in
packages/cli/src/__tests__/init.test.ts(or co-located):- Test
fn initcreates.kb/and registers project - Test migration hook runs on first command after upgrade
- Test idempotent
fn init(run twice, second reports already initialized)
- Test
- Run CLI tests:
pnpm test packages/cli
Artifacts:
packages/cli/src/bin.ts(modified — addinitcommand, migration hooks)packages/cli/src/init.test.ts(new — co-located test file)
Step 5: Dashboard First-Run Wizard Integration
Wire dashboard to use migration and setup wizard.
- Ensure dashboard API has endpoints (coordinate with KB-618 or create stubs):
GET /api/setup-state— ReturnsFirstRunStateand detected projectsPOST /api/complete-setup— Accepts{ projects: ProjectSetupInput[] }
- Create
/api/setup-statehandler in dashboard:- Use
FirstRunDetectorto get state - Return JSON:
{ state, detectedProjects, hasCentralDb }
- Use
- Create
/api/complete-setuphandler in dashboard:- Accept project selections from wizard
- Call
centralCore.registerProject()for each - Return
{ success: true, registered: string[] }
- Update dashboard root (
packages/dashboard/app/root.tsx) to show migration prompt:- If state is
"needs-migration"→ Show "Migrate Project" prompt with single click - If state is
"setup-wizard"→ Show full setup wizard (from KB-618 or minimal stub) - If state is
"normal-operation"→ Show overview page
- If state is
- Test dashboard flows:
- Fresh install → setup wizard
- Existing single project → migration prompt → overview
- Multiple projects → overview directly
Artifacts:
- Dashboard API routes (new or extended from KB-618)
- Dashboard UI integration (coordinate with KB-618)
Step 6: Testing & Verification
ZERO test failures allowed. Full test suite as quality gate.
- Run all new tests:
pnpm test packages/core/src/migration.test.tspnpm test packages/core/src/backward-compat.test.tspnpm test packages/cli/src/init.test.ts
- Run full core test suite:
pnpm test packages/core - Run full CLI test suite:
pnpm test packages/cli - Run full engine test suite:
pnpm test packages/engine - Build passes:
pnpm build - Manual integration test scenarios:
- Fresh install (new user):
- Delete
~/.pi/kb/kb-central.dbif exists - Create new empty directory, run
fn init - Verify
.kb/created and project registered in central DB
- Delete
- Existing single-project user (upgrade scenario):
- Delete
~/.pi/kb/kb-central.db - Keep existing
.kb/kb.dbin test project - Run
fn task list→ should auto-migrate with console output - Verify project appears in central registry
- Delete
- Idempotent migration:
- Run
fn task listagain → no re-migration, no errors
- Run
- Backward compatibility (single project):
- With single project registered, run without
--project - Should work seamlessly
- With single project registered, run without
- Rollback safety:
- Delete
~/.pi/kb/kb-central.db - Run
fn task listin project directory → should still work (recreates or legacy mode)
- Delete
- Fresh install (new user):
Test Coverage Requirements:
- Migration detection: 100% of first-run states covered
- Auto-migration: register, idempotency, error handling
- Backward compat: single project auto-resolve, multi-project enforcement, legacy mode
- Name generation: git remote extraction, directory fallback, duplicate handling
- CLI init: create, register, idempotency
Step 7: Documentation & Delivery
- Add JSDoc to all public methods in
migration.ts - Update
packages/core/src/index.tsexports:- Export
FirstRunDetector,MigrationCoordinator,BackwardCompat - Export types:
FirstRunState,DetectedProject,MigrationResult,ResolvedContext - Export migration functions from
db-migrate.ts
- Export
- Update
AGENTS.mdwith Migration section:- Auto-Migration: How it works on first run after upgrade
- Backward Compatibility: Single-project mode continues working
- Rollback Procedure: Steps to recover if migration fails
### Rollback from Multi-Project Migration If the central database causes issues: 1. Delete `~/.pi/kb/kb-central.db` (this only removes the project registry) 2. Your per-project `.kb/kb.db` files remain intact with all data 3. kb will fall back to single-project legacy mode 4. Re-run `fn init` in your project to re-register if needed
- Create changeset:
cat > .changeset/multi-project-migration.md << 'EOF' --- "@fusion/core": minor "@gsxdsm/fusion": minor --- Add migration and first-run experience for multi-project support - Auto-detect and register existing projects on first run after upgrade - Maintain backward compatibility for single-project workflows - Interactive first-run setup wizard in dashboard - Idempotent migration — safe to re-run - Rollback procedure documented in AGENTS.md EOF - Stage changeset with final commit
- Out-of-scope findings (create new tasks via
task_createif found):- Advanced project discovery (deep filesystem scanning beyond cwd ancestors)
- Import from other task management tools
- Batch migration of multiple detected projects
Artifacts:
packages/core/src/index.ts(modified — new exports)AGENTS.md(modified — add Migration section).changeset/multi-project-migration.md(new)
Documentation Requirements
Must Update:
AGENTS.md— Add "Multi-Project Migration" section covering:- Auto-migration behavior on first run after update
- Backward compatibility guarantees
- Rollback procedure
packages/core/src/index.ts— ExportFirstRunDetector,MigrationCoordinator,BackwardCompat, migration types
Check If Affected:
README.md— Update quickstart if migration flow changes user experience significantlypackages/cli/README.md— Document newfn initcommand
Completion Criteria
- All steps complete
FirstRunDetectorcorrectly identifies all four first-run statesMigrationCoordinatororchestrates migration and setup flows- Auto-migration registers existing projects automatically and idempotently
fn initcommand creates.kb/and registers projects in central database- Backward compatibility: single-project workflows work without
--project - All integration test scenarios pass (fresh install, upgrade, idempotent, rollback)
- All tests passing with >80% coverage on new files
- Build passes with no TypeScript errors
AGENTS.mdupdated with migration and rollback documentation- Changeset created and staged
Git Commit Convention
Commits at step boundaries. All commits include the task ID:
- Step completion:
feat(KB-620): complete Step N — description- Example:
feat(KB-620): complete Step 1 — FirstRunDetector with state detection
- Example:
- Bug fixes:
fix(KB-620): description - Tests:
test(KB-620): description - Docs:
docs(KB-620): add migration section to AGENTS.md
Do NOT
- Delete or modify existing
.kb/directories during migration (only register them) - Break existing single-project workflows
- Require users to manually migrate their data
- Remove legacy
TaskStoreinitialization patterns - Skip the first-run wizard for fresh installs
- Auto-register projects without valid
.kb/kb.dbfiles - Allow more than 100 projects in auto-detection (safety limit)
- Skip tests for backward compatibility scenarios
- Modify the central database schema (that's KB-615)
- Implement full dashboard UI (that's KB-618 — only wire up the integration)
- Implement full CLI project commands (that's KB-619 — only add
fn initand migration hooks)
Security Considerations
- Path validation: All auto-detected paths must contain
.kb/kb.dbbefore registration - Path traversal: Sanitize and resolve all paths before storing in central DB
- Circular registration: Prevent projects being registered inside other projects
- Privacy: Don't expose absolute paths in UI (show project names, not full paths)
- Rate limiting: Auto-migration runs once; no repeated scanning
Rollback Safety
If migration fails or causes issues:
-
Delete central DB:
rm ~/.pi/kb/kb-central.db- This only removes the project registry
- All per-project data in
.kb/kb.dbremains intact - Blob files (PROMPT.md, agent.log, attachments) remain intact
-
Fallback behavior: kb automatically falls back to single-project legacy mode
-
Recovery: Re-run
fn initin your project directory to re-register -
Emergency bypass: Set
KB_SKIP_MIGRATION=1to disable auto-migration hooks