/**
 * sf-db-migration.test.mjs — legacy SQLite schema upgrade coverage.
 *
 * Purpose: prove real project databases from older SF versions still migrate
 * automatically when later backfills depend on newly introduced columns.
 */
import assert from "node:assert/strict";
import {
	existsSync,
	mkdirSync,
	mkdtempSync,
	readdirSync,
	rmSync,
	statSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { DatabaseSync } from "node:sqlite";
import { afterEach, test } from "vitest";
import { initRoutingHistory } from "../routing-history.js";
import {
	closeDatabase,
	getDatabase,
	getJudgmentsForUnit,
	getRetrievalEvidence,
	getScheduleEntries,
	insertJudgment,
	insertMilestone,
	insertRetrievalEvidence,
	insertScheduleEntry,
	openDatabase,
	reconcileWorktreeDb,
} from "../sf-db.js";

const tmpDirs = [];

afterEach(() => {
	closeDatabase();
	while (tmpDirs.length > 0) {
		const dir = tmpDirs.pop();
		if (dir) rmSync(dir, { recursive: true, force: true });
	}
});

function makeLegacyV27Db() {
	const dir = mkdtempSync(join(tmpdir(), "sf-legacy-v27-"));
	tmpDirs.push(dir);
	const sfDir = join(dir, ".sf");
	mkdirSync(sfDir, { recursive: true });
	const dbPath = join(sfDir, "sf.db");
	const db = new DatabaseSync(dbPath);
	db.exec(`
		CREATE TABLE schema_version (
			version INTEGER NOT NULL,
			applied_at TEXT NOT NULL
		);
		INSERT INTO schema_version (version, applied_at)
		VALUES (27, '2026-05-06T00:00:00.000Z');

		CREATE TABLE milestones (
			id TEXT PRIMARY KEY,
			title TEXT NOT NULL DEFAULT '',
			status TEXT NOT NULL DEFAULT 'active',
			depends_on TEXT NOT NULL DEFAULT '[]',
			created_at TEXT NOT NULL DEFAULT '',
			completed_at TEXT DEFAULT NULL,
			vision TEXT NOT NULL DEFAULT '',
			success_criteria TEXT NOT NULL DEFAULT '[]',
			key_risks TEXT NOT NULL DEFAULT '[]',
			proof_strategy TEXT NOT NULL DEFAULT '[]',
			verification_contract TEXT NOT NULL DEFAULT '',
			verification_integration TEXT NOT NULL DEFAULT '',
			verification_operational TEXT NOT NULL DEFAULT '',
			verification_uat TEXT NOT NULL DEFAULT '',
			definition_of_done TEXT NOT NULL DEFAULT '[]',
			requirement_coverage TEXT NOT NULL DEFAULT '',
			boundary_map_markdown TEXT NOT NULL DEFAULT '',
			vision_meeting_json TEXT NOT NULL DEFAULT ''
		);

		CREATE TABLE slices (
			milestone_id TEXT NOT NULL,
			id TEXT NOT NULL,
			title TEXT NOT NULL DEFAULT '',
			status TEXT NOT NULL DEFAULT 'pending',
			risk TEXT NOT NULL DEFAULT 'medium',
			depends TEXT NOT NULL DEFAULT '[]',
			demo TEXT NOT NULL DEFAULT '',
			created_at TEXT NOT NULL DEFAULT '',
			completed_at TEXT DEFAULT NULL,
			full_summary_md TEXT NOT NULL DEFAULT '',
			full_uat_md TEXT NOT NULL DEFAULT '',
			goal TEXT NOT NULL DEFAULT '',
			success_criteria TEXT NOT NULL DEFAULT '',
			proof_level TEXT NOT NULL DEFAULT '',
			integration_closure TEXT NOT NULL DEFAULT '',
			observability_impact TEXT NOT NULL DEFAULT '',
			adversarial_partner TEXT NOT NULL DEFAULT '',
			adversarial_combatant TEXT NOT NULL DEFAULT '',
			adversarial_architect TEXT NOT NULL DEFAULT '',
			planning_meeting_json TEXT NOT NULL DEFAULT '',
			sequence INTEGER DEFAULT 0,
			replan_triggered_at TEXT DEFAULT NULL,
			is_sketch INTEGER NOT NULL DEFAULT 0,
			sketch_scope TEXT NOT NULL DEFAULT '',
			PRIMARY KEY (milestone_id, id)
		);

		CREATE TABLE tasks (
			milestone_id TEXT NOT NULL,
			slice_id TEXT NOT NULL,
			id TEXT NOT NULL,
			title TEXT NOT NULL DEFAULT '',
			status TEXT NOT NULL DEFAULT 'pending',
			one_liner TEXT NOT NULL DEFAULT '',
			narrative TEXT NOT NULL DEFAULT '',
			verification_result TEXT NOT NULL DEFAULT '',
			duration TEXT NOT NULL DEFAULT '',
			completed_at TEXT DEFAULT NULL,
			blocker_discovered INTEGER DEFAULT 0,
			deviations TEXT NOT NULL DEFAULT '',
			known_issues TEXT NOT NULL DEFAULT '',
			key_files TEXT NOT NULL DEFAULT '[]',
			key_decisions TEXT NOT NULL DEFAULT '[]',
			full_summary_md TEXT NOT NULL DEFAULT '',
			description TEXT NOT NULL DEFAULT '',
			estimate TEXT NOT NULL DEFAULT '',
			files TEXT NOT NULL DEFAULT '[]',
			verify TEXT NOT NULL DEFAULT '',
			inputs TEXT NOT NULL DEFAULT '[]',
			expected_output TEXT NOT NULL DEFAULT '[]',
			observability_impact TEXT NOT NULL DEFAULT '',
			full_plan_md TEXT NOT NULL DEFAULT '',
			verification_status TEXT NOT NULL DEFAULT '',
			sequence INTEGER DEFAULT 0,
			escalation_pending INTEGER NOT NULL DEFAULT 0,
			escalation_awaiting_review INTEGER NOT NULL DEFAULT 0,
			escalation_override_applied INTEGER NOT NULL DEFAULT 0,
			escalation_artifact_path TEXT DEFAULT NULL,
			PRIMARY KEY (milestone_id, slice_id, id)
		);

		INSERT INTO milestones (id, title, status, created_at)
		VALUES ('M010', 'Beta Launch Readiness', 'active', '2026-05-06T00:00:00.000Z');
		INSERT INTO slices (milestone_id, id, title, status, created_at)
		VALUES ('M010', 'S03', 'Alerting Pipeline Verification', 'pending', '2026-05-06T00:00:00.000Z');
		INSERT INTO tasks (milestone_id, slice_id, id, title, status, verify)
		VALUES ('M010', 'S03', 'T01', 'Verify alert endpoint', 'pending', 'go test ./portal');
	`);
	db.close();
	return dbPath;
}

function makeLegacyV52Db() {
	const dir = mkdtempSync(join(tmpdir(), "sf-legacy-v52-"));
	tmpDirs.push(dir);
	const sfDir = join(dir, ".sf");
	mkdirSync(sfDir, { recursive: true });
	const dbPath = join(sfDir, "sf.db");
	const db = new DatabaseSync(dbPath);
	db.exec(`
		CREATE TABLE schema_version (
			version INTEGER NOT NULL,
			applied_at TEXT NOT NULL
		);
		INSERT INTO schema_version (version, applied_at)
		VALUES (52, '2026-05-06T00:00:00.000Z');

		CREATE TABLE milestones (
			id TEXT PRIMARY KEY,
			title TEXT NOT NULL DEFAULT '',
			status TEXT NOT NULL DEFAULT 'active',
			depends_on TEXT NOT NULL DEFAULT '[]',
			created_at TEXT NOT NULL DEFAULT '',
			completed_at TEXT DEFAULT NULL
		);

		CREATE TABLE slices (
			milestone_id TEXT NOT NULL,
			id TEXT NOT NULL,
			title TEXT NOT NULL DEFAULT '',
			status TEXT NOT NULL DEFAULT 'pending',
			risk TEXT NOT NULL DEFAULT 'medium',
			depends TEXT NOT NULL DEFAULT '[]',
			demo TEXT NOT NULL DEFAULT '',
			created_at TEXT NOT NULL DEFAULT '',
			completed_at TEXT DEFAULT NULL,
			PRIMARY KEY (milestone_id, id)
		);

		CREATE TABLE tasks (
			milestone_id TEXT NOT NULL,
			slice_id TEXT NOT NULL,
			id TEXT NOT NULL,
			title TEXT NOT NULL DEFAULT '',
			status TEXT NOT NULL DEFAULT 'pending',
			PRIMARY KEY (milestone_id, slice_id, id)
		);
	`);
	db.close();
	return dbPath;
}

function makeLegacyV35GateRunsDb() {
	const dir = mkdtempSync(join(tmpdir(), "sf-legacy-v35-gates-"));
	tmpDirs.push(dir);
	const sfDir = join(dir, ".sf");
	mkdirSync(sfDir, { recursive: true });
	const dbPath = join(sfDir, "sf.db");
	const db = new DatabaseSync(dbPath);
	db.exec(`
		CREATE TABLE schema_version (
			version INTEGER NOT NULL,
			applied_at TEXT NOT NULL
		);
		INSERT INTO schema_version (version, applied_at)
		VALUES (35, '2026-05-07T00:00:00.000Z');

		CREATE TABLE gate_runs (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			trace_id TEXT NOT NULL,
			turn_id TEXT NOT NULL,
			gate_id TEXT NOT NULL,
			gate_type TEXT NOT NULL DEFAULT '',
			unit_type TEXT DEFAULT NULL,
			unit_id TEXT DEFAULT NULL,
			milestone_id TEXT DEFAULT NULL,
			slice_id TEXT DEFAULT NULL,
			task_id TEXT DEFAULT NULL,
			outcome TEXT NOT NULL DEFAULT 'pass',
			failure_class TEXT NOT NULL DEFAULT 'none',
			rationale TEXT NOT NULL DEFAULT '',
			findings TEXT NOT NULL DEFAULT '',
			attempt INTEGER NOT NULL DEFAULT 1,
			max_attempts INTEGER NOT NULL DEFAULT 1,
			retryable INTEGER NOT NULL DEFAULT 0,
			evaluated_at TEXT NOT NULL DEFAULT '',
			duration_ms INTEGER DEFAULT NULL,
			cost_usd REAL DEFAULT NULL
		);
		INSERT INTO gate_runs (
			trace_id, turn_id, gate_id, gate_type, unit_type, unit_id,
			milestone_id, slice_id, task_id, outcome, failure_class, rationale,
			findings, attempt, max_attempts, retryable, evaluated_at, duration_ms,
			cost_usd
		) VALUES (
			'trace-1', 'turn-1', 'cost-gate', 'policy', 'execute-task',
			'M001/S01/T01', 'M001', 'S01', 'T01', 'pass', 'none', 'ok',
			'', 1, 1, 0, '2026-05-07T00:00:00.000Z', 12, 0.123456
		);
	`);
	db.close();
	return dbPath;
}

test("openDatabase_migrates_v27_tasks_without_created_at_through_spec_backfill", () => {
	const dbPath = makeLegacyV27Db();

	assert.equal(openDatabase(dbPath), true);
	const db = getDatabase();

	const columns = db.prepare("PRAGMA table_info(tasks)").all();
	assert.ok(columns.some((row) => row.name === "created_at"));
	const milestoneColumns = db.prepare("PRAGMA table_info(milestones)").all();
	assert.ok(
		milestoneColumns.some((row) => row.name === "product_research_json"),
	);
	const milestoneSpecColumns = db
		.prepare("PRAGMA table_info(milestone_specs)")
		.all();
	assert.ok(
		milestoneSpecColumns.some((row) => row.name === "product_research_json"),
	);
	const version = db
		.prepare("SELECT MAX(version) AS version FROM schema_version")
		.get();
	assert.equal(version.version, 72);
	// v70: tasks.purpose_trace (P3, ADR-0000)
	const taskColumnsAfter = db.prepare("PRAGMA table_info(tasks)").all();
	assert.ok(
		taskColumnsAfter.some((row) => row.name === "purpose_trace"),
		"purpose_trace column should exist after v70 migration",
	);
	// v71: self_feedback.purpose_anchor (P5, ADR-0000)
	const selfFeedbackColumns = db
		.prepare("PRAGMA table_info(self_feedback)")
		.all();
	assert.ok(
		selfFeedbackColumns.some((row) => row.name === "purpose_anchor"),
		"purpose_anchor column should exist after v71 migration",
	);
	// v61: intent_chapters table exists
	const chaptersTable = db
		.prepare(
			"SELECT name FROM sqlite_master WHERE type='table' AND name='intent_chapters'",
		)
		.get();
	assert.ok(
		chaptersTable,
		"intent_chapters table should exist after v61 migration",
	);
	// v62: tracked_md_files table exists
	const trackedMdTable = db
		.prepare(
			"SELECT name FROM sqlite_master WHERE type='table' AND name='tracked_md_files'",
		)
		.get();
	assert.ok(
		trackedMdTable,
		"tracked_md_files table should exist after v62 migration",
	);
	// v63: context_board table exists (always-in-context invariants board)
	const contextBoardTable = db
		.prepare(
			"SELECT name FROM sqlite_master WHERE type='table' AND name='context_board'",
		)
		.get();
	assert.ok(
		contextBoardTable,
		"context_board table should exist after v63 migration",
	);
	// v64: self_feedback_relations table exists (causal links between entries)
	const relationsTable = db
		.prepare(
			"SELECT name FROM sqlite_master WHERE type='table' AND name='self_feedback_relations'",
		)
		.get();
	assert.ok(
		relationsTable,
		"self_feedback_relations table should exist after v64 migration",
	);
	// v65: self_feedback gained impact_score + effort_estimate columns
	// (prioritization signal — sf-mp4rxkx0-fkt3e2)
	const sfColumns = db.prepare("PRAGMA table_info(self_feedback)").all();
	const colNames = sfColumns.map((c) => c.name);
	assert.ok(
		colNames.includes("impact_score"),
		"impact_score column should exist after v65 migration",
	);
	assert.ok(
		colNames.includes("effort_estimate"),
		"effort_estimate column should exist after v65 migration",
	);
	// v71: self_feedback gained purpose_anchor (ADR-0000 restoration — every
	// feedback row must name the milestone vision / slice goal it's filed
	// against so triage can prioritize against purpose).
	assert.ok(
		colNames.includes("purpose_anchor"),
		"purpose_anchor column should exist after v71 migration",
	);
	// v66: quality_gates gained UOK schema-v2 metadata columns (UOK
	// control-plane plan, slice 2).
	const qgColumns = db.prepare("PRAGMA table_info(quality_gates)").all();
	const qgColNames = qgColumns.map((c) => c.name);
	for (const col of [
		"surface",
		"run_control",
		"permission_profile",
		"trace_id",
		"parent_trace",
	]) {
		assert.ok(
			qgColNames.includes(col),
			`${col} column should exist after v66 migration`,
		);
	}
	// v69: slices gained traces_vision_fragment column (ADR-0000 P2 — per-slice
	// vision trace so validate-milestone has structured grounds for assessment).
	const sliceColumns = db.prepare("PRAGMA table_info(slices)").all();
	const sliceColNames = sliceColumns.map((c) => c.name);
	assert.ok(
		sliceColNames.includes("traces_vision_fragment"),
		"traces_vision_fragment column should exist after v69 migration",
	);
	const taskSpec = db
		.prepare(
			"SELECT milestone_id, slice_id, task_id, verify FROM task_specs WHERE task_id = 'T01'",
		)
		.get();
	assert.deepEqual(taskSpec, {
		milestone_id: "M010",
		slice_id: "S03",
		task_id: "T01",
		verify: "go test ./portal",
	});
	const schedulerRow = db
		.prepare(
			"SELECT status FROM task_scheduler WHERE milestone_id = 'M010' AND slice_id = 'S03' AND task_id = 'T01'",
		)
		.get();
	assert.deepEqual(schedulerRow, { status: "queued" });
});

test("openDatabase_v52_db_heals_routing_history_and_auto_start_path_works", () => {
	const dbPath = makeLegacyV52Db();

	assert.equal(openDatabase(dbPath), true);
	const db = getDatabase();

	// ensurePostBootstrapTables should have created routing_history
	const routingTable = db
		.prepare(
			"SELECT name FROM sqlite_master WHERE type='table' AND name='routing_history'",
		)
		.get();
	assert.ok(
		routingTable,
		"routing_history table should exist after ensurePostBootstrapTables",
	);

	// initRoutingHistory (auto-start path) must not crash on a v52 DB
	assert.doesNotThrow(() => {
		initRoutingHistory(dbPath);
	}, "initRoutingHistory should not throw on a v52 DB");

	// Schema should have migrated to v72 (current head after the
	// ADR-0000 swarm). See higher comment for the migration genealogy.
	const version = db
		.prepare("SELECT MAX(version) AS version FROM schema_version")
		.get();
	assert.equal(version.version, 72);
});

test("openDatabase_when_fresh_db_supports_schedule_entries", () => {
	assert.equal(openDatabase(":memory:"), true);

	insertScheduleEntry("project", {
		id: "sched-1",
		schemaVersion: 1,
		kind: "reminder",
		status: "pending",
		due_at: "2026-05-08T00:00:00.000Z",
		created_at: "2026-05-07T00:00:00.000Z",
		payload: { message: "check DB schedule" },
		created_by: "user",
	});

	const rows = getScheduleEntries("project");
	assert.equal(rows.length, 1);
	assert.equal(rows[0].id, "sched-1");
	assert.deepEqual(rows[0].payload, { message: "check DB schedule" });
});

test("openDatabase_when_file_backed_creates_db_snapshot_and_maintenance_marker", () => {
	const dir = mkdtempSync(join(tmpdir(), "sf-db-backup-"));
	tmpDirs.push(dir);
	const sfDir = join(dir, ".sf");
	mkdirSync(sfDir, { recursive: true });
	const dbPath = join(sfDir, "sf.db");

	assert.equal(openDatabase(dbPath), true);
	closeDatabase();

	const backupDir = join(sfDir, "backups", "db");
	const backups = readdirSync(backupDir).filter((name) =>
		name.startsWith("sf.db."),
	);
	assert.equal(backups.length, 1);
	assert.equal(backups[0].endsWith(".tmp"), false);
	assert.equal(statSync(join(backupDir, backups[0])).size > 0, true);
	assert.equal(existsSync(join(backupDir, "maintenance.json")), true);
});

test("openDatabase_when_fresh_db_does_not_create_gate_runs_table", () => {
	assert.equal(openDatabase(":memory:"), true);

	// After v58 migration, gate_runs table no longer exists
	const tableInfo = getDatabase()
		.prepare(
			"SELECT name FROM sqlite_master WHERE type='table' AND name='gate_runs'",
		)
		.get();
	assert.equal(
		tableInfo,
		undefined,
		"gate_runs table should not exist after v58 migration",
	);
});

test("reconcileWorktreeDb_when_worktree_lacks_product_research_column_merges_milestones", () => {
	const dir = mkdtempSync(join(tmpdir(), "sf-worktree-v41-"));
	tmpDirs.push(dir);
	const mainDbPath = join(dir, "main.db");
	const worktreeDbPath = join(dir, "worktree.db");

	assert.equal(openDatabase(mainDbPath), true);
	closeDatabase();

	assert.equal(openDatabase(worktreeDbPath), true);
	insertMilestone({
		id: "M041",
		title: "Old worktree milestone",
		status: "active",
	});
	closeDatabase();

	const wt = new DatabaseSync(worktreeDbPath);
	wt.exec("ALTER TABLE milestones DROP COLUMN product_research_json");
	wt.close();

	assert.equal(openDatabase(mainDbPath), true);
	const merged = reconcileWorktreeDb(mainDbPath, worktreeDbPath);
	assert.equal(merged.conflicts.length, 0);
	assert.equal(merged.milestones, 1);

	const row = getDatabase()
		.prepare(
			"SELECT id, title, product_research_json FROM milestones WHERE id = 'M041'",
		)
		.get();
	assert.deepEqual(row, {
		id: "M041",
		title: "Old worktree milestone",
		product_research_json: "",
	});
});

test("openDatabase_migrates_v35_gate_cost_usd_drops_table_in_v58", () => {
	const dbPath = makeLegacyV35GateRunsDb();

	assert.equal(openDatabase(dbPath), true);
	const db = getDatabase();
	// After v58 migration, gate_runs is dropped
	const tableInfo = db
		.prepare(
			"SELECT name FROM sqlite_master WHERE type='table' AND name='gate_runs'",
		)
		.get();
	assert.equal(
		tableInfo,
		undefined,
		"gate_runs should be dropped by v58 migration",
	);
});

test("openDatabase_memories_table_has_tags_column", () => {
	assert.equal(openDatabase(":memory:"), true);
	const db = getDatabase();
	const columns = db.prepare("PRAGMA table_info(memories)").all();
	const tagsCol = columns.find((row) => row.name === "tags");
	assert.ok(tagsCol, "memories table should have tags column");
	assert.equal(tagsCol.type, "TEXT");
	assert.equal(tagsCol.dflt_value, "'[]'");
});

test("openDatabase_memories_table_has_tenant_column", () => {
	assert.equal(openDatabase(":memory:"), true);
	const db = getDatabase();
	const columns = db.prepare("PRAGMA table_info(memories)").all();
	const tenantCol = columns.find((row) => row.name === "tenant");
	assert.ok(tenantCol, "memories table should have tenant column");
	assert.equal(tenantCol.type, "TEXT");
});

test("openDatabase_memory_indexes_exist", () => {
	assert.equal(openDatabase(":memory:"), true);
	const db = getDatabase();
	const indexes = db
		.prepare(
			"SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name IN ('memories', 'memory_sources')",
		)
		.all();
	const names = indexes.map((r) => r.name);
	assert.ok(
		names.includes("idx_memories_active"),
		"should have idx_memories_active",
	);
	assert.ok(
		names.includes("idx_memories_category"),
		"should have idx_memories_category",
	);
	assert.ok(
		names.includes("idx_memory_sources_content_hash"),
		"should have idx_memory_sources_content_hash",
	);
});

test("openDatabase_memory_extraction_attempts_table_exists", () => {
	assert.equal(openDatabase(":memory:"), true);
	const db = getDatabase();
	const columns = db
		.prepare("PRAGMA table_info(memory_extraction_attempts)")
		.all();
	const names = columns.map((row) => row.name);
	assert.deepEqual(names, [
		"id",
		"unit_key",
		"unit_type",
		"unit_id",
		"activity_file",
		"status",
		"failure_class",
		"reason",
		"error",
		"created_at",
	]);
	const index = db
		.prepare(
			"SELECT name FROM sqlite_master WHERE type = 'index' AND name = 'idx_memory_extraction_attempts_created'",
		)
		.get();
	assert.ok(index, "memory extraction attempts should be indexed by time");
});

test("openDatabase_judgments_table_round_trip", () => {
	assert.equal(openDatabase(":memory:"), true);
	insertJudgment({
		unitId: "M001/S01/T01",
		decision: "Use SQLite over JSONL",
		alternatives: ["Keep JSONL", "Use JSONL with schema"],
		reasoning: "SQLite gives us querying and indexing",
		confidence: "high",
		ts: "2026-05-07T00:00:00.000Z",
	});
	insertJudgment({
		unitId: "M001/S01/T02",
		decision: "Add tags column",
		alternatives: ["Separate tags table"],
		reasoning: "Simpler schema",
		confidence: "medium",
		ts: "2026-05-07T00:01:00.000Z",
	});

	const m001 = getJudgmentsForUnit("M001", 100);
	assert.equal(m001.length, 2);
	assert.equal(m001[0].unitId, "M001/S01/T02"); // DESC order
	assert.equal(m001[1].unitId, "M001/S01/T01");
	assert.deepEqual(m001[1].alternatives, [
		"Keep JSONL",
		"Use JSONL with schema",
	]);

	const t01 = getJudgmentsForUnit("M001/S01/T01", 100);
	assert.equal(t01.length, 1);
	assert.equal(t01[0].confidence, "high");
});

test("openDatabase_retrieval_evidence_table_round_trip", () => {
	assert.equal(openDatabase(":memory:"), true);
	insertRetrievalEvidence({
		backend: "sift",
		sourceKind: "code",
		query: "approval policy",
		strategy: "bm25",
		scope: "src",
		projectRoot: "/repo",
		gitHead: "abc123",
		gitBranch: "main",
		worktreeDirty: true,
		freshness: "working-tree",
		status: "ok",
		hitCount: 1,
		elapsedMs: 42,
		cachePath: "/repo/.sf/runtime/sift/search-cache",
		result: { hits: [{ path: "src/index.ts", score: 0.9 }] },
		recordedAt: "2026-05-07T00:00:00.000Z",
	});

	const rows = getRetrievalEvidence(10);
	assert.equal(rows.length, 1);
	assert.equal(rows[0].backend, "sift");
	assert.equal(rows[0].sourceKind, "code");
	assert.equal(rows[0].query, "approval policy");
	assert.equal(rows[0].scope, "src");
	assert.equal(rows[0].worktreeDirty, true);
	assert.equal(rows[0].freshness, "working-tree");
	assert.deepEqual(rows[0].result, {
		hits: [{ path: "src/index.ts", score: 0.9 }],
	});
});
