Skip to content

fix(FN-7952): migrate bundled plugins to PostgreSQL#2111

Merged
gsxdsm merged 9 commits into
mainfrom
feature/postgres-cutover-plugins
Jul 15, 2026
Merged

fix(FN-7952): migrate bundled plugins to PostgreSQL#2111
gsxdsm merged 9 commits into
mainfrom
feature/postgres-cutover-plugins

Conversation

@gsxdsm

@gsxdsm gsxdsm commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

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

Related: #2105

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.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@gsxdsm, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3ce0b26a-2455-4dad-aafa-c21f81ad9b8e

📥 Commits

Reviewing files that changed from the base of the PR and between e1a34f3 and 997f51c.

📒 Files selected for processing (23)
  • docs/architecture.md
  • docs/multi-project.md
  • docs/postgres-migration-review-2026-07-14.md
  • docs/storage.md
  • packages/core/src/__tests__/postgres/plugin-schema-hook.test.ts
  • packages/core/src/__tests__/postgres/schema-applier.test.ts
  • packages/core/src/__tests__/postgres/sqlite-migrator.test.ts
  • packages/core/src/plugin-store.ts
  • packages/core/src/postgres/plugin-schema-hook.ts
  • packages/core/src/postgres/sqlite-migrator.ts
  • packages/dashboard/app/api/legacy.ts
  • packages/dashboard/app/components/TaskIdIntegrityBanner.tsx
  • packages/dashboard/app/components/dashboard/DashboardBanners.tsx
  • packages/dashboard/src/__tests__/dashboard-postgres-health.test.ts
  • packages/dashboard/src/dashboard-postgres-health.ts
  • packages/dashboard/src/server.ts
  • plugins/fusion-plugin-cli-printing-press/src/__tests__/cli-press-store.pg.test.ts
  • plugins/fusion-plugin-cli-printing-press/src/__tests__/executor-runtime-env.test.ts
  • plugins/fusion-plugin-cli-printing-press/src/runtime/executor-runtime-env.ts
  • plugins/fusion-plugin-cli-printing-press/src/store/cli-press-store.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/pipeline-store.pg.test.ts
  • plugins/fusion-plugin-compound-engineering/src/sync/pg-schema.ts
  • plugins/fusion-plugin-compound-engineering/src/sync/pipeline-store.ts
📝 Walkthrough

Walkthrough

PostgreSQL becomes the runtime persistence authority. Plugin-owned schemas and stores now enforce project isolation, legacy plugin state is migrated into central PostgreSQL tables, plugin schema execution is transactional, and bundled plugins adopt async PostgreSQL persistence with updated tests, tools, and documentation.

Changes

PostgreSQL runtime cutover

