Files
fusion/packages/core/src/postgres/index.ts
gsxdsm 2e4fcfcaea fix(FN-7952): establish PostgreSQL core authority (#2108)
## 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 -->
2026-07-14 22:13:30 -07:00

229 lines
7.9 KiB
TypeScript

/**
* PostgreSQL connection layer.
*
* FNXC:PostgresConnection 2026-06-24-02:05:
* Barrel export for the postgres connection subsystem. This module provides
* backend resolution (embedded vs external), connection pool management with
* the DATABASE_MIGRATION_URL split, and credential redaction.
*
* Consumers:
* - The embedded lifecycle feature (U2) calls createConnectionSetFromUrl()
* after starting the bundled Postgres.
* - The external startup path calls createConnectionSet() with DATABASE_URL set.
* - Tests use resolveBackend() and the credential-redact helpers directly.
*/
export {
resolveBackend,
resolveBackendWithOptions,
looksLikePoolerUrl,
poolerWarning,
describeBackendForLog,
DATABASE_URL_ENV,
DATABASE_MIGRATION_URL_ENV,
POOLER_PREPARED_STATEMENT_WARNING,
type BackendMode,
type ResolvedBackend,
type ResolveBackendOptions,
} from "./backend-resolver.js";
export {
createConnectionSet,
createConnectionSetFromUrl,
verifyConnection,
DatabaseConnectionError,
redactConnectionString,
type PostgresConnections,
type CreateConnectionOptions,
} from "./connection.js";
export {
redactUrlPassword,
redactUrlQueryPassword,
redactKeywordPassword,
redactConnectionString as redactCredentials,
redactCredentialsFromMessage,
REDACTED_PASSWORD_PLACEHOLDER,
} from "./credential-redact.js";
// FNXC:RuntimeStartupWiring 2026-06-24-11:05:
// The embedded PostgreSQL lifecycle module (embedded-lifecycle.ts) imports the
// `embedded-postgres` package, which uses dynamic import() for platform-
// specific optional binaries (@embedded-postgres/linux-x64, etc.). Re-exporting
// it from this barrel would pull those unresolved imports into every consumer
// of @fusion/core — including the CLI bundle (tsup/esbuild bundles @fusion/*
// with noExternal), which breaks the build and the boot smoke on platforms
// whose optional binary is absent.
//
// The embedded lifecycle is therefore NOT re-exported here. The runtime
// startup factory (startup-factory.ts) loads it lazily via await import()
// only when FUSION_EMBEDDED_PG=1, and the integration tests import it
// directly from "./embedded-lifecycle.js". This keeps the embedded-postgres
// dependency out of the static import graph of every other consumer.
/**
* FNXC:PostgresSchema 2026-06-24-03:50:
* Drizzle schema-as-code for the three application databases (project/central/
* archive) and plugin-owned tables. The fresh migration baseline
* (migrations/0000_initial.sql) materializes these definitions; the schema
* applier applies the baseline + plugin hooks to a connection.
*/
export * as schema from "./schema/index.js";
export {
applySchemaBaseline,
getAppliedMigrations,
readBaselineMigrationSql,
SCHEMA_BASELINE_VERSION,
PROJECT_OWNERSHIP_SCHEMA_VERSION,
SESSION_ADVISOR_ENABLED_SCHEMA_VERSION,
MIGRATION_BOOKKEEPING_TABLE,
} from "./schema-applier.js";
export {
roadmapPluginSchemaInit,
cePluginSchemaInit,
whatsappPluginSchemaInit,
evenRealitiesPluginSchemaInit,
reportsPluginSchemaInit,
cliPressPluginSchemaInit,
DEFAULT_PLUGIN_SCHEMA_INIT_HOOKS,
runPluginSchemaInitHooks,
runLoadedPluginSchemaInitHooks,
assertLoadedPluginSchemaInitHooksSupported,
type PluginSchemaInitHook,
type LoadedPluginSchemaContract,
} from "./plugin-schema-hook.js";
/**
* FNXC:AsyncDataLayer 2026-06-24-10:30:
* Async data-layer foundation (U4). The stable `AsyncDataLayer` interface that
* replaces the synchronous `DatabaseSync` adapter. Plugin stores and the
* decomposed task-store modules program against this interface so the
* SQLite→PostgreSQL backend swap is invisible to them (VAL-DATA-016).
*
* The `getDatabase()` accessor on `TaskStore` will return an async-capable
* connection backed by this interface; the direct-`prepare()` consumers that
* relied on the synchronous `Database` shape are converted in U15.
*
* Consumers:
* - U12-U14 (task-store module migrations) call `layer.transactionImmediate()`
* and `recordRunAuditEventWithinTransaction(tx, ...)` to preserve the
* run-audit-event-within-transaction atomicity.
* - U6 (satellite stores) construct an `AsyncDataLayer` per database.
* - Plugin stores (`fusion-plugin-roadmap`) consume the stable interface.
*/
export {
createAsyncDataLayer,
recordRunAuditEvent,
recordRunAuditEventWithinTransaction,
projectTable,
type AsyncDataLayer,
type DrizzleDb,
type DbTransaction,
type TransactionOptions,
type RunAuditEventInput,
type RunAuditEvent,
} from "./data-layer.js";
/**
* FNXC:PostgresHealth 2026-06-24-16:00:
* PostgreSQL health and maintenance surface (U8). Replaces SQLite-specific
* surfaces (PRAGMA integrity_check, VACUUM-on-SQLite, WAL checkpointing,
* PRAGMA table_info schema self-heal) with PostgreSQL equivalents:
* - Health check via connectivity probe + pg_stat_database (VAL-HEALTH-001/002)
* - Schema drift detection via information_schema with self-heal (VAL-HEALTH-004)
* - Explicit VACUUM/ANALYZE compaction with stats (VAL-HEALTH-005)
* - Async task-ID integrity detector (VAL-HEALTH-003)
*/
export {
checkPostgresHealth,
detectSchemaDrift,
healSchemaDrift,
validateAndHealSchema,
vacuumAnalyze,
EXPECTED_PROJECT_COLUMNS,
type PostgresHealthSnapshot,
type SchemaDriftFinding,
type SchemaValidationReport,
type VacuumAnalyzeStats,
type VacuumAnalyzeResult,
} from "./postgres-health.js";
export {
detectTaskIdIntegrityAnomaliesAsync,
} from "./async-task-id-integrity.js";
/**
* FNXC:PostgresMigration 2026-06-24-10:00:
* SQLite-to-PostgreSQL data migration tool (U9 / VAL-MIGRATE-001..006).
* Snapshots the current final SQLite schema into PostgreSQL and bulk-copies
* all data across the three Fusion databases, idempotently and with
* verification. Used at cutover to migrate a populated SQLite deployment into
* PostgreSQL. The cutover harness (dual-read-cutover) and SQLite removal
* (sqlite-removal) features consume this tool.
*/
export {
migrateSqliteToPostgres,
isSqliteMigrationComplete,
completeSqliteMigration,
defaultMigrationSources,
formatMigrationProgress,
toSnakeCase,
type SqliteMigrationSource,
type SchemaName,
type MigrationOptions,
type MigrationReport,
type MigrationProgressEvent,
type MigrationProgressPhase,
type TableMigrationResult,
} from "./sqlite-migrator.js";
/**
* FNXC:CentralProjectIdentity 2026-07-13-23:10:
* Post-migration project-partition stamping, shared by the startup-factory
* first-boot auto-migration and the manual `fn db migrate` cutover command so
* migrated rows (tasks/archived_tasks/config/workflow settings) are re-keyed to
* the central-registry project id on BOTH paths.
*/
export {
stampMigratedProjectRows,
lookupRegisteredProjectIdByPath,
type StampMigratedProjectRowsInput,
type StampMigratedProjectRowsResult,
} from "./migration-stamping.js";
/**
* FNXC:BackendFlip 2026-06-26-14:30:
* Runtime startup factory (cutover milestone). `createTaskStoreForBackend()`
* is the single entry point production construction sites consult to decide
* how to boot PostgreSQL. Embedded PostgreSQL is the zero-config default and
* DATABASE_URL selects an external service; the removed SQLite opt-out is
* rejected so callers always receive a live backend result.
*/
export {
createTaskStoreForBackend,
createCentralBackendLayer,
shouldUsePostgresBackend,
isEmbeddedPgRequested,
isEmbeddedPgOptedOut,
EMBEDDED_PG_ENV,
NO_EMBEDDED_PG_ENV,
type BackendBootResult,
type CentralBackendLayerResult,
type CreateTaskStoreForBackendOptions,
} from "./startup-factory.js";
/**
* FNXC:PostgresBackup 2026-06-24-21:00:
* PostgreSQL backup and restore via pg_dump/pg_restore (U11 / VAL-REMOVAL-003).
* After the SQLite cutover, backups are PostgreSQL logical dumps instead of
* SQLite file copies. This module preserves the project + central pairing.
*/
export {
PgBackupManager,
PROJECT_BACKUP_SCHEMAS,
CENTRAL_BACKUP_SCHEMAS,
parsePgUrl,
type PgBackupOptions,
type PgBackupPair,
type PgDumpResult,
} from "./pg-backup.js";