Skip to content

fix(FN-7952): establish PostgreSQL core authority#2108

Merged
gsxdsm merged 8 commits into
mainfrom
feature/postgres-cutover-stack
Jul 15, 2026
Merged

fix(FN-7952): establish PostgreSQL core authority#2108
gsxdsm merged 8 commits into
mainfrom
feature/postgres-cutover-stack

Conversation

@gsxdsm

@gsxdsm gsxdsm commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fusion’s core runtime now treats PostgreSQL as the authoritative metadata store without leaving current CLI, dashboard, desktop, or engine composition roots uncompilable between stack layers. This is the 99-file foundation for the larger cutover: subsequent PRs migrate the remaining consumers, plugins, and operator surfaces.

Design decisions

  • Runtime store construction fails closed when an asynchronous PostgreSQL layer is unavailable; SQLite remains readable only at explicit migration and identity-recovery boundaries.
  • Project ownership is enforced across active, archived, workflow, mission, analytics, and plugin-schema data.
  • The small set of cross-package files in this layer are compatibility-critical call sites required for a green intermediate commit, not the complete consumer migration.
  • Schema migration 0008 remains assigned to session-advisor state from current main; mission lineage idempotency advances to 0009 so neither invariant can be skipped.

Validation

  • All affected package typechecks pass: Core, Engine, Dashboard, CLI, and Desktop.
  • pnpm test:gate passes: 478 tests across the engine gate, PostgreSQL core gate, and CLI workflow shape.
  • The PR changes exactly 99 files.

Stack

This is the base PR. Engine/dashboard, CLI/desktop/ops, plugins, and docs/release follow as stacked PRs, each below 100 changed files.

Related: #2105

Summary by CodeRabbit

  • New Features

    • PostgreSQL is now the standard runtime backend, with embedded PostgreSQL enabled by default.
    • Added project-scoped storage for tasks, archives, chat sessions, missions, knowledge pages, and operational data.
    • Improved archived-task search, filtering, pagination, and restoration.
    • Added safer plugin schema initialization with validation and project isolation.
    • Added PostgreSQL-backed workflow, mission, validator, and dashboard capabilities.
  • Bug Fixes

    • Improved startup timeout cancellation and resource cleanup.
    • Prevented cross-project data access and phantom reservation cleanup errors.
    • Ensured archived tasks remain read-only and asynchronous writes complete reliably.
    • Retired SQLite opt-out settings with clear startup errors.

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
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

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

Next review available in: 10 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: d955d4dc-34c1-441c-8e64-c271dc53cfa4

📥 Commits

Reviewing files that changed from the base of the PR and between 54e6e2f and 84e48e7.

📒 Files selected for processing (24)
  • packages/cli/src/commands/daemon.ts
  • packages/cli/src/commands/dashboard.ts
  • packages/cli/src/commands/serve.ts
  • packages/core/src/__tests__/postgres/archive-project-isolation.pg.test.ts
  • packages/core/src/__tests__/postgres/mission-store.pg.test.ts
  • packages/core/src/__tests__/postgres/operational-maintenance.pg.test.ts
  • packages/core/src/__tests__/postgres/startup-factory-integration.test.ts
  • packages/core/src/__tests__/postgres/store-archive-reads.pg.test.ts
  • packages/core/src/__tests__/store-phantom-reservation-reconcile.test.ts
  • packages/core/src/async-knowledge.ts
  • packages/core/src/async-mission-store-queries.ts
  • packages/core/src/postgres/migration-stamping.ts
  • packages/core/src/postgres/schema/project.ts
  • packages/core/src/task-store/archive-lifecycle-2.ts
  • packages/core/src/task-store/async-archive-lineage.ts
  • packages/core/src/task-store/async-comments-attachments.ts
  • packages/core/src/task-store/async-lifecycle.ts
  • packages/core/src/task-store/async-maintenance.ts
  • packages/core/src/task-store/async-phantom-reservations.ts
  • packages/core/src/task-store/audit-ops.ts
  • packages/core/src/task-store/comments-ops.ts
  • packages/core/src/task-store/remaining-ops-7.ts
  • packages/engine/src/__tests__/plugin-runner.test.ts
  • packages/engine/src/plugin-runner.ts
📝 Walkthrough

Walkthrough

The PR makes PostgreSQL the required runtime backend, adds project-scoped persistence and plugin schema contracts, migrates chat, session, mission, archive, and dashboard stores to async APIs, and centralizes startup and shutdown ownership across CLI, desktop, dashboard, and engine runtimes.

Changes

PostgreSQL backend cutover

