## 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 -->
37 lines
1.5 KiB
JavaScript
37 lines
1.5 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { DatabaseSync } from "node:sqlite";
|
|
import test from "node:test";
|
|
import { hasLocalProjectMigrationInput } from "../lib/start-local-project.mjs";
|
|
|
|
test("local startup recognizes project identity and legacy migration input", async (t) => {
|
|
const root = await mkdtemp(join(tmpdir(), "fusion-start-local-project-"));
|
|
t.after(() => rm(root, { recursive: true, force: true }));
|
|
await mkdir(join(root, ".fusion"));
|
|
|
|
assert.equal(hasLocalProjectMigrationInput(root), false);
|
|
|
|
await writeFile(join(root, ".fusion", "fusion.db"), "");
|
|
assert.equal(hasLocalProjectMigrationInput(root), true);
|
|
|
|
await rm(join(root, ".fusion", "fusion.db"));
|
|
const db = new DatabaseSync(join(root, ".fusion", "fusion.db"));
|
|
db.exec("CREATE TABLE migration_input (id INTEGER PRIMARY KEY)");
|
|
db.close();
|
|
assert.equal(hasLocalProjectMigrationInput(root), true);
|
|
|
|
await rm(join(root, ".fusion", "fusion.db"));
|
|
await writeFile(join(root, ".fusion", "fusion.db"), "malformed legacy input");
|
|
assert.equal(hasLocalProjectMigrationInput(root), false);
|
|
|
|
await rm(join(root, ".fusion", "fusion.db"));
|
|
await mkdir(join(root, ".fusion", "fusion.db"));
|
|
assert.equal(hasLocalProjectMigrationInput(root), false);
|
|
|
|
await rm(join(root, ".fusion", "fusion.db"), { recursive: true });
|
|
await writeFile(join(root, ".fusion", "project.json"), "{}");
|
|
assert.equal(hasLocalProjectMigrationInput(root), true);
|
|
});
|