import { DatabaseSync } from 'node:sqlite'; import { mkdirSync } from 'node:fs'; // Schema for the private incident notebook and the Git fix-candidate inbox. // Deployment-receipt tables live in receipts.mjs, next to the code that // actually owns that data — this file only owns the notebook's own shape. const SCHEMA = ` PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON; CREATE TABLE IF NOT EXISTS incidents( id TEXT PRIMARY KEY, title TEXT NOT NULL, project TEXT, status TEXT NOT NULL DEFAULT 'investigating', severity TEXT NOT NULL DEFAULT 'annoyance', detected_at TEXT NOT NULL, symptom TEXT NOT NULL, impact TEXT DEFAULT '', root_cause TEXT DEFAULT '', confidence TEXT DEFAULT 'unknown', fix TEXT DEFAULT '', verification TEXT DEFAULT '', prevention TEXT DEFAULT '', remaining_risk TEXT DEFAULT '', lesson TEXT DEFAULT '', public_summary TEXT DEFAULT '', created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS timeline( id INTEGER PRIMARY KEY AUTOINCREMENT, incident_id TEXT NOT NULL REFERENCES incidents(id) ON DELETE CASCADE, at TEXT NOT NULL, kind TEXT NOT NULL, body TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS hypotheses( id INTEGER PRIMARY KEY AUTOINCREMENT, incident_id TEXT NOT NULL REFERENCES incidents(id) ON DELETE CASCADE, statement TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'untested', evidence TEXT DEFAULT '' ); CREATE TABLE IF NOT EXISTS candidates( id INTEGER PRIMARY KEY AUTOINCREMENT, repo TEXT NOT NULL, sha TEXT NOT NULL, committed_at TEXT NOT NULL, subject TEXT NOT NULL, paths TEXT DEFAULT '', reason TEXT NOT NULL, state TEXT NOT NULL DEFAULT 'new', incident_id TEXT, UNIQUE(repo, sha) );`; // Additive, best-effort migrations for columns that shipped after the // initial schema. SQLite has no "ADD COLUMN IF NOT EXISTS", so each one is // tried and a "duplicate column" failure is the expected steady state once // it's already applied everywhere. const MIGRATIONS = [ "ALTER TABLE incidents ADD COLUMN published_at TEXT DEFAULT ''", "ALTER TABLE incidents ADD COLUMN public_slug TEXT DEFAULT ''", ]; export function openDb(path = 'data/trace.db') { mkdirSync('data', { recursive: true }); mkdirSync('data/exports', { recursive: true }); const db = new DatabaseSync(path); db.exec(SCHEMA); for (const sql of MIGRATIONS) { try { db.exec(sql); } catch (error) { if (!String(error.message).includes('duplicate column')) throw error; } } return db; }