Layer / File(s) Summary
Backend startup and ownership
packages/core/src/postgres/startup-factory.ts, packages/core/src/central-core.ts, packages/cli/src/commands/serve.ts, packages/cli/src/commands/dashboard.ts
Backend factories now return live PostgreSQL stores or throw; shared layers, CentralCore ownership, startup failure cleanup, and shutdown sequencing are explicit and asynchronous.
Project identity and migration compatibility
packages/core/src/project-identity.ts, packages/core/src/migration.ts, packages/core/src/project-root-guard.ts, packages/core/src/sqlite-validation.ts
project.json becomes the primary identity marker, legacy SQLite reads are read-only, and discovery accepts both current markers and legacy databases while runtime legacy mode is removed.
Project-scoped task and archive operations
packages/core/src/task-store/*, packages/core/src/async-archive-db.ts
Task, archive, lineage, soft-delete, search, pagination, comments, documents, artifacts, and maintenance queries apply PostgreSQL project partitions and authoritative archived-state handling.
Async storage consumers
packages/core/src/chat-store.ts, packages/core/src/cli-session-store.ts, packages/dashboard/src/*, packages/engine/src/*
Chat, CLI sessions, AI sessions, mission stores, schedulers, executors, and runtime collaborators use async layers and await persistence, claims, audit reads, and cleanup operations.

Plugin schemas and migrations

Layer / File(s) Summary
Plugin schema contracts
packages/core/src/plugin-types.ts, packages/core/src/plugin-loader.ts, packages/core/src/postgres/plugin-schema-hook.ts, packages/core/src/store.ts
Plugins can declare PostgreSQL schema definitions; loading performs preflight and initialization before startup, validates third-party DDL, tracks contracts through reloads, and rejects unsupported legacy-only hooks.
PostgreSQL schema updates
packages/core/src/postgres/schema/*, packages/core/src/postgres/schema-applier.ts, packages/core/src/postgres/migrations/0009_mission_fix_idempotency.sql
The Even Realities table and project-scoped knowledge-page uniqueness are added, and migration 0009 installs mission-fix lineage idempotency protection.

Mission and lifecycle behavior

Layer / File(s) Summary
Mission persistence and async execution
packages/core/src/async-mission-store-queries.ts, packages/core/src/async-mission-store.ts, packages/engine/src/mission-execution-loop.ts, packages/engine/src/scheduler.ts
Mission CRUD, validator runs, generated fixes, assertions, lineage, rollups, and event logging use PostgreSQL query helpers with transactional transitions, bulk reads, idempotency, and async execution paths.
Operational reconciliation
packages/core/src/task-store/async-maintenance.ts, packages/core/src/task-store/async-phantom-reservations.ts, packages/engine/src/self-healing.ts
Operational-log pruning, phantom reservation reconciliation, soft-delete drift repair, and stale branch cleanup are implemented through project-scoped asynchronous routines.

Validation coverage

Layer / File(s) Summary
PostgreSQL integration tests
packages/core/src/__tests__/postgres/*
Tests cover archive isolation, archived reads, CLI-session durability, mission concurrency and idempotency, plugin schemas, migrations, operational retention, workflow authority, project discovery, and safe-default behavior.
Lifecycle and API tests
packages/core/src/__tests__/*
Planner intervention tests now use asynchronous audit reads, embedded PostgreSQL timeout cancellation is tested, project identity writes use project.json, and SQLite validation verifies no file mutation.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: making PostgreSQL the authoritative core runtime store.
Docstring Coverage ✅ Passed Docstring coverage is 83.46% which is sufficient. The required threshold is 80.00%.
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-stack

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 makes PostgreSQL the authoritative metadata store across Fusion. The main changes are:

  • PostgreSQL-backed project, task, archive, workflow, mission, analytics, and plugin-schema storage.
  • Project-aware ownership and composite isolation across shared data.
  • Fail-closed PostgreSQL startup for CLI, dashboard, desktop, and engine runtimes.
  • Updated shutdown ordering and embedded PostgreSQL lifecycle handling.
  • Expanded PostgreSQL migration and integration coverage.

Confidence Score: 5/5

The latest mission isolation changes look safe to merge.

  • Mission-owned queries now use the authoritative project partition.
  • Composite project keys protect duplicate mission identifiers across projects.
  • No separate blocking issue qualifies for this follow-up review.

Important Files Changed

Filename Overview
packages/core/src/async-mission-store-queries.ts Adds PostgreSQL mission query helpers with session-based project scoping.
packages/core/src/async-mission-store.ts Moves mission runtime operations to the asynchronous PostgreSQL layer.
packages/core/src/postgres/schema/project.ts Adds project-aware keys and relationships across PostgreSQL project data.
packages/core/src/tests/postgres/mission-store.pg.test.ts Adds mission isolation, lifecycle, concurrency, and idempotency coverage.

Reviews (3): Last reviewed commit: "test(FN-7952): align plugin schema owner..." | Re-trigger Greptile

Comment thread packages/core/src/async-mission-store-queries.ts

@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: 14

Caution

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

⚠️ Outside diff range comments (9)
packages/engine/src/plugin-runner.ts (1)

242-254: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Update plugin-runner.test.ts for the direct runPluginSchemaInits call packages/engine/src/__tests__/plugin-runner.test.ts still expects mockTaskStore.getDatabase() and db.runPluginSchemaInits(...); this needs to switch to mockTaskStore.runPluginSchemaInits(...) or the test will break.

🤖 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/engine/src/plugin-runner.ts` around lines 242 - 254, The
plugin-runner tests still assert the old database-level schema initialization
path. Update the relevant test setup and expectations in plugin-runner.test.ts
to mock and verify mockTaskStore.runPluginSchemaInits(schemaInitHooks) directly,
removing assertions against getDatabase() and db.runPluginSchemaInits(...).
packages/dashboard/src/server.ts (1)

2790-2858: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Wait for backend shutdown before closing CentralCore evictAllProjectStores() is synchronous, but evictProjectStore() fires backendShutdown() and does not await it. That means await evictAllProjectStores() only waits for store.close(), not for the embedded PostgreSQL teardown the shutdown order depends on. Return/await the backend shutdown promises here before options?.centralCore?.close().

🤖 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/server.ts` around lines 2790 - 2858, Update
evictAllProjectStores and the underlying evictProjectStore flow so
backendShutdown promises are returned and awaited, ensuring await
evictAllProjectStores() completes PostgreSQL teardown before
options?.centralCore?.close(). Preserve the existing store cleanup behavior
while propagating each asynchronous backend shutdown result through the shutdown
handler.
packages/core/src/task-store/async-archive-lineage.ts (1)

321-363: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep the archive snapshot until the row is restored
deleteArchivedTaskEntry(tx, entry.id, layer.projectId) should not run on the hard-delete path. unarchiveTaskImpl calls this helper before any reconstruction and does not rebuild the task here, so deleting the snapshot first can drop the only remaining copy.

🤖 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/task-store/async-archive-lineage.ts` around lines 321 -
363, Update the transaction in the unarchive helper around
readTaskRowInTransaction and deleteArchivedTaskEntry so the archive snapshot is
deleted only when an existing soft-deleted row is successfully restored.
Preserve the snapshot in the hard-delete else branch, allowing unarchiveTaskImpl
to reconstruct the task from it before removal.
packages/core/src/cli-session-store.ts (1)

119-152: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use the full UUID for CLI session IDs

randomUUID().slice(0, 8) drops this default to 32 bits of entropy. That can collide, overwrite a different in-memory session, and make the queued insert fail against the cli_sessions.id primary key — which leaves subsequent flush() calls failing for unrelated later sessions in the same process.

Proposed fix
-      id: input.id ?? `cli-${randomUUID().slice(0, 8)}`,
+      id: input.id ?? `cli-${randomUUID()}`,
🤖 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/cli-session-store.ts` around lines 119 - 152, Update the
default ID generation in createSession to use the full randomUUID() value
instead of truncating it with slice(0, 8). Preserve caller-provided input.id
values and all other session creation behavior.
packages/core/src/task-store/async-persistence.ts (1)

220-243: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Consider making projectId a required parameter, not optional.

Both softDeleteTaskRowInTransaction and readTaskRowInTransaction fall back to the "__legacy_unscoped__" sentinel when projectId is omitted — a silent, non-throwing degradation rather than a compile-time contract. A sibling file in this same PR (remaining-ops-2.ts's renewCheckoutLeaseImpl) shows exactly this class of gap: a task-row mutation left unscoped by project. Making projectId required here would force the compiler to catch any future caller that forgets to thread it through, which is cheap given the current small number of call sites.

♻️ Proposed signature tightening
 export async function softDeleteTaskRowInTransaction(
   tx: DbTransaction,
   id: string,
   deletedAt: string,
   allowResurrection = false,
-  projectId?: string,
+  projectId: string,
 ): Promise<void> {
   ...
-      eq(schema.project.tasks.projectId, projectId?.trim() || "__legacy_unscoped__"),
+      eq(schema.project.tasks.projectId, projectId.trim() || "__legacy_unscoped__"),
 export async function readTaskRowInTransaction(
   tx: DbTransaction,
   id: string,
   options?: { includeDeleted?: boolean },
-  projectId?: string,
+  projectId: string,
 ): Promise<Record<string, unknown> | undefined> {
   const conditions = [
-    eq(schema.project.tasks.projectId, projectId?.trim() || "__legacy_unscoped__"),
+    eq(schema.project.tasks.projectId, projectId.trim() || "__legacy_unscoped__"),

Also applies to: 293-311

🤖 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/task-store/async-persistence.ts` around lines 220 - 243,
Make projectId a required parameter in softDeleteTaskRowInTransaction and the
corresponding readTaskRowInTransaction function, removing the optional fallback
to "__legacy_unscoped__". Update all callers to pass the owning project ID so
both task-row operations always use the composite projectId and id predicate.
packages/core/src/task-store/remaining-ops-2.ts (1)

497-517: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Scope the checkout-lease update by projectId. The transaction already reads the row with layer.projectId, but the UPDATE only filters by id/deletedAt. project.tasks is project-partitioned (PRIMARY KEY (projectId, id)), so this can stamp checkout-lease fields on same-id tasks from other projects. Add the same project predicate used elsewhere in task-store updates.

🤖 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/task-store/remaining-ops-2.ts` around lines 497 - 517, The
UPDATE in the backend transaction within the checkout-lease update flow must be
scoped to the current project. Extend its WHERE predicate to include the
projectId associated with layer.projectId, alongside the existing taskId and
non-deleted conditions, so only the matching project’s task is updated.
packages/core/src/central-core.ts (1)

311-345: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Concurrent init() calls on a layer-less instance can leak a backend connection pool.

The layer-less branch now does real async work (creating a PostgreSQL connection pool / embedded-postgres bootstrap) before setting this.initialized = true. The if (this.initialized) return; guard only protects re-entrant calls made after a prior call finished — it does not protect against two overlapping in-flight calls, since both will read initialized === false, both will call createCentralBackendLayer(...), and whichever completes last simply overwrites ownedBackendShutdown/ownedBackendReleaseConnections, silently orphaning the other backend's pool (and, if embeddedPgRequested applies, its embedded-postgres process) with no way for close() to ever release it. The docstring's "Idempotent — safe to call multiple times" claim doesn't hold under concurrency.

🔒️ Proposed fix: memoize the in-flight init promise
+  private initPromise: Promise<void> | null = null;
+
   async init(): Promise<void> {
     if (this.initialized) return;
+    if (this.initPromise) {
+      return this.initPromise;
+    }
+    this.initPromise = this.doInit();
+    try {
+      await this.initPromise;
+    } finally {
+      this.initPromise = null;
+    }
+  }
+
+  private async doInit(): Promise<void> {
     if (this.backendMode) {
       await asyncCentralCore.ensureBackendBootstrap(this.asyncLayer!);
       this.initialized = true;
       return;
     }
     ... (rest of existing body)
🤖 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/central-core.ts` around lines 311 - 345, Make
CentralCore.init concurrency-safe by memoizing the in-flight initialization
promise, rather than relying only on initialized. Ensure overlapping calls await
the same initialization work and only one layer-less backend is created and
assigned to ownedBackendShutdown/ownedBackendReleaseConnections; clear the
memoized promise after completion as appropriate while preserving the existing
backend bootstrap and failure cleanup behavior.
packages/core/src/task-store/remaining-ops-4.ts (1)

111-157: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Scope the UPDATE by project_id too

packages/core/src/task-store/remaining-ops-4.ts still updates schema.project.tasks with where(eq(schema.project.tasks.id, id)) only. packages/core/src/postgres/schema/project.ts defines tasks with a composite primary key on (projectId, id), so this can write the wrong row when IDs overlap across projects.

🤖 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/task-store/remaining-ops-4.ts` around lines 111 - 157, The
UPDATE in the transaction callback around readTaskRowInTransaction must be
scoped to both task identity and project partition. Extend the where condition
in the tx.update call to match schema.project.tasks.projectId with
layer.projectId alongside the existing id predicate, preserving the
changed-column update behavior.
packages/engine/src/scheduler.ts (1)

708-730: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don’t swallow dependency read failures as “missing”
this.store.getTask(dependencyId).catch(() => null) makes read failures indistinguishable from absent/soft-deleted dependencies, and getUnmetSchedulingDependencies() then treats them as satisfied. Split “not found” from real store errors in both reconciliation handlers so a temporary read failure can’t unblock dependents early.

  • packages/engine/src/scheduler.ts#L708-L730
  • packages/engine/src/scheduler.ts#L876-L895
🤖 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/engine/src/scheduler.ts` around lines 708 - 730, Update both
dependency reconciliation handlers in packages/engine/src/scheduler.ts at lines
708-730 and 876-895: change the getTask handling so only an explicit not-found
or soft-deleted result is treated as absent, while other store read errors are
propagated or preserved as unresolved. Ensure getUnmetSchedulingDependencies
cannot treat a temporary dependency read failure as satisfied; both sites
require the same distinction.
🧹 Nitpick comments (8)
packages/engine/src/mission-execution-loop.ts (1)

323-327: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

refreshedById.get(feature.id) is a no-op fallback here — it always resolves to the exact same object as feature.

refreshedById is built directly from refreshedFeatures (new Map(refreshedFeatures.map((f) => [f.id, f]))), and the enclosing loop is for (const feature of refreshedFeatures). So refreshedById.get(feature.id) ?? feature can never differ from feature itself within this same pass — the lookup doesn't pick up any state that a prior iteration of this same loop may have mutated (e.g. via transitionLoopState/processTaskOutcome on an earlier feature). This doesn't break anything today since feature is already the freshest value the map has, but it doesn't provide the "coherent snapshot re-check" protection the accompanying comment implies for cross-feature effects within a single recovery pass.

Also applies to: 388-414

🤖 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/engine/src/mission-execution-loop.ts` around lines 323 - 327,
Replace the ineffective refreshedById lookup in the recovery loop around feature
and the corresponding logic at the later recovery block with a lookup that can
observe state changes caused by earlier iterations, or otherwise explicitly
re-read the current feature state before processing. Ensure cross-feature
effects from transitionLoopState/processTaskOutcome are reflected during the
same recovery pass, rather than falling back to the unchanged feature object.
packages/core/src/async-mission-store.ts (1)

1359-1437: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

seedContractAssertionsForFeatures has no cross-call concurrency guard, unlike createGeneratedFixFeature.

Duplicate detection here is computed from a pre-transaction read (listLinkedAssertionsForFeatures) and only dedups within the same batch. Two concurrent calls seeding overlapping assertions for the same feature/milestone can each pass the "already exists" check before the other commits, inserting true duplicate assertion rows — nothing here uses onConflictDoNothing/a unique constraint/row lock the way createGeneratedFixFeature does with .for("update"). The mission-store.pg.test.ts idempotency test only covers two sequential calls, so this gap isn't caught by the current suite.

If concurrent seeding for the same milestone is a realistic call pattern (e.g. multiple triage/planning workers), consider locking the milestone row (or the target features) for the duration of the insert, mirroring the pattern already used in createGeneratedFixFeature.

🤖 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/async-mission-store.ts` around lines 1359 - 1437, Update
seedContractAssertionsForFeatures to serialize overlapping seed operations by
acquiring row-level locks on the affected milestone rows within the transaction
before inserting assertions and links, mirroring the locking approach used by
createGeneratedFixFeature. Ensure duplicate detection and insertion occur under
the same lock so concurrent calls for the same milestone cannot create duplicate
assertion rows.
packages/core/src/async-mission-store-queries.ts (1)

1038-1043: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

getMaxEventSeq + insert is a classic race for concurrent mission-event writers.

getMaxEventSeq reads max(seq) with no row lock; its only caller (logMissionEvent in async-mission-store.ts) computes maxSeq + 1 and inserts inside a transactionImmediate. Under the documented READ COMMITTED default isolation, two concurrent logMissionEvent calls for the same (or even different) missions can both read the same max before either commits, producing duplicate seq values. Since listMissionEvents/getMissionEventsPage order by desc(seq), desc(id), a collision means two chronologically-distinct events can sort in an effectively arbitrary order relative to each other.

Consider a Postgres SEQUENCE/IDENTITY column for seq, or SELECT ... FOR UPDATE on a per-mission counter row, instead of max()+1.

🤖 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/async-mission-store-queries.ts` around lines 1038 - 1043,
Replace the max(seq)+1 allocation used by getMaxEventSeq and logMissionEvent
with a concurrency-safe sequence or per-mission counter-row mechanism; ensure
each inserted mission event receives a unique, monotonically increasing seq
without relying on an unlocked aggregate read. Update the schema and insert path
consistently, preserving existing event ordering behavior.
packages/core/src/__tests__/postgres/store-archive-reads.pg.test.ts (1)

91-123: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Search order should follow the ranking contract. This page assertion is coupled to ts_rank DESC + createdAt ASC and the live/archive merge; if that exact order matters, derive the expected ids from the sort key instead of hard-coding a fragile sequence.

🤖 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/__tests__/postgres/store-archive-reads.pg.test.ts` around
lines 91 - 123, Update the searchTasks assertion in “keeps globally ordered
pages exact across multiple live/cold boundaries” to derive expected IDs using
the documented ts_rank DESC, then createdAt ASC ordering and the live/archive
merge, rather than hard-coding the sequence. Preserve the pagination parameters
and verify the returned page matches the contract-driven sort order.
packages/core/src/chat-store.ts (1)

622-635: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Raw-SQL insert vs. query-builder select for the same table.

recordTokenUsage hand-writes column names in a raw sql INSERT while listTokenUsageAsync (just below) uses the typed query builder against schema.project.chatTokenUsage for the same table. Using db.insert(schema.project.chatTokenUsage).values(record) here would remove the manual column-name/order duplication and get compile-time checking against schema changes.

🤖 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/chat-store.ts` around lines 622 - 635, Update
recordTokenUsage to insert through the typed query builder using
schema.project.chatTokenUsage and the existing record values instead of the
hand-written raw SQL INSERT. Preserve the awaited database operation and return
record after the insert, while removing the duplicated table and column
definitions.
packages/core/src/task-store/async-maintenance.ts (1)

18-80: 🚀 Performance & Scalability | 🔵 Trivial

Operational note: confirm indexes support these retention deletes.

runAuditEvents and agentHeartbeats delete via subqueries against project.tasks/project.agents, and agentConfigRevisions uses a window function over the whole per-project revision set. On large installs, running this periodically without indexes on project_id/timestamp/task_id/agent_id could produce long-running deletes that lock rows under concurrent writers. Worth confirming the underlying schema migrations already index these columns.

🤖 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/task-store/async-maintenance.ts` around lines 18 - 80,
Confirm the schema migrations provide indexes supporting the retention queries
in pruneOperationalLogsAsync: run_audit_events should cover task_id and
timestamp, agent_heartbeats should cover project_id, agent_id, and timestamp,
and agent_config_revisions should support project_id with the revision
ordering/filter columns. Add only missing migration indexes, preserving existing
schema conventions.
packages/engine/src/executor.ts (1)

17623-17623: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Isolate the best-effort execution to prevent misleading error logs.

If clearStaleExecutionStartBranchReferences throws an error, the outer catch block will log failed to delete conflicting branch, which is misleading because the branch was already successfully deleted. Consider wrapping it in a try...catch block, similar to how it is handled in cleanupStaleBranch and the dep-abort cleanup path.

♻️ Proposed refactor
-        await this.store.clearStaleExecutionStartBranchReferences([branch], taskId);
+        try { await this.store.clearStaleExecutionStartBranchReferences([branch], taskId); } catch { /* best-effort */ }
🤖 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/engine/src/executor.ts` at line 17623, Wrap the best-effort call to
clearStaleExecutionStartBranchReferences in its own try/catch, following the
handling used by cleanupStaleBranch and the dep-abort cleanup path. Prevent any
cleanup error from reaching the outer catch that reports failed to delete
conflicting branch, while preserving the successful branch deletion flow.
packages/engine/src/scheduler.ts (1)

1450-1460: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Per-tick sequential mission-store round trips in the scheduling hot path.

For each distinct blocked sliceId among todo tasks, this now performs up to three sequential awaited network calls (getSlicegetMilestonegetMission) against PostgreSQL instead of the previous in-memory/local-SQLite lookups. It's deduplicated per sliceId and guarded by try/catch, but on boards with many distinct mission slices this adds latency to every scheduling pass (default poll interval 15s).

Given this PR is explicitly described as an intermediate cutover step, this is likely acceptable for now, but consider batching these lookups (e.g. a single getSlicesWithHierarchy call) in a follow-up once the mission-store async API surface stabilizes.

🤖 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/engine/src/scheduler.ts` around lines 1450 - 1460, Leave the current
sequential mission-store lookups in the todo scheduling loop unchanged for this
intermediate cutover; track a follow-up optimization around the blockedSliceIds
logic to batch slice, milestone, and mission retrieval through a single
hierarchy lookup once the mission-store async API stabilizes.
🤖 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/cli/src/commands/daemon.ts`:
- Around line 551-561: Remove the post-load batch call to
store.runPluginSchemaInits(schemaHooks) from the daemon flow after
loadAllPlugins(), including its now-unneeded surrounding error-handling block.
Also remove the equivalent redundant schema initialization in the plugin-runner
flow after its own loadAllPlugins() call, while preserving per-plugin
initialization performed by pluginLoader.loadPlugin().

In `@packages/cli/src/commands/dashboard.ts`:
- Line 1167: Update disposeAsync() to explicitly await store.close() during
teardown, since closeProjectStores() excludes the current workspace TaskStore.
Add this to the existing disposal chain so the cwd store’s watcher and timers
are always shut down through the exported dispose() path.

In `@packages/cli/src/commands/serve.ts`:
- Around line 617-628: Ensure failures from
store.runPluginSchemaInits(schemaHooks) propagate beyond the surrounding
plugin-load catch so serve startup fails closed. Update the enclosing
plugin-loading error flow around schemaHooks and its outer catch to preserve the
specific schema error while rethrowing it, rather than logging "[plugins] Failed
to load plugins" and continuing startup; leave non-fatal plugin-load handling
unchanged.

In `@packages/core/src/async-knowledge.ts`:
- Around line 117-134: Update queryKnowledgePagesInPostgres to use
case-insensitive matching consistent with the sibling PostgreSQL search paths,
and escape each term’s literal wildcard characters before building the pattern.
Preserve the existing project, sourceKind, ordering, limit, and mapping behavior
while replacing the unescaped like predicate construction.

In `@packages/core/src/central-core.ts`:
- Around line 356-389: Update the daemon shutdown sequence to call the store’s
close method before centralCore.close(), ensuring the store remains available
while CentralCore.markLocalNodeOffline() persists the terminal state. Preserve
the existing shutdown behavior after reordering these calls.

In `@packages/core/src/postgres/migration-stamping.ts`:
- Around line 192-226: Wrap the entire check-then-update flow in
stampMigratedProjectRows, including the fusion_sqlite_migrations ownership query
and all three partition UPDATEs, in a single db.transaction(...) callback. Use
the transaction handle for every query so canClaimLegacyQuarantine is evaluated
against the same snapshot used for the writes, matching
rekeyFallbackProjectPartition’s pattern.

In `@packages/core/src/task-store/archive-lifecycle-2.ts`:
- Around line 298-313: The task update query in the unarchive flow duplicates
project partition fallback logic. Reuse the existing projectPartition helper or
introduce the same private helper for this module, and replace the inline
projectId trim-or-sentinel expression in the update where clause while
preserving the current partition behavior.
- Around line 261-274: Update the unarchive flow around restoreTaskFromArchive
so a missing live row is rebuilt from the archive snapshot before that snapshot
is deleted. Ensure the archive entry is only cleared after the restored row is
successfully persisted and retrievable, while preserving the existing path for
already-active live rows.

In `@packages/core/src/task-store/async-archive-lineage.ts`:
- Around line 42-49: Extract the duplicated projectPartition logic into one
shared exported helper, preserving the existing trimming behavior,
"__legacy_unscoped__" sentinel, and FNXC annotation. Update projectPartition
usages in async-archive-lineage.ts, async-lifecycle.ts, and the inline copy in
archive-lifecycle-2.ts to import and reuse that helper, removing the local
definitions.

In `@packages/core/src/task-store/async-comments-attachments.ts`:
- Around line 112-129: Thread projectId through getLiveTaskColumn and every
affected caller in packages/core/src/task-store/async-comments-attachments.ts
(112-129), packages/core/src/task-store/comments-ops.ts (22-27), and
packages/core/src/task-store/remaining-ops-7.ts (626-630). Scope the tasks
lookup by both task id and schema.project.tasks.projectId, preserving the
existing archived, deleted, and missing-task behavior.

In `@packages/core/src/task-store/async-lifecycle.ts`:
- Around line 55-61: Remove the duplicate projectPartition() implementation from
async-lifecycle.ts and reuse the shared helper consolidated from
async-archive-lineage.ts, preserving the existing trimmed project ID behavior
and "__legacy_unscoped__" fallback for unbound callers.

In `@packages/core/src/task-store/async-maintenance.ts`:
- Line 26: Update the projectId handling in the async maintenance flow to log a
warning when layer.projectId is missing or blank before using the
"__legacy_unscoped__" fallback. Preserve the existing safe sentinel behavior and
retention-delete logic, but make the resulting no-op observable.
- Line 74: The usage event deletion in the maintenance flow uses an inconsistent
key for deletedByTable; update the count call around the usage_events DELETE to
use the existing camelCase key convention, matching activityLog, runAuditEvents,
agentHeartbeats, agentRuns, and agentConfigRevisions.

In `@packages/core/src/task-store/async-phantom-reservations.ts`:
- Around line 130-156: Update the reconciliation loop around
store.recordRunAuditEvent so an audit-write failure is handled per taskId rather
than by the surrounding batch catch. Preserve successfully completed tasks in
result.reconciled, and add only the task whose audit/reconciliation fails to
result.skipped with the existing error reason format; avoid reprocessing all
filesystemApproved ids after one failure.

---

Outside diff comments:
In `@packages/core/src/central-core.ts`:
- Around line 311-345: Make CentralCore.init concurrency-safe by memoizing the
in-flight initialization promise, rather than relying only on initialized.
Ensure overlapping calls await the same initialization work and only one
layer-less backend is created and assigned to
ownedBackendShutdown/ownedBackendReleaseConnections; clear the memoized promise
after completion as appropriate while preserving the existing backend bootstrap
and failure cleanup behavior.

In `@packages/core/src/cli-session-store.ts`:
- Around line 119-152: Update the default ID generation in createSession to use
the full randomUUID() value instead of truncating it with slice(0, 8). Preserve
caller-provided input.id values and all other session creation behavior.

In `@packages/core/src/task-store/async-archive-lineage.ts`:
- Around line 321-363: Update the transaction in the unarchive helper around
readTaskRowInTransaction and deleteArchivedTaskEntry so the archive snapshot is
deleted only when an existing soft-deleted row is successfully restored.
Preserve the snapshot in the hard-delete else branch, allowing unarchiveTaskImpl
to reconstruct the task from it before removal.

In `@packages/core/src/task-store/async-persistence.ts`:
- Around line 220-243: Make projectId a required parameter in
softDeleteTaskRowInTransaction and the corresponding readTaskRowInTransaction
function, removing the optional fallback to "__legacy_unscoped__". Update all
callers to pass the owning project ID so both task-row operations always use the
composite projectId and id predicate.

In `@packages/core/src/task-store/remaining-ops-2.ts`:
- Around line 497-517: The UPDATE in the backend transaction within the
checkout-lease update flow must be scoped to the current project. Extend its
WHERE predicate to include the projectId associated with layer.projectId,
alongside the existing taskId and non-deleted conditions, so only the matching
project’s task is updated.

In `@packages/core/src/task-store/remaining-ops-4.ts`:
- Around line 111-157: The UPDATE in the transaction callback around
readTaskRowInTransaction must be scoped to both task identity and project
partition. Extend the where condition in the tx.update call to match
schema.project.tasks.projectId with layer.projectId alongside the existing id
predicate, preserving the changed-column update behavior.

In `@packages/dashboard/src/server.ts`:
- Around line 2790-2858: Update evictAllProjectStores and the underlying
evictProjectStore flow so backendShutdown promises are returned and awaited,
ensuring await evictAllProjectStores() completes PostgreSQL teardown before
options?.centralCore?.close(). Preserve the existing store cleanup behavior
while propagating each asynchronous backend shutdown result through the shutdown
handler.

In `@packages/engine/src/plugin-runner.ts`:
- Around line 242-254: The plugin-runner tests still assert the old
database-level schema initialization path. Update the relevant test setup and
expectations in plugin-runner.test.ts to mock and verify
mockTaskStore.runPluginSchemaInits(schemaInitHooks) directly, removing
assertions against getDatabase() and db.runPluginSchemaInits(...).

In `@packages/engine/src/scheduler.ts`:
- Around line 708-730: Update both dependency reconciliation handlers in
packages/engine/src/scheduler.ts at lines 708-730 and 876-895: change the
getTask handling so only an explicit not-found or soft-deleted result is treated
as absent, while other store read errors are propagated or preserved as
unresolved. Ensure getUnmetSchedulingDependencies cannot treat a temporary
dependency read failure as satisfied; both sites require the same distinction.

---

Nitpick comments:
In `@packages/core/src/__tests__/postgres/store-archive-reads.pg.test.ts`:
- Around line 91-123: Update the searchTasks assertion in “keeps globally
ordered pages exact across multiple live/cold boundaries” to derive expected IDs
using the documented ts_rank DESC, then createdAt ASC ordering and the
live/archive merge, rather than hard-coding the sequence. Preserve the
pagination parameters and verify the returned page matches the contract-driven
sort order.

In `@packages/core/src/async-mission-store-queries.ts`:
- Around line 1038-1043: Replace the max(seq)+1 allocation used by
getMaxEventSeq and logMissionEvent with a concurrency-safe sequence or
per-mission counter-row mechanism; ensure each inserted mission event receives a
unique, monotonically increasing seq without relying on an unlocked aggregate
read. Update the schema and insert path consistently, preserving existing event
ordering behavior.

In `@packages/core/src/async-mission-store.ts`:
- Around line 1359-1437: Update seedContractAssertionsForFeatures to serialize
overlapping seed operations by acquiring row-level locks on the affected
milestone rows within the transaction before inserting assertions and links,
mirroring the locking approach used by createGeneratedFixFeature. Ensure
duplicate detection and insertion occur under the same lock so concurrent calls
for the same milestone cannot create duplicate assertion rows.

In `@packages/core/src/chat-store.ts`:
- Around line 622-635: Update recordTokenUsage to insert through the typed query
builder using schema.project.chatTokenUsage and the existing record values
instead of the hand-written raw SQL INSERT. Preserve the awaited database
operation and return record after the insert, while removing the duplicated
table and column definitions.

In `@packages/core/src/task-store/async-maintenance.ts`:
- Around line 18-80: Confirm the schema migrations provide indexes supporting
the retention queries in pruneOperationalLogsAsync: run_audit_events should
cover task_id and timestamp, agent_heartbeats should cover project_id, agent_id,
and timestamp, and agent_config_revisions should support project_id with the
revision ordering/filter columns. Add only missing migration indexes, preserving
existing schema conventions.

In `@packages/engine/src/executor.ts`:
- Line 17623: Wrap the best-effort call to
clearStaleExecutionStartBranchReferences in its own try/catch, following the
handling used by cleanupStaleBranch and the dep-abort cleanup path. Prevent any
cleanup error from reaching the outer catch that reports failed to delete
conflicting branch, while preserving the successful branch deletion flow.

In `@packages/engine/src/mission-execution-loop.ts`:
- Around line 323-327: Replace the ineffective refreshedById lookup in the
recovery loop around feature and the corresponding logic at the later recovery
block with a lookup that can observe state changes caused by earlier iterations,
or otherwise explicitly re-read the current feature state before processing.
Ensure cross-feature effects from transitionLoopState/processTaskOutcome are
reflected during the same recovery pass, rather than falling back to the
unchanged feature object.

In `@packages/engine/src/scheduler.ts`:
- Around line 1450-1460: Leave the current sequential mission-store lookups in
the todo scheduling loop unchanged for this intermediate cutover; track a
follow-up optimization around the blockedSliceIds logic to batch slice,
milestone, and mission retrieval through a single hierarchy lookup once the
mission-store async API stabilizes.
🪄 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: f542e394-9d77-4bbe-960e-4941fc847f9b

📥 Commits

Reviewing files that changed from the base of the PR and between f4e78ab and 54e6e2f.

📒 Files selected for processing (99)
  • packages/cli/src/commands/daemon.ts
  • packages/cli/src/commands/dashboard.ts
  • packages/cli/src/commands/serve.ts
  • packages/core/src/__tests__/planner-intervention.test.ts
  • packages/core/src/__tests__/plugin-hot-reload.test.ts
  • packages/core/src/__tests__/postgres-project-discovery.test.ts
  • packages/core/src/__tests__/postgres/archive-project-isolation.pg.test.ts
  • packages/core/src/__tests__/postgres/cli-session-store.pg.test.ts
  • packages/core/src/__tests__/postgres/embedded-lifecycle.test.ts
  • packages/core/src/__tests__/postgres/mission-store.pg.test.ts
  • packages/core/src/__tests__/postgres/operational-maintenance.pg.test.ts
  • packages/core/src/__tests__/postgres/plugin-schema-hook.test.ts
  • packages/core/src/__tests__/postgres/schema-applier.test.ts
  • packages/core/src/__tests__/postgres/startup-factory-integration.test.ts
  • packages/core/src/__tests__/postgres/startup-factory.test.ts
  • packages/core/src/__tests__/postgres/store-archive-reads.pg.test.ts
  • packages/core/src/__tests__/postgres/store-safe-defaults.pg.test.ts
  • packages/core/src/__tests__/postgres/task-lifecycle-e2e.pg.test.ts
  • packages/core/src/__tests__/postgres/workflow-authoritative-reads.pg.test.ts
  • packages/core/src/__tests__/project-identity.test.ts
  • packages/core/src/__tests__/sqlite-validation.test.ts
  • packages/core/src/__tests__/store-phantom-reservation-reconcile.test.ts
  • packages/core/src/agent-prompts.ts
  • packages/core/src/agent-store.ts
  • packages/core/src/async-archive-db.ts
  • packages/core/src/async-central-core.ts
  • packages/core/src/async-central-db.ts
  • packages/core/src/async-knowledge.ts
  • packages/core/src/async-mission-store-queries.ts
  • packages/core/src/async-mission-store.ts
  • packages/core/src/central-core.ts
  • packages/core/src/central-db.ts
  • packages/core/src/chat-store.ts
  • packages/core/src/cli-session-store.ts
  • packages/core/src/fs-watch-poll-controller.ts
  • packages/core/src/index.gate.ts
  • packages/core/src/index.ts
  • packages/core/src/migration.ts
  • packages/core/src/pi-extensions.ts
  • packages/core/src/planner-intervention.ts
  • packages/core/src/plugin-loader.ts
  • packages/core/src/plugin-types.ts
  • packages/core/src/postgres/embedded-lifecycle.ts
  • packages/core/src/postgres/index.ts
  • packages/core/src/postgres/migration-stamping.ts
  • packages/core/src/postgres/migrations/0009_mission_fix_idempotency.sql
  • packages/core/src/postgres/plugin-schema-hook.ts
  • packages/core/src/postgres/schema-applier.ts
  • packages/core/src/postgres/schema/index.ts
  • packages/core/src/postgres/schema/plugin.ts
  • packages/core/src/postgres/schema/project.ts
  • packages/core/src/postgres/sqlite-migrator.ts
  • packages/core/src/postgres/startup-factory.ts
  • packages/core/src/project-identity.ts
  • packages/core/src/project-root-guard.ts
  • packages/core/src/settings-export.ts
  • packages/core/src/sqlite-adapter.ts
  • packages/core/src/sqlite-validation.ts
  • packages/core/src/store.ts
  • packages/core/src/task-store/archive-lifecycle-2.ts
  • packages/core/src/task-store/async-archive-lineage.ts
  • packages/core/src/task-store/async-comments-attachments.ts
  • packages/core/src/task-store/async-lifecycle.ts
  • packages/core/src/task-store/async-maintenance.ts
  • packages/core/src/task-store/async-persistence.ts
  • packages/core/src/task-store/async-phantom-reservations.ts
  • packages/core/src/task-store/async-search.ts
  • packages/core/src/task-store/audit-ops.ts
  • packages/core/src/task-store/branch-group-ops.ts
  • packages/core/src/task-store/comments-ops.ts
  • packages/core/src/task-store/moves.ts
  • packages/core/src/task-store/reads.ts
  • packages/core/src/task-store/remaining-ops-1.ts
  • packages/core/src/task-store/remaining-ops-2.ts
  • packages/core/src/task-store/remaining-ops-3.ts
  • packages/core/src/task-store/remaining-ops-4.ts
  • packages/core/src/task-store/remaining-ops-5.ts
  • packages/core/src/task-store/remaining-ops-6.ts
  • packages/core/src/task-store/remaining-ops-7.ts
  • packages/core/src/task-store/remaining-ops-8.ts
  • packages/core/src/task-store/workflow-ops.ts
  • packages/core/src/types.ts
  • packages/dashboard/src/ai-session-store.ts
  • packages/dashboard/src/chat-project-services.ts
  • packages/dashboard/src/require-async-layer.ts
  • packages/dashboard/src/routes/register-settings-sync-routes.ts
  • packages/dashboard/src/server.ts
  • packages/desktop/src/local-runtime.ts
  • packages/desktop/src/local-server.ts
  • packages/engine/src/cli-agent/runtime.ts
  • packages/engine/src/executor.ts
  • packages/engine/src/merger.ts
  • packages/engine/src/mesh-lease-manager.ts
  • packages/engine/src/mission-execution-loop.ts
  • packages/engine/src/plugin-runner.ts
  • packages/engine/src/runtimes/in-process-runtime.ts
  • packages/engine/src/scheduler.ts
  • packages/engine/src/self-healing.ts
  • packages/engine/src/workflow-authoritative-driver.ts

Comment thread packages/cli/src/commands/daemon.ts Outdated
Comment thread packages/cli/src/commands/dashboard.ts
Comment thread packages/cli/src/commands/serve.ts Outdated
Comment thread packages/core/src/async-knowledge.ts
Comment thread packages/core/src/central-core.ts
Comment thread packages/core/src/task-store/async-comments-attachments.ts
Comment thread packages/core/src/task-store/async-lifecycle.ts
Comment thread packages/core/src/task-store/async-maintenance.ts Outdated
Comment thread packages/core/src/task-store/async-maintenance.ts Outdated
Comment thread packages/core/src/task-store/async-phantom-reservations.ts
gsxdsm added 5 commits July 14, 2026 21:53
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.
@gsxdsm gsxdsm merged commit 2e4fcfc into main Jul 15, 2026
7 checks passed
@gsxdsm gsxdsm deleted the feature/postgres-cutover-stack branch July 15, 2026 05:13
gsxdsm added a commit that referenced this pull request Jul 15, 2026
## 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 -->
gsxdsm added a commit that referenced this pull request Jul 15, 2026
## 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 -->
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 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

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 -->
gsxdsm added a commit that referenced this pull request Jul 15, 2026
## Summary

Fusion’s core runtime now treats PostgreSQL as the authoritative
metadata store without leaving current CLI, dashboard, desktop, or
engine composition roots uncompilable between stack layers. This is the
99-file foundation for the larger cutover: subsequent PRs migrate the
remaining consumers, plugins, and operator surfaces.

## Design decisions

- Runtime store construction fails closed when an asynchronous
PostgreSQL layer is unavailable; SQLite remains readable only at
explicit migration and identity-recovery boundaries.
- Project ownership is enforced across active, archived, workflow,
mission, analytics, and plugin-schema data.
- The small set of cross-package files in this layer are
compatibility-critical call sites required for a green intermediate
commit, not the complete consumer migration.
- Schema migration 0008 remains assigned to session-advisor state from
current `main`; mission lineage idempotency advances to 0009 so neither
invariant can be skipped.

## Validation

- All affected package typechecks pass: Core, Engine, Dashboard, CLI,
and Desktop.
- `pnpm test:gate` passes: 478 tests across the engine gate, PostgreSQL
core gate, and CLI workflow shape.
- The PR changes exactly 99 files.

## Stack

This is the base PR. Engine/dashboard, CLI/desktop/ops, plugins, and
docs/release follow as stacked PRs, each below 100 changed files.

Related: #2105


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* PostgreSQL is now the standard runtime backend, with embedded
PostgreSQL enabled by default.
* Added project-scoped storage for tasks, archives, chat sessions,
missions, knowledge pages, and operational data.
* Improved archived-task search, filtering, pagination, and restoration.
* Added safer plugin schema initialization with validation and project
isolation.
* Added PostgreSQL-backed workflow, mission, validator, and dashboard
capabilities.

* **Bug Fixes**
  * Improved startup timeout cancellation and resource cleanup.
* Prevented cross-project data access and phantom reservation cleanup
errors.
* Ensured archived tasks remain read-only and asynchronous writes
complete reliably.
  * Retired SQLite opt-out settings with clear startup errors.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
gsxdsm added a commit that referenced this pull request Jul 15, 2026
## 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 -->
gsxdsm added a commit that referenced this pull request Jul 15, 2026
## 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 -->
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