feat(FN-5411): add project identity recovery and identity-aware startup rea

Implements project identity tracking and recovery across the Fusion system (FN-5411), enabling persistent identity for projects across storage migrations, daemon reattaches, and CLI session management. Adds a project identity metadata API and central reattach ensure mechanism, wires identity stampin

Fusion-Task-Id: FN-5411

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5411
This commit is contained in:
gsxdsm
2026-05-23 04:11:09 -07:00
parent fe58a57a7d
commit 687237bd91
28 changed files with 1172 additions and 119 deletions

View File

@@ -61,6 +61,18 @@ If one side succeeds and the other fails, the next scheduler/self-healing tick r
This fencing prevents double-claims: a restarted or delayed stale owner cannot reclaim work once central ownership has been released and lease generation has advanced.
## Recovering after a central DB wipe
If a project's row is deleted from `~/.fusion/fusion-central.db`, Fusion now automatically recovers on next startup:
1. Startup checks central for a row at the project path.
2. If missing, it reads `__meta.projectIdentity` from `<project>/.fusion/fusion.db`.
3. If present, central reattaches that exact `projectId` instead of creating a new one.
This prevents “empty workspace” regressions where project data still exists locally but is keyed to an older `projectId`.
Backups remain the first-line protection strategy (see FN-5407), but this identity reattach path lets operators recover even when no central backup is available.
## Registering and Managing Projects
```bash
@@ -331,3 +343,11 @@ flowchart TD
```
See also: [Architecture](./architecture.md), [CLI Reference](./cli-reference.md), and [Missions](./missions.md).
## Identity persistence and recovery
Each project persists its canonical central identity inside `.fusion/fusion.db` `__meta` as `projectId` and `projectCreatedAt`. Registration paths should use `CentralCore.ensureProjectForPath({ path, identity, ... })` after reading local identity with `readProjectIdentity()`; this reattaches central rows when central was wiped and refuses silent remint if the persisted id is owned by another path.
Dashboard `POST /api/projects` now surfaces this mismatch as `409` with `error: "orphan-identity"` and recovery metadata, and callers can opt into recovery flows with `acceptRecovery: true` behavior at the route layer.
Central DB backup coverage is already enabled by default (`BackupManager` uses `includeCentralDb: true`), so identity recovery data remains in the normal daily backup set.

View File

@@ -339,7 +339,7 @@ The `tasks.githubTracking` JSON column stores per-task GitHub tracking state (`e
| `secrets` | Encrypted secret KV rows (`key` unique) with raw BLOB `value_ciphertext` + per-row random `nonce` (AES-256-GCM), per-secret `access_policy` CHECK (`auto`/`prompt`/`deny`), env-materialization metadata (`env_exportable`, `env_export_key`), and read-audit fields (`last_read_at`, `last_read_by`). Plaintext is never written to the database. |
| `task_documents` | Task-scoped document metadata/content keyed by `(taskId, key)` with current revision pointer. |
| `task_document_revisions` | Immutable revision history for task documents (content snapshots by revision). |
| `__meta` | Schema version + monotonic `lastModified` change detector, plus one-time bootstrap metadata such as `bootstrappedAt`. |
| `__meta` | Schema version + monotonic `lastModified` change detector, plus one-time bootstrap metadata such as `bootstrappedAt` and `projectIdentity`. |
| `missions` | Mission-level planning hierarchy root. |
| `milestones` | Milestones under missions, including dependency lists and validation state. |
| `slices` | Slices under milestones with plan-state/activation metadata. |
@@ -393,6 +393,16 @@ The `tasks.githubTracking` JSON column stores per-task GitHub tracking state (`e
Invariant: after init, every declared column for covered tables exists regardless of `__meta.schemaVersion` whenever the fingerprint is stale or missing, preventing legacy drift from causing `no such column` regressions on newly added fields while keeping unchanged-schema opens fast.
### Project identity row (`__meta.projectIdentity`)
Each project-scoped `.fusion/fusion.db` now stores the canonical central registry identity in `__meta.projectIdentity` as JSON:
```json
{ "id": "proj_0123456789abcdef", "createdAt": "2026-05-21T12:00:00.000Z", "firstSeenPath": "/abs/project/path" }
```
This is written on first successful registration (and back-filled on later startup for older projects). If `~/.fusion/fusion-central.db` loses the row for that path, startup reads this identity and reattaches the same `projectId` instead of minting a new id. That preserves project-scoped rows keyed by `projectId` (`todo_lists`, `chat_sessions`, `project_insights`, etc.).
---
### Chat rooms (migration 70)