feat(FN-4218): complete Steps 2-5 — schema, store, tests, and docs

Fusion-Task-Id: FN-4218
Fusion-Task-Lineage: 0b50d7f4-5001-4eb6-9633-7b24b00097fd
This commit is contained in:
Fusion
2026-05-13 23:49:20 -07:00
committed by gsxdsm
parent 1d369a268f
commit 773f493419
12 changed files with 751 additions and 26 deletions

View File

@@ -119,7 +119,7 @@ export function probeFts5(db: DatabaseSync): boolean {
// ── Schema Definition ────────────────────────────────────────────────
const SCHEMA_VERSION = 74;
const SCHEMA_VERSION = 75;
function normalizeTaskComments(
steeringComments: SteeringComment[] | undefined,
@@ -562,6 +562,42 @@ CREATE TABLE IF NOT EXISTS research_run_events (
);
CREATE INDEX IF NOT EXISTS idxResearchRunEventsRunIdSeq ON research_run_events(runId, seq);
CREATE TABLE IF NOT EXISTS experiment_sessions (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
projectId TEXT,
status TEXT NOT NULL,
metric TEXT NOT NULL,
currentSegment INTEGER NOT NULL DEFAULT 1,
maxIterations INTEGER,
workingDir TEXT,
baselineRunId TEXT,
bestRunId TEXT,
keptRunIds TEXT NOT NULL DEFAULT '[]',
tags TEXT NOT NULL DEFAULT '[]',
metadata TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL,
finalizedAt TEXT
);
CREATE INDEX IF NOT EXISTS idxExperimentSessionsStatus ON experiment_sessions(status);
CREATE INDEX IF NOT EXISTS idxExperimentSessionsProject ON experiment_sessions(projectId);
CREATE INDEX IF NOT EXISTS idxExperimentSessionsCreatedAt ON experiment_sessions(createdAt);
CREATE TABLE IF NOT EXISTS experiment_session_records (
id TEXT PRIMARY KEY,
sessionId TEXT NOT NULL,
segment INTEGER NOT NULL,
seq INTEGER NOT NULL,
type TEXT NOT NULL,
payload TEXT NOT NULL,
createdAt TEXT NOT NULL,
FOREIGN KEY (sessionId) REFERENCES experiment_sessions(id) ON DELETE CASCADE,
UNIQUE(sessionId, seq)
);
CREATE INDEX IF NOT EXISTS idxExperimentRecordsSessionSegment ON experiment_session_records(sessionId, segment, seq);
CREATE INDEX IF NOT EXISTS idxExperimentRecordsType ON experiment_session_records(sessionId, type);
-- Eval run persistence (FN-3387)
CREATE TABLE IF NOT EXISTS eval_runs (
id TEXT PRIMARY KEY,
@@ -3164,6 +3200,50 @@ export class Database {
});
}
if (version < 75) {
this.applyMigration(75, () => {
this.db.exec(`
CREATE TABLE IF NOT EXISTS experiment_sessions (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
projectId TEXT,
status TEXT NOT NULL,
metric TEXT NOT NULL,
currentSegment INTEGER NOT NULL DEFAULT 1,
maxIterations INTEGER,
workingDir TEXT,
baselineRunId TEXT,
bestRunId TEXT,
keptRunIds TEXT NOT NULL DEFAULT '[]',
tags TEXT NOT NULL DEFAULT '[]',
metadata TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL,
finalizedAt TEXT
)
`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxExperimentSessionsStatus ON experiment_sessions(status)`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxExperimentSessionsProject ON experiment_sessions(projectId)`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxExperimentSessionsCreatedAt ON experiment_sessions(createdAt)`);
this.db.exec(`
CREATE TABLE IF NOT EXISTS experiment_session_records (
id TEXT PRIMARY KEY,
sessionId TEXT NOT NULL,
segment INTEGER NOT NULL,
seq INTEGER NOT NULL,
type TEXT NOT NULL,
payload TEXT NOT NULL,
createdAt TEXT NOT NULL,
FOREIGN KEY (sessionId) REFERENCES experiment_sessions(id) ON DELETE CASCADE,
UNIQUE(sessionId, seq)
)
`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxExperimentRecordsSessionSegment ON experiment_session_records(sessionId, segment, seq)`);
this.db.exec(`CREATE INDEX IF NOT EXISTS idxExperimentRecordsType ON experiment_session_records(sessionId, type)`);
});
}
}
/**