# Remove dead SQLite dual-path code; keep migration-only readers ## Summary PostgreSQL cutover left hundreds of production dual-path branches (`backendMode ? PG : SQLite/store.db`) whose SQLite arms only hit throwing `Database`/`ArchiveDatabase`/`CentralDatabase` stubs. This change mechanically collapses those unreachable arms so production authority is AsyncDataLayer/PostgreSQL only, while preserving the six authorized read-only migration/recovery `DatabaseSync` seams. ## Dual-path mass removed | Metric | Before | After | |---|---|---| | `if (…backendMode)` (non-test) | ~328 | ~70 | | `store.db` / `this.db` refs in core (non-test) | ~570+ | ~375 (mostly pure legacy MissionStore/eval/insight SQLite classes + thin getters) | | Net diff | — | **~6.7k lines removed** across 41 files | Remaining `backendMode` checks are intentional (incomplete-PG sync safe-defaults, settings-sync disabled-on-PG, symbol-lock PG-only gates, “requires PostgreSQL” config versioning throws), not live SQLite authority. ## Subsystems cleaned - **Core TaskStore / task-store/***: collapsed if/else and early-return dual-path across reads, moves, lifecycle, mutations, workflow, archive, branch/PR, artifacts, comments, audit, project ops, etc. `initImpl` is PostgreSQL-only (SQLite startup tail deleted). - **Satellite stores**: automation, agent, routine, plugin, secrets, approval-request, central-core dual-path arms collapsed. - **Plugins**: reports async methods, compound-engineering pipeline + session stores, CLI Printing Press store — SQLite fallbacks removed; PG required. - **Engine**: no functional dual-path change beyond whitespace (settings-sync / peer-exchange PG-disabled behavior kept). ## Six migration-only readers retained (allowlist unchanged) 1. `packages/core/src/postgres/sqlite-migrator.ts` 2. `packages/core/src/project-identity.ts` 3. `packages/core/src/sqlite-validation.ts` 4. `packages/core/src/postgres/startup-factory.ts` 5. `packages/cli/src/commands/db.ts` 6. `scripts/lib/start-local-project.mjs` Plus low-level `sqlite-adapter` and migrator/startup-import tests. Inventory ratchet still requires exactly these six `new DatabaseSync(` production sites, all `readOnly: true`. ## Not treated as SQLite - `.fusion/project.json`, `task.json`, `agent-log.jsonl` file storage - AsyncDataLayer / Drizzle PG paths - Incomplete-PG sync safe-default stubs (still return empty/false/null under backend without consulting SQLite) ## Verification - `sqlite-production-reader-inventory.test.ts` — 15/15 pass - `incomplete-pg-ports.pg.test.ts` — 6/6 pass - Targeted PG tests (create-task, move, handoff, runtime-persistence, agent, mission, insight, central-core) — green - `tsc --noEmit` for `@fusion/core`, `@fusion/engine`, `@fusion/dashboard` — green - `scripts/check-no-getdatabase.mjs` — clean <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Improved end-to-end consistency by making PostgreSQL/async persistence the standard across core task/workflow, automation, agents, plugins, routines, secrets, approvals, central operations, and session storage. * Unified scheduling, settings, configuration revision writes, run/workflow selection, queues/leases/transitions, and audit/lifecycle updates around consistent async transaction behavior. * **Bug Fixes** * Fixed edge cases for archived/deleted reads, unarchive/recovery flows, not-found handling, and task/artifact/document/log/comment operations, including more reliable emissions and hydration across search/list and lifecycle operations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
79 lines
3.1 KiB
TypeScript
79 lines
3.1 KiB
TypeScript
/**
|
|
* Task commit-association persistence (domain module).
|
|
*
|
|
* FNXC:StoreModularization 2026-06-25-00:00:
|
|
* Extracted from the monolithic packages/core/src/store.ts as a pure
|
|
* behavior-preserving refactor. Each function receives the TaskStore
|
|
* instance as its first parameter and performs byte-identical work.
|
|
*
|
|
* FNXC:CodeOrganization 2026-07-15-00:00:
|
|
* Renamed from remaining-ops-9 to a domain name; no behavior change.
|
|
*/
|
|
|
|
import { TaskStore } from "../store.js";
|
|
import { normalizeTaskCommitAssociation } from "../task-lineage.js";
|
|
import { TaskCommitAssociationRow } from "./row-types.js";
|
|
import { TaskCommitAssociation } from "../types.js";
|
|
import { and, desc, eq, inArray } from "drizzle-orm";
|
|
import * as schema from "../postgres/schema/index.js";
|
|
|
|
export async function getTaskCommitAssociationsByLineageIdImpl(store: TaskStore, lineageId: string): Promise<TaskCommitAssociation[]> {
|
|
/*
|
|
FNXC:PostgresCutover 2026-07-04:
|
|
Backend-mode read of task_commit_associations by lineage id via async
|
|
Drizzle. Mirrors the SQLite ORDER BY authoredAt DESC, createdAt DESC. The
|
|
Drizzle select returns camelCase columns (schema-mapped), cast to the
|
|
shared TaskCommitAssociationRow shape used by normalizeTaskCommitAssociation.
|
|
*/
|
|
const layer = store.asyncLayer!;
|
|
const rows = await layer.db
|
|
.select()
|
|
.from(schema.project.taskCommitAssociations)
|
|
.where(eq(schema.project.taskCommitAssociations.taskLineageId, lineageId))
|
|
.orderBy(
|
|
desc(schema.project.taskCommitAssociations.authoredAt),
|
|
desc(schema.project.taskCommitAssociations.createdAt),
|
|
);
|
|
return (rows as TaskCommitAssociationRow[]).map((row) =>
|
|
normalizeTaskCommitAssociation({
|
|
...row,
|
|
note: row.note ?? undefined,
|
|
additions: row.additions ?? undefined,
|
|
deletions: row.deletions ?? undefined,
|
|
}),
|
|
);
|
|
}
|
|
|
|
export async function replaceLegacyTaskCommitAssociationsImpl(store: TaskStore,
|
|
lineageId: string,
|
|
associations: Array<Omit<TaskCommitAssociation, "id" | "createdAt" | "updatedAt" | "taskLineageId">>,
|
|
): Promise<void> {
|
|
/*
|
|
FNXC:PostgresCutover 2026-07-04:
|
|
Backend-mode replacement of legacy-matched task_commit_associations: delete
|
|
the legacy-/manual-matched rows for the lineage via async Drizzle, then
|
|
re-insert through store.upsertTaskCommitAssociation (which itself dispatches
|
|
to the async upsert in backend mode). Canonical-lineage-trailer rows are
|
|
preserved (matched only on the three legacy sources), matching the SQLite
|
|
path's IN-filter exactly.
|
|
*/
|
|
const layer = store.asyncLayer!;
|
|
await layer.db
|
|
.delete(schema.project.taskCommitAssociations)
|
|
.where(
|
|
and(
|
|
eq(schema.project.taskCommitAssociations.taskLineageId, lineageId),
|
|
inArray(schema.project.taskCommitAssociations.matchedBy, [
|
|
"legacy-task-id-trailer",
|
|
"legacy-subject",
|
|
"manual-reconciliation",
|
|
]),
|
|
),
|
|
);
|
|
for (const association of associations) {
|
|
await store.upsertTaskCommitAssociation({ ...association, taskLineageId: lineageId });
|
|
}
|
|
return;
|
|
}
|
|
|