Layer / File(s) Summary
Runtime and migration contract
.changeset/*, docs/*, README.md, CONCEPTS.md
Documentation and release metadata define PostgreSQL as authoritative, remove runtime SQLite fallback, and classify legacy SQLite files as migration or recovery inputs.
Plugin schema contracts and migration
packages/core/src/postgres/*, packages/core/src/plugin-*, packages/cli/src/commands/*, packages/desktop/src/*
Plugin schema execution is owned by PluginLoader, serialized transactionally, restricted to approved DDL, and applied with project isolation. Legacy plugin rows migrate into central installation and project-state tables.
CLI Printing Press integration
plugins/fusion-plugin-cli-printing-press/src/*
Stores require project-bound async layers, all PostgreSQL operations include project predicates, runtime environment data loads in batches, and route-backed agent tools are added.
Compound Engineering isolation
plugins/fusion-plugin-compound-engineering/src/*
Pipeline links, state, queues, and sessions use project-bound PostgreSQL async storage with composite project keys and scoped operations.
Even Realities persistence
plugins/fusion-plugin-even-realities-glasses/src/*
Notification snapshots move to chunked PostgreSQL upserts and scoped pruning; notifier polling writes only changed rows and uses an injectable persistence abstraction.
Reports store and tools
plugins/fusion-plugin-reports/src/*
Report access uses a shared cached PostgreSQL provider with project filtering, and report listing, retrieval, and export tools delegate to existing routes.
Roadmap and WhatsApp cutover
plugins/fusion-plugin-roadmap/src/*, plugins/fusion-plugin-whatsapp-chat/src/*
Roadmap migrations use explicit ownership sentinels. WhatsApp removes SQLite persistence and schema hooks in favor of project-bound async persistence for messages, credentials, dedupe, and auth state.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: migrating bundled plugins to PostgreSQL.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/postgres-cutover-plugins

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR moves bundled plugin runtime state to project-scoped PostgreSQL storage. The main changes are:

  • Project-scoped tables and queries for Reports, Printing Press, Compound Engineering, Roadmap, Even Realities, and WhatsApp.
  • Guarded ownership backfills for legacy plugin data.
  • Declarative PostgreSQL schema hooks exposed through the plugin SDK.
  • PostgreSQL-only startup, health checks, and runtime storage.
  • Removal of the Reports decision tool until authenticated tool principals are available.

Confidence Score: 5/5

This looks safe to merge.

  • The updated stores consistently use project-leading keys and query predicates.
  • Legacy ownership upgrades fail closed when ownership is ambiguous and run transactionally.
  • The caller-supplied Reports decision tool path was removed.
  • No blocking issue distinct from the existing review threads was found.

Important Files Changed

Filename Overview
packages/core/src/postgres/plugin-schema-hook.ts Adds transactional plugin schema setup, project ownership enforcement, and guarded legacy backfills.
plugins/fusion-plugin-reports/src/store/report-store.ts Scopes PostgreSQL report reads and mutations by project ID.
plugins/fusion-plugin-reports/src/tools.ts Removes the privileged report decision tool and keeps read-only report tools.
plugins/fusion-plugin-cli-printing-press/src/store/cli-press-store.ts Scopes Printing Press services, specifications, artifacts, credentials, and settings by project.
plugins/fusion-plugin-compound-engineering/src/sync/pipeline-store.ts Scopes pipeline links, state, and synchronization queues by project.

Reviews (7): Last reviewed commit: "fix(FN-7952): fail closed across migrati..." | Re-trigger Greptile

Comment thread plugins/fusion-plugin-reports/src/tools.ts Outdated
Comment thread plugins/fusion-plugin-reports/src/store/report-store-provider.ts
Comment thread plugins/fusion-plugin-cli-printing-press/src/index.ts
Comment thread packages/core/src/postgres/plugin-schema-hook.ts Outdated
@gsxdsm gsxdsm force-pushed the feature/postgres-cutover-cli-desktop branch from 4cf7da9 to bff151c Compare July 15, 2026 05:34
Base automatically changed from feature/postgres-cutover-cli-desktop to main July 15, 2026 06:18
gsxdsm added a commit that referenced this pull request Jul 15, 2026
## 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
gsxdsm added a commit that referenced this pull request Jul 15, 2026
## 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
@gsxdsm gsxdsm force-pushed the feature/postgres-cutover-plugins branch from f43af48 to b8b1f49 Compare July 15, 2026 06:21
gsxdsm added 6 commits July 14, 2026 23:24
Move bundled plugin persistence to project-scoped PostgreSQL contracts and keep plugin SDK/package dependencies aligned with the new runtime.

Fusion-Task-Id: FN-7952
## 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
@gsxdsm gsxdsm force-pushed the feature/postgres-cutover-plugins branch from b8b1f49 to 058e891 Compare July 15, 2026 06:25
Keep PluginLoader as the single schema-contract owner so CLI and desktop hosts do not replay PostgreSQL transactions after onLoad preparation.

Fusion-Task-Id: FN-7952

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
plugins/fusion-plugin-compound-engineering/src/sync/pipeline-store.ts (1)

351-377: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Race condition in async state upsert (TOCTOU).

The conversion from synchronous SQLite to async PostgreSQL introduces a Time-of-Check to Time-of-Use (TOCTOU) race condition here. Because await this.getStateAsync(input.cePipelineId) yields the event loop, concurrent async calls to upsertStateAsync for the same pipeline ID can both see existing as undefined and attempt to insert the same record, crashing the request with a unique constraint violation.

While the read-modify-write pattern was practically safe in the synchronous SQLite path due to avoiding event-loop yields, you can solve this in the async PostgreSQL path by switching to a single atomic INSERT ... ON CONFLICT DO UPDATE using Drizzle, which also allows you to skip the getStateAsync query completely.

🛠️ Proposed fix using Drizzle's `onConflictDoUpdate`

By conditionally applying the updates in the set object, you can preserve the existing row values automatically if the caller omits them:

   async upsertStateAsync(input: UpsertCePipelineStateInput): Promise<CePipelineState> {
     if (!this.asyncLayer) return this.upsertState(input);
     const now = new Date().toISOString();
-    const existing = await this.getStateAsync(input.cePipelineId);
-    const status = input.status ?? existing?.status ?? "running";
-    const lastArtifactPath =
-      input.lastArtifactPath !== undefined ? input.lastArtifactPath : existing?.lastArtifactPath ?? null;
-    if (existing) {
-      await this.dbAsync().update(cePipelineStateTable).set({
-        currentStage: input.currentStage,
-        status,
-        lastArtifactPath,
-        updatedAt: now,
-      }).where(and(eq(cePipelineStateTable.projectId, this.projectId()), eq(cePipelineStateTable.cePipelineId, input.cePipelineId)));
-    } else {
-      await this.dbAsync().insert(cePipelineStateTable).values({
-        projectId: this.projectId(),
-        cePipelineId: input.cePipelineId,
-        currentStage: input.currentStage,
-        status,
-        lastArtifactPath,
-        createdAt: now,
-        updatedAt: now,
-      });
-    }
+    
+    await this.dbAsync().insert(cePipelineStateTable).values({
+      projectId: this.projectId(),
+      cePipelineId: input.cePipelineId,
+      currentStage: input.currentStage,
+      status: input.status ?? "running",
+      lastArtifactPath: input.lastArtifactPath ?? null,
+      createdAt: now,
+      updatedAt: now,
+    }).onConflictDoUpdate({
+      // Target your unique constraint/PK combination here
+      target: [cePipelineStateTable.projectId, cePipelineStateTable.cePipelineId],
+      set: {
+        currentStage: input.currentStage,
+        ...(input.status !== undefined ? { status: input.status } : {}),
+        ...(input.lastArtifactPath !== undefined ? { lastArtifactPath: input.lastArtifactPath } : {}),
+        updatedAt: now,
+      }
+    });
+
     return (await this.getStateAsync(input.cePipelineId))!;
   }
🤖 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 `@plugins/fusion-plugin-compound-engineering/src/sync/pipeline-store.ts` around
lines 351 - 377, Replace the read-then-insert/update flow in upsertStateAsync
with a single atomic insert using Drizzle’s onConflictDoUpdate for the projectId
and cePipelineId key. Preserve existing status and lastArtifactPath values when
the corresponding input fields are omitted, while updating supplied fields and
timestamps, then retain the final getStateAsync return.
packages/core/src/postgres/schema/plugin.ts (1)

132-175: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Duplicate Drizzle table definitions for ce_pipeline_links/ce_pipeline_state/ce_pipeline_sync_queue across two files. The same three tables (columns, composite primary keys, foreign keys, indexes) are hand-maintained in both the canonical migration schema and a plugin-local copy, with no shared source of truth enforcing consistency.

  • packages/core/src/postgres/schema/plugin.ts#L132-L175: keep as the canonical definition (owns DDL/migration via the plugin-schema-hook).
  • plugins/fusion-plugin-compound-engineering/src/sync/pg-schema.ts#L20-L63: stop re-declaring cePipelineLinks/cePipelineState/cePipelineSyncQueue locally; import the table objects from packages/core/src/postgres/schema/plugin.ts (as cli-press-store.ts already does for its tables) so pipeline-store.ts's runtime queries can never drift from the migrated schema.
🤖 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/core/src/postgres/schema/plugin.ts` around lines 132 - 175, Keep
cePipelineLinks, cePipelineState, and cePipelineSyncQueue in
packages/core/src/postgres/schema/plugin.ts as the canonical definitions. In
plugins/fusion-plugin-compound-engineering/src/sync/pg-schema.ts, remove the
duplicate local table declarations and import these table objects from the core
plugin schema, preserving pipeline-store.ts runtime query usage and preventing
schema drift.
plugins/fusion-plugin-cli-printing-press/src/store/cli-press-store.ts (1)

724-749: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use an atomic upsert here. The read-then-insert/update sequence can race under concurrent async callers, causing one writer to fail on the unique constraint instead of updating the existing setting.

🤖 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 `@plugins/fusion-plugin-cli-printing-press/src/store/cli-press-store.ts` around
lines 724 - 749, Update setSetting’s asyncLayer database path to use a single
atomic upsert for cliPressSettings instead of selecting first and branching
between update and insert. Preserve the existing conflict key (projectId,
serviceId, key, and scope), values, timestamps, and returned ServiceSetting
behavior while ensuring concurrent callers cannot hit a unique-constraint
failure.
packages/core/src/postgres/plugin-schema-hook.ts (1)

155-161: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Validate Roadmap relationships using both project_id and ID.

These joins count a valid hierarchy as conflicting when two projects reuse the same roadmap or milestone ID. Use composite NOT EXISTS checks so only missing or cross-project parents fail.

Proposed validation
- JOIN project.roadmaps roadmap ON roadmap.id = milestone.roadmap_id
- WHERE milestone.project_id IS DISTINCT FROM roadmap.project_id
+ WHERE NOT EXISTS (
+   SELECT 1 FROM project.roadmaps roadmap
+   WHERE roadmap.project_id = milestone.project_id
+     AND roadmap.id = milestone.roadmap_id
+ )

Apply the equivalent check to features.

🤖 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/core/src/postgres/plugin-schema-hook.ts` around lines 155 - 161,
Update the roadmap relationship validation query to use composite NOT EXISTS
checks rather than ID-only joins. For milestones, require a matching roadmap
with both roadmap.id = milestone.roadmap_id and roadmap.project_id =
milestone.project_id; apply the equivalent ID-and-project check for features
against milestones. Count only missing or cross-project parents as conflicts,
allowing reused IDs across projects.
🧹 Nitpick comments (2)
plugins/fusion-plugin-cli-printing-press/src/runtime/executor-runtime-env.ts (1)

24-32: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Per-dispatch catalog load is unbounded, not just query-count-bounded.

The FNXC comment states this keeps round trips constant "as services and historical specs grow," but listAllSpecs()/listAllArtifacts() still return the full historical set on every dispatch — only status === "generated" specs and executable artifacts are ever used here. As spec/artifact history accumulates per project, this hot path (every dispatched task) will transfer and process an ever-growing payload despite the fixed query count.

Consider pushing the status/executable filters (or a "latest generated spec per service" query) down to SQL in cli-press-store.ts rather than filtering after loading full history into memory.

🤖 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 `@plugins/fusion-plugin-cli-printing-press/src/runtime/executor-runtime-env.ts`
around lines 24 - 32, Update the catalog-loading flow around the Promise.all
call and the store methods it invokes so SQL returns only generated specs and
executable artifacts needed by dispatches, preferably using a
latest-generated-spec-per-service query where required. Replace the
listAllSpecs/listAllArtifacts usage with filtered store APIs, preserve the
existing specsByService and artifactsBySpec grouping behavior, and avoid loading
historical records into memory.
packages/core/src/postgres/plugin-schema-hook.ts (1)

261-296: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Lead shared-table secondary indexes with project_id.

The CE, Reports, and CLI tables are now project-partitioned, but their list/order indexes remain global. Project-scoped reads can consequently scan and filter rows belonging to every project. Rebuild these indexes as (project_id, ...).

Also applies to: 443-470, 563-617

🤖 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/core/src/postgres/plugin-schema-hook.ts` around lines 261 - 296,
Update the CE secondary index definitions in the schema hook, including
idxCePipelineLinksPipeline, idxCePipelineLinksTask, idxCePipelineStateStatus,
idxCePipelineSyncQueuePending, and idxCePipelineSyncQueuePipeline, to lead with
project_id before their existing columns. Apply the same project_id-leading
index change to the corresponding Reports and CLI index definitions in the
referenced sections, preserving each index’s remaining column order.
🤖 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/core/src/__tests__/postgres/plugin-schema-hook.test.ts`:
- Around line 137-139: Remove the duplicate envelope and ddl declarations in the
affected test scopes, retaining only one declaration of each. Simplify the
repeated queryChunks type usage so each type property is declared once,
including the corresponding sections around the additional referenced locations,
while preserving the existing execute mock assertions and envelope extraction.

In `@packages/core/src/plugin-store.ts`:
- Around line 203-212: Update the outdated FNXC comment near the backend
initialization flow to remove the claim that backend mode skips legacy
migration. Align the comment with the migrateLegacyProjectPluginRows bridge,
which now runs during initialization to recover project plugin state without
overwriting newer central changes.

---

Outside diff comments:
In `@packages/core/src/postgres/plugin-schema-hook.ts`:
- Around line 155-161: Update the roadmap relationship validation query to use
composite NOT EXISTS checks rather than ID-only joins. For milestones, require a
matching roadmap with both roadmap.id = milestone.roadmap_id and
roadmap.project_id = milestone.project_id; apply the equivalent ID-and-project
check for features against milestones. Count only missing or cross-project
parents as conflicts, allowing reused IDs across projects.

In `@packages/core/src/postgres/schema/plugin.ts`:
- Around line 132-175: Keep cePipelineLinks, cePipelineState, and
cePipelineSyncQueue in packages/core/src/postgres/schema/plugin.ts as the
canonical definitions. In
plugins/fusion-plugin-compound-engineering/src/sync/pg-schema.ts, remove the
duplicate local table declarations and import these table objects from the core
plugin schema, preserving pipeline-store.ts runtime query usage and preventing
schema drift.

In `@plugins/fusion-plugin-cli-printing-press/src/store/cli-press-store.ts`:
- Around line 724-749: Update setSetting’s asyncLayer database path to use a
single atomic upsert for cliPressSettings instead of selecting first and
branching between update and insert. Preserve the existing conflict key
(projectId, serviceId, key, and scope), values, timestamps, and returned
ServiceSetting behavior while ensuring concurrent callers cannot hit a
unique-constraint failure.

In `@plugins/fusion-plugin-compound-engineering/src/sync/pipeline-store.ts`:
- Around line 351-377: Replace the read-then-insert/update flow in
upsertStateAsync with a single atomic insert using Drizzle’s onConflictDoUpdate
for the projectId and cePipelineId key. Preserve existing status and
lastArtifactPath values when the corresponding input fields are omitted, while
updating supplied fields and timestamps, then retain the final getStateAsync
return.

---

Nitpick comments:
In `@packages/core/src/postgres/plugin-schema-hook.ts`:
- Around line 261-296: Update the CE secondary index definitions in the schema
hook, including idxCePipelineLinksPipeline, idxCePipelineLinksTask,
idxCePipelineStateStatus, idxCePipelineSyncQueuePending, and
idxCePipelineSyncQueuePipeline, to lead with project_id before their existing
columns. Apply the same project_id-leading index change to the corresponding
Reports and CLI index definitions in the referenced sections, preserving each
index’s remaining column order.

In
`@plugins/fusion-plugin-cli-printing-press/src/runtime/executor-runtime-env.ts`:
- Around line 24-32: Update the catalog-loading flow around the Promise.all call
and the store methods it invokes so SQL returns only generated specs and
executable artifacts needed by dispatches, preferably using a
latest-generated-spec-per-service query where required. Replace the
listAllSpecs/listAllArtifacts usage with filtered store APIs, preserve the
existing specsByService and artifactsBySpec grouping behavior, and avoid loading
historical records into memory.
🪄 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: 3a047b39-12a6-4688-8882-f764a3dad623

📥 Commits

Reviewing files that changed from the base of the PR and between 97172fd and e1a34f3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (82)
  • .changeset/finish-postgres-runtime-cutover.md
  • .changeset/isolate-postgres-plugin-state.md
  • CONCEPTS.md
  • README.md
  • docs/PLUGIN_AUTHORING.md
  • docs/README.md
  • docs/architecture.md
  • docs/cli-reference.md
  • docs/dashboard-guide.md
  • docs/dashboard-realtime.md
  • docs/multi-project-sequencing.md
  • docs/multi-project.md
  • docs/performance/dashboard-load.md
  • docs/plugin-management.md
  • docs/postgres-migration-review-2026-06-26.md
  • docs/postgres-migration-review-2026-07-14.md
  • docs/research.md
  • docs/sandbox.md
  • docs/secrets.md
  • docs/settings-reference.md
  • docs/storage.md
  • docs/task-management.md
  • docs/todo-view.md
  • packages/cli/src/commands/dashboard.ts
  • packages/cli/src/commands/db.ts
  • packages/cli/src/commands/serve.ts
  • packages/core/src/__tests__/postgres/plugin-schema-hook.test.ts
  • packages/core/src/__tests__/postgres/sqlite-migrator.test.ts
  • packages/core/src/plugin-store.ts
  • packages/core/src/plugin-types.ts
  • packages/core/src/postgres/plugin-schema-hook.ts
  • packages/core/src/postgres/schema/plugin.ts
  • packages/core/src/postgres/sqlite-migrator.ts
  • packages/core/src/postgres/startup-factory.ts
  • packages/desktop/src/__tests__/local-runtime.test.ts
  • packages/desktop/src/__tests__/local-server.test.ts
  • packages/desktop/src/local-runtime.ts
  • packages/desktop/src/local-server.ts
  • packages/plugin-sdk/src/index.ts
  • plugins/fusion-plugin-cli-printing-press/src/__tests__/cli-press-store.pg.test.ts
  • plugins/fusion-plugin-cli-printing-press/src/__tests__/executor-runtime-env.test.ts
  • plugins/fusion-plugin-cli-printing-press/src/__tests__/tools.test.ts
  • plugins/fusion-plugin-cli-printing-press/src/index.ts
  • plugins/fusion-plugin-cli-printing-press/src/routes/wizard-routes.ts
  • plugins/fusion-plugin-cli-printing-press/src/runtime/executor-runtime-env.ts
  • plugins/fusion-plugin-cli-printing-press/src/store/cli-press-store.ts
  • plugins/fusion-plugin-cli-printing-press/src/tools.ts
  • plugins/fusion-plugin-compound-engineering/src/__tests__/pipeline-store.pg.test.ts
  • plugins/fusion-plugin-compound-engineering/src/index.ts
  • plugins/fusion-plugin-compound-engineering/src/schema.ts
  • plugins/fusion-plugin-compound-engineering/src/session/session-store.ts
  • plugins/fusion-plugin-compound-engineering/src/sync/pg-schema.ts
  • plugins/fusion-plugin-compound-engineering/src/sync/pipeline-store.ts
  • plugins/fusion-plugin-even-realities-glasses/package.json
  • plugins/fusion-plugin-even-realities-glasses/src/__tests__/index.test.ts
  • plugins/fusion-plugin-even-realities-glasses/src/__tests__/notification-store.pg.test.ts
  • plugins/fusion-plugin-even-realities-glasses/src/__tests__/notification-store.test.ts
  • plugins/fusion-plugin-even-realities-glasses/src/__tests__/notifier.test.ts
  • plugins/fusion-plugin-even-realities-glasses/src/index.ts
  • plugins/fusion-plugin-even-realities-glasses/src/notifications/store.ts
  • plugins/fusion-plugin-even-realities-glasses/src/notifier.ts
  • plugins/fusion-plugin-even-realities-glasses/tsconfig.json
  • plugins/fusion-plugin-reports/README.md
  • plugins/fusion-plugin-reports/src/__tests__/report-store-provider.test.ts
  • plugins/fusion-plugin-reports/src/__tests__/report-store.pg.test.ts
  • plugins/fusion-plugin-reports/src/__tests__/tools.test.ts
  • plugins/fusion-plugin-reports/src/index.ts
  • plugins/fusion-plugin-reports/src/routes/report-approval-routes.ts
  • plugins/fusion-plugin-reports/src/routes/report-export-routes.ts
  • plugins/fusion-plugin-reports/src/routes/report-list-routes.ts
  • plugins/fusion-plugin-reports/src/store/report-store-provider.ts
  • plugins/fusion-plugin-reports/src/store/report-store.ts
  • plugins/fusion-plugin-reports/src/tools.ts
  • plugins/fusion-plugin-roadmap/src/__tests__/roadmap-store.pg.test.ts
  • plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.ts
  • plugins/fusion-plugin-whatsapp-chat/src/__tests__/auth-state.test.ts
  • plugins/fusion-plugin-whatsapp-chat/src/__tests__/connection.test.ts
  • plugins/fusion-plugin-whatsapp-chat/src/__tests__/index.test.ts
  • plugins/fusion-plugin-whatsapp-chat/src/__tests__/persistence.pg.test.ts
  • plugins/fusion-plugin-whatsapp-chat/src/auth-state.ts
  • plugins/fusion-plugin-whatsapp-chat/src/index.ts
  • plugins/fusion-plugin-whatsapp-chat/src/persistence.ts

Comment thread packages/core/src/__tests__/postgres/plugin-schema-hook.test.ts
Comment thread packages/core/src/plugin-store.ts
gsxdsm added 2 commits July 15, 2026 00:16
Make plugin schema application idempotent, enforce tenant-leading indexes, remove duplicate test declarations, and make concurrent plugin writes atomic.

Fusion-Task-Id: FN-7952
Keep dry runs non-mutating, make retained plugin SQLite a one-time input, and derive dashboard health and compaction from the live PostgreSQL layer.

Fusion-Task-Id: FN-7952
@gsxdsm

gsxdsm commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the latest CodeRabbit review in deeb2dcdd and 997f51ccf:

  • removed the duplicated envelope/ddl declarations (now one envelope, one ddl, one separately named indexDdl)
  • updated the stale backend-mode FNXC contract
  • made Roadmap relationship validation composite-project aware
  • reconciled bundled-plugin secondary indexes to project_id-leading definitions without steady-state OID churn
  • made CE state and CLI Press setting writes atomic under concurrency
  • filtered executor runtime catalog reads in SQL
  • added an always-running CE local/core schema parity contract; direct canonical import is intentionally blocked by the published CLI minimal @fusion/core shim
  • fixed production PostgreSQL health/compact layer derivation, non-mutating dry-run, and one-time retained-SQLite plugin import

Verification: pnpm lint passed; all relevant typechecks passed; focused real-PG concurrency/migration/OID tests passed; pnpm test:gate passed (294 engine + 122 PostgreSQL + 63 CI-shape). PR remains below the limit at 90 files.

@gsxdsm gsxdsm merged commit a242f1b into main Jul 15, 2026
7 checks passed
@gsxdsm gsxdsm deleted the feature/postgres-cutover-plugins branch July 15, 2026 07:28
gsxdsm added a commit that referenced this pull request Jul 15, 2026
## 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 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant