fix(FN-7952): cut runtime services over to PostgreSQL#2109
Conversation
Make the async PostgreSQL data layer authoritative and migrate the minimum runtime composition seams needed to keep the repository buildable as the first layer of the cutover stack. Fusion-Task-Id: FN-7952
📝 WalkthroughWalkthroughThis PR completes PostgreSQL-oriented storage migration across dashboard and engine paths, replaces synchronous audit and knowledge-index APIs with async variants, coordinates project-store eviction, enforces persistence ordering, simplifies worktree hydration, removes the storage migration notice, and adds regression and integration coverage. ChangesPostgreSQL storage and dashboard routing
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR moves runtime services to the PostgreSQL data layer. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (5): Last reviewed commit: "test(FN-7952): preserve stacked PostgreS..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/dashboard/src/routes/register-agent-runtime-routes.ts (1)
1480-1486: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick winUse the existing count query for pagination metadata
countRunAuditEventsalready exists inpackages/core/src/task-store/async-audit.ts, butTaskStoreonly exposesgetRunAuditEventsAsync, so this route still loads every matching event just to computetotalCount/hasMore. Add a thin store wrapper or call the count helper here instead of materializing the full result set.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/dashboard/src/routes/register-agent-runtime-routes.ts` around lines 1480 - 1486, Replace the full-result call to scopedStore.getRunAuditEventsAsync in the audit-events route with the existing countRunAuditEvents query, exposing it through a thin TaskStore wrapper if needed. Pass the same runId, taskId, domain, startTime, and endTime filters, and use the count directly for totalCount/hasMore pagination metadata without materializing all events.packages/dashboard/src/chat.ts (1)
2537-2547: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winPrevent false failures and duplicate messages on token-accounting errors.
Because
recordTokenUsageis now awaited after the primary message has been successfully delivered or persisted, a database failure during token accounting throws an error that inadvertently breaks the local success flow. This leads to data inconsistencies and false UI errors despite the message already reaching the chat room.
packages/dashboard/src/chat.ts#L2537-L2547: Wrap thisrecordTokenUsagecall in atry/catchblock. If it throws, it currently falls into the outercatchblock which incorrectly assumes the AI generation failed, leading it to re-persistaccumulatedTextas a duplicate message.packages/dashboard/src/chat.ts#L1648-L1656: Wrap thisrecordTokenUsagecall in atry/catchblock. If telemetry fails, it currently skipssuccessfulResponderIds.push(responder.id)and can cause a falseRoomReplyGenerationErroreven though the message was delivered.packages/dashboard/src/chat.ts#L1957-L1967: Wrap thisrecordTokenUsagecall in atry/catchblock to prevent telemetry errors from falsely bubbling up as a chat-breaking error for CLI sessions.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/dashboard/src/chat.ts` around lines 2537 - 2547, Wrap each recordTokenUsage call in packages/dashboard/src/chat.ts at lines 2537-2547, 1648-1656, and 1957-1967 in its own try/catch. Handle token-accounting failures without propagating them into the surrounding chat-generation error paths, ensuring the delivered message remains successful, successfulResponderIds is still updated, and CLI sessions do not fail due to telemetry errors.
🧹 Nitpick comments (3)
packages/dashboard/src/__tests__/project-store-resolver.test.ts (1)
230-236: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winSignal delayed-watch entry directly instead of polling.
vi.waitForintroduces wall-clock polling even though this mock controls the exact seam. Expose and await a deferredwatchStartedpromise before triggering eviction.As per coding guidelines, tests should use narrow seams and must not add real polling loops.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/dashboard/src/__tests__/project-store-resolver.test.ts` around lines 230 - 236, Replace the vi.waitFor polling in the “awaits an in-flight creation and closes it without a late cache insertion” test with a deferred watchStarted promise controlled by the delayed-watch mock. Await watchStarted to detect that the watch has begun, then trigger evictAllProjectStores and release the delayed watch while preserving the existing assertions and test behavior.Source: Coding guidelines
packages/dashboard/src/routes/register-knowledge-routes.ts (1)
60-64: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winFetch pages and count concurrently.
Since querying pages and counting the total are independent database operations, they can be executed concurrently to reduce latency.
⚡ Proposed refactor
- const pages = await queryKnowledgePagesAsync(layer, options); - res.json({ - query: q, - pages, - total: await countKnowledgePagesAsync(layer), - }); + const [pages, total] = await Promise.all([ + queryKnowledgePagesAsync(layer, options), + countKnowledgePagesAsync(layer), + ]); + res.json({ + query: q, + pages, + total, + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/dashboard/src/routes/register-knowledge-routes.ts` around lines 60 - 64, Update the route handler around queryKnowledgePagesAsync and countKnowledgePagesAsync to start both independent database operations concurrently, await their results together, and use those results for pages and total in res.json. Preserve the existing query and response fields.packages/dashboard/src/routes/register-git-github.ts (1)
686-697: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove redundant
TaskStoreintersection types.Both files define redundant intersection types (
TaskStore & { getRunAuditEventsAsync: ... }). SinceTaskStorenatively definesgetRunAuditEventsAsync(as seen being called natively without casts atregister-task-workflow-routes.tsline 3045), these intersections can be removed to avoid clutter and stale typings.
packages/dashboard/src/routes/register-git-github.ts#L686-L697: TypescopedStoresimply asTaskStore. (Note: The caller around line 880 of this file is also castingscopedStorewith the old synchronousgetRunAuditEvents?: ...signature, which can be cleaned up as well).packages/dashboard/src/routes/register-task-workflow-routes.ts#L717-L725: Remove thestoreWithRunAuditintersection cast entirely and just usescopedStoredirectly.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/dashboard/src/routes/register-git-github.ts` around lines 686 - 697, Remove the redundant RunAuditEvents intersection types: in packages/dashboard/src/routes/register-git-github.ts:686-697, type scopedStore as TaskStore and clean up the caller near line 880 using the obsolete synchronous signature; in packages/dashboard/src/routes/register-task-workflow-routes.ts:717-725, remove the storeWithRunAudit intersection cast and pass scopedStore directly, relying on TaskStore.getRunAuditEventsAsync.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/dashboard/src/project-store-resolver.ts`:
- Around line 48-49: Make the eviction guards ownership-aware in the
project/global eviction flows using evictionRequested and evictAllInProgress:
deduplicate or track all concurrent eviction promises so each caller awaits the
shared cleanup, and only clear the guard after every overlapping barrier
completes. Update the related guard checks and cleanup paths without allowing
replacement stores to initialize while the previous backend is still shutting
down.
In `@packages/engine/src/__tests__/in-process-runtime.pg.test.ts`:
- Around line 37-48: Update the PostgreSQL setup around PG_TEST_URL_BASE,
pgDescribe, and adminExec to use a separately gated integration-test harness
rather than defaulting to localhost. Keep these tests excluded unless the
explicit PostgreSQL integration gate is enabled, and replace synchronous
execFileSync administration with an asynchronous command that enforces a timeout
and rejects on failure or timeout.
- Around line 76-114: Ensure the test teardown around InProcessRuntime.start,
central, and runtime uses try/finally or retains these handles for afterEach so
failures after startup still stop the runtime before central/database cleanup.
Preserve the existing idempotent runtime.stop assertions for the successful path
and prevent timers, watchers, or backend clients from surviving failed tests.
In `@packages/engine/src/cli-agent/session-manager.ts`:
- Line 500: Wrap the teardown update and `this.store.flush()` call in the
spawn-failure handling path with a best-effort `try/catch`, matching the
patterns in `handleExit` and `killLive`. Ensure any persistence error is
contained and the original PTY spawn error `err` remains the propagated failure.
In `@scripts/lib/test-quarantine.json`:
- Around line 184-187: Update the quarantine entry for
packages/dashboard/src/__tests__/routes-system.test.ts by adding a direct CI job
or archived-run URL to the reason, while preserving the existing failure details
and quarantine scope.
---
Outside diff comments:
In `@packages/dashboard/src/chat.ts`:
- Around line 2537-2547: Wrap each recordTokenUsage call in
packages/dashboard/src/chat.ts at lines 2537-2547, 1648-1656, and 1957-1967 in
its own try/catch. Handle token-accounting failures without propagating them
into the surrounding chat-generation error paths, ensuring the delivered message
remains successful, successfulResponderIds is still updated, and CLI sessions do
not fail due to telemetry errors.
In `@packages/dashboard/src/routes/register-agent-runtime-routes.ts`:
- Around line 1480-1486: Replace the full-result call to
scopedStore.getRunAuditEventsAsync in the audit-events route with the existing
countRunAuditEvents query, exposing it through a thin TaskStore wrapper if
needed. Pass the same runId, taskId, domain, startTime, and endTime filters, and
use the count directly for totalCount/hasMore pagination metadata without
materializing all events.
---
Nitpick comments:
In `@packages/dashboard/src/__tests__/project-store-resolver.test.ts`:
- Around line 230-236: Replace the vi.waitFor polling in the “awaits an
in-flight creation and closes it without a late cache insertion” test with a
deferred watchStarted promise controlled by the delayed-watch mock. Await
watchStarted to detect that the watch has begun, then trigger
evictAllProjectStores and release the delayed watch while preserving the
existing assertions and test behavior.
In `@packages/dashboard/src/routes/register-git-github.ts`:
- Around line 686-697: Remove the redundant RunAuditEvents intersection types:
in packages/dashboard/src/routes/register-git-github.ts:686-697, type
scopedStore as TaskStore and clean up the caller near line 880 using the
obsolete synchronous signature; in
packages/dashboard/src/routes/register-task-workflow-routes.ts:717-725, remove
the storeWithRunAudit intersection cast and pass scopedStore directly, relying
on TaskStore.getRunAuditEventsAsync.
In `@packages/dashboard/src/routes/register-knowledge-routes.ts`:
- Around line 60-64: Update the route handler around queryKnowledgePagesAsync
and countKnowledgePagesAsync to start both independent database operations
concurrently, await their results together, and use those results for pages and
total in res.json. Preserve the existing query and response fields.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 7b5a8c41-2497-4b92-abfd-1e86654438fd
📒 Files selected for processing (62)
packages/core/src/__tests__/planner-overseer-events.test.tspackages/dashboard/app/components/StorageMigrationNoticeBanner.csspackages/dashboard/app/components/StorageMigrationNoticeBanner.tsxpackages/dashboard/app/components/__tests__/StorageMigrationNoticeBanner.test.tsxpackages/dashboard/app/components/dashboard/DashboardBanners.tsxpackages/dashboard/src/__tests__/knowledge-index.pg.test.tspackages/dashboard/src/__tests__/project-store-resolver.test.tspackages/dashboard/src/__tests__/require-async-layer.test.tspackages/dashboard/src/__tests__/routes-planning.test.tspackages/dashboard/src/__tests__/routes-project-discovery.test.tspackages/dashboard/src/__tests__/server-postgres-store-construction.test.tspackages/dashboard/src/__tests__/setup-routes.test.tspackages/dashboard/src/chat.tspackages/dashboard/src/index.tspackages/dashboard/src/knowledge-index.tspackages/dashboard/src/monitor-trait.tspackages/dashboard/src/otel-exporter.tspackages/dashboard/src/planning.tspackages/dashboard/src/project-store-resolver.tspackages/dashboard/src/routes.tspackages/dashboard/src/routes/monitor-routes.tspackages/dashboard/src/routes/register-agent-core-routes.tspackages/dashboard/src/routes/register-agent-runtime-routes.tspackages/dashboard/src/routes/register-approval-routes.tspackages/dashboard/src/routes/register-command-center-routes.tspackages/dashboard/src/routes/register-git-github.tspackages/dashboard/src/routes/register-knowledge-routes.tspackages/dashboard/src/routes/register-messaging-scripts.tspackages/dashboard/src/routes/register-project-routes.tspackages/dashboard/src/routes/register-session-diff-routes.tspackages/dashboard/src/routes/register-settings-memory-routes.tspackages/dashboard/src/routes/register-signal-routes.tspackages/dashboard/src/routes/register-task-workflow-routes.tspackages/dashboard/src/routes/register-worktrunk-routes.tspackages/dashboard/vitest.config.tspackages/engine/src/__tests__/executor-fast-mode-workflows.test.tspackages/engine/src/__tests__/in-process-runtime.pg.test.tspackages/engine/src/__tests__/mission-execution-loop.test.tspackages/engine/src/__tests__/planner-overseer-intervention-wiring.test.tspackages/engine/src/__tests__/plugin-runner.test.tspackages/engine/src/__tests__/project-engine-manager.test.tspackages/engine/src/__tests__/reliability-interactions/engine-stop-aborts-execution.test.tspackages/engine/src/__tests__/self-healing.test.tspackages/engine/src/__tests__/workflow-authoritative-driver-async-selection.test.tspackages/engine/src/__tests__/worktree-db-hydrate.test.tspackages/engine/src/agent-heartbeat.tspackages/engine/src/backlog-pressure-reporter.tspackages/engine/src/cli-agent/__tests__/runtime-postgres.test.tspackages/engine/src/cli-agent/session-manager.tspackages/engine/src/cron-runner.tspackages/engine/src/dependency-blocked-todo-reporter.tspackages/engine/src/effective-settings.tspackages/engine/src/evaluator-evidence.tspackages/engine/src/project-engine-manager.tspackages/engine/src/project-engine.tspackages/engine/src/routine-runner.tspackages/engine/src/unlinked-missions-advisory-reporter.tspackages/engine/src/worktree-acquisition.tspackages/engine/src/worktree-db-hydrate.tspackages/i18n/locales/en/app.jsonpackages/i18n/src/resources.d.tsscripts/lib/test-quarantine.json
💤 Files with no reviewable changes (5)
- packages/dashboard/app/components/StorageMigrationNoticeBanner.css
- packages/dashboard/app/components/tests/StorageMigrationNoticeBanner.test.tsx
- packages/dashboard/app/components/StorageMigrationNoticeBanner.tsx
- packages/i18n/src/resources.d.ts
- packages/i18n/locales/en/app.json
Fusion-Task-Id: FN-7952
Fusion-Task-Id: FN-7952
Avoid replaying plugin schema hooks after PluginLoader has initialized each plugin, preserve fatal serve schema failures, and shut down daemon/dashboard resources in their owning order.
Migrate the remaining engine and dashboard runtime paths, remove the obsolete migration notice, and keep the quarantine ledger paired with its dashboard exclusion. Fusion-Task-Id: FN-7952
8b9125d to
73ed302
Compare
## Summary CLI commands, daemon/dashboard startup, packaged desktop startup, and live-data maintenance scripts now share the mandatory PostgreSQL lifecycle. Operators no longer risk a command silently reading or writing a disconnected SQLite shadow when PostgreSQL setup fails. ## Design decisions - Every startup owner retains and awaits its PostgreSQL shutdown callback, including partial-startup failure paths. - CLI project context and lock-retry flows resolve through asynchronous project stores. - Maintenance scripts use the shared backend helper; explicit database migration/inspection remains the only CLI surface allowed to read legacy SQLite sources. ## Validation - CLI and Desktop typechecks pass on the stacked branch. - `pnpm test:gate` passes all 478 gate tests. - This PR changes 54 files. ## Stack - Depends on #2109, which depends on #2108. - Bundled plugins and docs/release follow in later PRs. Related: #2105 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * PostgreSQL is now the authoritative store for structured project and task metadata. * Projects can be recognized and initialized using `.fusion/project.json`, without creating a legacy SQLite database. * CLI commands now retry transient PostgreSQL contention errors. * **Bug Fixes** * Improved cleanup when commands complete, fail, or run in the background, preventing lingering resources. * Improved desktop, server, and session shutdown reliability. * **Documentation** * Updated storage and standalone binary guidance to reflect PostgreSQL and legacy SQLite compatibility. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary Operators and contributors now have one current contract for the completed PostgreSQL cutover: PostgreSQL is mandatory runtime authority, legacy SQLite files are read-only migration evidence, and rollback is restore-based rather than a fallback toggle. The major changeset communicates the same boundary to published CLI users. ## Operational guidance - Documents the authoritative storage inventory and the five intentional legacy SQLite readers. - Defines maintenance-window migration, backup, restore-test, ownership, and isolation checks. - Clarifies lifecycle ownership for CLI, desktop, dashboard, engine, and bundled plugins. - Supersedes the earlier readiness review without deleting its historical record. ## Validation - Changeset format check passes. - `pnpm test:gate` passes all 478 gate tests on the completed stack. - Every file from the original oversized PR is represented in the replacement stack; this PR changes 22 files. ## Stack - Depends on #2111 → #2110 → #2109 → #2108. - Merge bottom-up in that order. Related: #2105
## Summary Operators and contributors now have one current contract for the completed PostgreSQL cutover: PostgreSQL is mandatory runtime authority, legacy SQLite files are read-only migration evidence, and rollback is restore-based rather than a fallback toggle. The major changeset communicates the same boundary to published CLI users. ## Operational guidance - Documents the authoritative storage inventory and the five intentional legacy SQLite readers. - Defines maintenance-window migration, backup, restore-test, ownership, and isolation checks. - Clarifies lifecycle ownership for CLI, desktop, dashboard, engine, and bundled plugins. - Supersedes the earlier readiness review without deleting its historical record. ## Validation - Changeset format check passes. - `pnpm test:gate` passes all 478 gate tests on the completed stack. - Every file from the original oversized PR is represented in the replacement stack; this PR changes 22 files. ## Stack - Depends on #2111 → #2110 → #2109 → #2108. - Merge bottom-up in that order. Related: #2105
## Summary Operators and contributors now have one current contract for the completed PostgreSQL cutover: PostgreSQL is mandatory runtime authority, legacy SQLite files are read-only migration evidence, and rollback is restore-based rather than a fallback toggle. The major changeset communicates the same boundary to published CLI users. ## Operational guidance - Documents the authoritative storage inventory and the five intentional legacy SQLite readers. - Defines maintenance-window migration, backup, restore-test, ownership, and isolation checks. - Clarifies lifecycle ownership for CLI, desktop, dashboard, engine, and bundled plugins. - Supersedes the earlier readiness review without deleting its historical record. ## Validation - Changeset format check passes. - `pnpm test:gate` passes all 478 gate tests on the completed stack. - Every file from the original oversized PR is represented in the replacement stack; this PR changes 22 files. ## Stack - Depends on #2111 → #2110 → #2109 → #2108. - Merge bottom-up in that order. Related: #2105
## Summary Bundled plugins now persist shared runtime state in project-scoped PostgreSQL tables instead of maintaining independent SQLite authority. Reports, CLI Printing Press, Compound Engineering, Roadmap, Even Realities, and WhatsApp all follow the same ownership and startup contract as Fusion core. ## Design decisions - Plugin schema hooks run through the host’s PostgreSQL owner and enforce project isolation. - The SDK exposes the host contract needed by bundled plugins without importing engine internals. - Legacy Roadmap ownership fixtures use the supported empty-owner sentinel, preserving current composite primary/foreign keys while exercising backfill behavior. - The lockfile travels with the Even Realities PostgreSQL dependency so packaged installs remain reproducible. ## Validation - All six affected plugin builds pass. - Affected plugin suites pass: 773 tests across Printing Press, Compound Engineering, Even Realities, Reports, Roadmap, and WhatsApp. - `pnpm test:gate` passes all 478 gate tests. - This PR changes 40 files. ## Stack - Depends on #2110 → #2109 → #2108. - The documentation/release PR completes the stack. Related: #2105 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Breaking Changes** * PostgreSQL is now required for runtime storage; SQLite files are used only as one-time migration inputs. * The legacy `FUSION_NO_EMBEDDED_PG` fallback has been removed. * **New Features** * Added project-isolated PostgreSQL storage for plugins, reports, tasks, notifications, and other plugin data. * Added agent tools for reports and CLI service drafts. * Added PostgreSQL schema initialization support for plugin authors. * **Bug Fixes** * Improved migration and recovery of legacy plugin state. * Prevented cross-project data access and strengthened transactional schema updates. * **Documentation** * Updated storage, migration, deployment, plugin authoring, CLI, and dashboard guidance for PostgreSQL. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary Engine and dashboard traffic now stays on the authoritative PostgreSQL layer across execution, recovery, project discovery, planning sessions, analytics, and shutdown. The dashboard no longer presents a migration notice for a cutover that is already mandatory. ## Design decisions - Runtime composition requires an async data layer instead of constructing a hidden SQLite fallback. - Engine workflow, mission, claim, and self-healing reads await their PostgreSQL-backed store contracts. - Project-scoped dashboard stores retain and close their backend owner exactly once. - The dashboard test quarantine entry remains paired with its Vitest exclusion, preserving the repository’s deletion-ratchet policy. ## Validation - Core, Engine, Dashboard, CLI, and Desktop typechecks pass on the stacked branch. - `pnpm test:gate` passes all 478 gate tests. - This PR changes 62 files. ## Stack - Depends on #2108. - CLI/desktop/ops, plugins, and docs/release follow in later PRs. Related: #2105 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Project discovery now recognizes projects using the `.fusion/project.json` marker. * Knowledge indexing and search are more reliable across project-scoped storage. * **Bug Fixes** * Improved session, audit timeline, approval, monitoring, and analytics data consistency. * Prevented stale planning-session updates and project-store shutdown races. * Ensured chat usage and CLI session status are saved before continuing. * **UI Changes** * Removed the storage migration notice banner now that the PostgreSQL transition is complete. * **Reliability** * Improved shutdown handling, workflow execution, and worktree behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary CLI commands, daemon/dashboard startup, packaged desktop startup, and live-data maintenance scripts now share the mandatory PostgreSQL lifecycle. Operators no longer risk a command silently reading or writing a disconnected SQLite shadow when PostgreSQL setup fails. ## Design decisions - Every startup owner retains and awaits its PostgreSQL shutdown callback, including partial-startup failure paths. - CLI project context and lock-retry flows resolve through asynchronous project stores. - Maintenance scripts use the shared backend helper; explicit database migration/inspection remains the only CLI surface allowed to read legacy SQLite sources. ## Validation - CLI and Desktop typechecks pass on the stacked branch. - `pnpm test:gate` passes all 478 gate tests. - This PR changes 54 files. ## Stack - Depends on #2109, which depends on #2108. - Bundled plugins and docs/release follow in later PRs. Related: #2105 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * PostgreSQL is now the authoritative store for structured project and task metadata. * Projects can be recognized and initialized using `.fusion/project.json`, without creating a legacy SQLite database. * CLI commands now retry transient PostgreSQL contention errors. * **Bug Fixes** * Improved cleanup when commands complete, fail, or run in the background, preventing lingering resources. * Improved desktop, server, and session shutdown reliability. * **Documentation** * Updated storage and standalone binary guidance to reflect PostgreSQL and legacy SQLite compatibility. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary Bundled plugins now persist shared runtime state in project-scoped PostgreSQL tables instead of maintaining independent SQLite authority. Reports, CLI Printing Press, Compound Engineering, Roadmap, Even Realities, and WhatsApp all follow the same ownership and startup contract as Fusion core. ## Design decisions - Plugin schema hooks run through the host’s PostgreSQL owner and enforce project isolation. - The SDK exposes the host contract needed by bundled plugins without importing engine internals. - Legacy Roadmap ownership fixtures use the supported empty-owner sentinel, preserving current composite primary/foreign keys while exercising backfill behavior. - The lockfile travels with the Even Realities PostgreSQL dependency so packaged installs remain reproducible. ## Validation - All six affected plugin builds pass. - Affected plugin suites pass: 773 tests across Printing Press, Compound Engineering, Even Realities, Reports, Roadmap, and WhatsApp. - `pnpm test:gate` passes all 478 gate tests. - This PR changes 40 files. ## Stack - Depends on #2110 → #2109 → #2108. - The documentation/release PR completes the stack. Related: #2105 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Breaking Changes** * PostgreSQL is now required for runtime storage; SQLite files are used only as one-time migration inputs. * The legacy `FUSION_NO_EMBEDDED_PG` fallback has been removed. * **New Features** * Added project-isolated PostgreSQL storage for plugins, reports, tasks, notifications, and other plugin data. * Added agent tools for reports and CLI service drafts. * Added PostgreSQL schema initialization support for plugin authors. * **Bug Fixes** * Improved migration and recovery of legacy plugin state. * Prevented cross-project data access and strengthened transactional schema updates. * **Documentation** * Updated storage, migration, deployment, plugin authoring, CLI, and dashboard guidance for PostgreSQL. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary
Engine and dashboard traffic now stays on the authoritative PostgreSQL layer across execution, recovery, project discovery, planning sessions, analytics, and shutdown. The dashboard no longer presents a migration notice for a cutover that is already mandatory.
Design decisions
Validation
pnpm test:gatepasses all 478 gate tests.Stack
Related: #2105
Summary by CodeRabbit
.fusion/project.jsonmarker.