import {
	existsSync,
	mkdirSync,
	readdirSync,
	readFileSync,
	unlinkSync,
	writeFileSync,
} from "node:fs";
import { join } from "node:path";
import {
	countMustHavesMentionedInSummary,
	loadFile,
	parseSummary,
	parseTaskPlanMustHaves,
} from "../files.js";
import { createMemory } from "../memory-store.js";
import {
	relSliceFile,
	relTaskFile,
	resolveSliceFile,
	resolveTaskFile,
	sfRoot,
} from "../paths.js";
import { getSlice, getTask, isDbAvailable } from "../sf-db.js";
import { parseUnitId } from "../unit-id.js";
import {
	normalizeUnitLineage,
	recordUnitLineageEvent,
} from "./unit-lineage.js";
/**
 * Lists every unit runtime projection status in UOK lifecycle order.
 *
 * Purpose: keep the `.sf/runtime/units` compatibility projection aligned with
 * the UOK-owned lifecycle vocabulary so query and older recovery surfaces do not
 * redispatch terminal units through ambiguous legacy phases.
 *
 * Consumer: auto runtime persistence, unit-runtime tests, headless query summaries.
 */
export const UNIT_RUNTIME_STATUSES = [
	"queued",
	"claimed",
	"running",
	"progress",
	"completed",
	"failed",
	"blocked",
	"cancelled",
	"stale",
	"runaway-recovered",
	"notified",
];
/**
 * Names the unit statuses that end an execution attempt.
 *
 * Purpose: centralize the terminal-state union so retry and notification policy
 * does not drift between watchdog recovery and dispatch preview logic.
 *
 * Consumer: decideUnitRuntimeDispatch and operator-facing query summaries.
 */
export const UNIT_RUNTIME_TERMINAL_STATUSES = [
	"completed",
	"failed",
	"blocked",
	"cancelled",
	"stale",
	"runaway-recovered",
];
/**
 * Describes the explicit unit runtime lifecycle transitions.
 *
 * Purpose: make retry, notification, and reset projection transitions reviewable
 * as data while UOK owns authoritative lifecycle and recovery decisions.
 *
 * Consumer: unit runtime tests and UOK projection/reconciler guards.
 */
export const UNIT_RUNTIME_TRANSITIONS = {
	queued: ["claimed", "cancelled"],
	claimed: ["running", "stale", "cancelled"],
	running: [
		"progress",
		"completed",
		"failed",
		"blocked",
		"cancelled",
		"stale",
		"runaway-recovered",
	],
	progress: [
		"running",
		"completed",
		"failed",
		"blocked",
		"cancelled",
		"stale",
		"runaway-recovered",
	],
	completed: ["notified"],
	failed: ["queued", "notified"],
	blocked: ["notified"],
	cancelled: ["notified"],
	stale: ["queued", "notified"],
	"runaway-recovered": ["queued", "notified"],
	notified: ["queued"],
};
const DEFAULT_UNIT_RUNTIME_MAX_RETRIES = 1;
const RETRYABLE_TERMINAL_STATUSES = new Set(["failed", "stale"]);
function hasUpdate(updates, key) {
	return Object.hasOwn(updates, key);
}
function phaseForStatus(status) {
	switch (status) {
		case "queued":
		case "claimed":
		case "running":
			return "dispatched";
		case "progress":
			return "wrapup-warning-sent";
		case "completed":
			return "finalized";
		default:
			return status;
	}
}
function inferStatusFromPhase(phase, record) {
	if (UNIT_RUNTIME_STATUSES.includes(phase)) {
		return phase;
	}
	switch (phase) {
		case "dispatched":
			return "running";
		case "wrapup-warning-sent":
		case "runaway-warning-sent":
		case "runaway-final-warning-sent":
		case "recovered":
			return "progress";
		case "timeout":
			return "stale";
		case "finalized":
			return "completed";
		case "paused":
			return record?.runawayGuardPause ? "runaway-recovered" : "blocked";
		case "skipped":
			return "blocked";
		default:
			return "running";
	}
}
function retryBudgetRemaining(retryCount, maxRetries) {
	return Math.max(0, maxRetries - retryCount);
}
/**
 * Returns true when a runtime status is terminal for one execution attempt.
 *
 * Purpose: keep terminal-state checks exhaustive against the exported terminal
 * union rather than hard-coded differently at each caller.
 *
 * Consumer: decideUnitRuntimeDispatch and query summary generation.
 */
export function isTerminalUnitRuntimeStatus(status) {
	return UNIT_RUNTIME_TERMINAL_STATUSES.includes(status);
}

/**
 * Store unit completion pattern in memory for learning.
 *
 * Purpose: Record what type of units succeed/fail with what outcomes so UOK
 * can learn patterns and improve future dispatch decisions.
 *
 * Consumer: UOK auto-dispatch for enhanced unit ranking and pattern matching.
 */
export async function recordUnitOutcomeInMemory(unit, status, result) {
	if (!isDbAvailable()) return;

	try {
		const unitType = unit.type || unit.unitType || "unknown";
		const isSuccess = status === "completed";
		const confidence = isSuccess ? 0.9 : 0.5;
		const outcome = result?.outcome || status;

		const pattern = isSuccess
			? `Unit type '${unitType}' succeeded with outcome: ${outcome}`
			: `Unit type '${unitType}' failed with status: ${status} (${result?.error || "no error info"})`;

		await createMemory({
			category: "pattern",
			content: pattern,
			confidence,
			source_unit_type: unitType,
			source_unit_id: unit.id ?? unit.unitId ?? null,
		});
	} catch (_err) {
		// Degrade gracefully - memory failures do not block UOK
	}
}

/**
 * Returns the normalized UOK runtime state embedded in a runtime record.
 *
 * Purpose: let legacy records with only `phase` still participate in retry and
 * query policy while new records persist explicit runtime state fields.
 *
 * Consumer: decideUnitRuntimeDispatch and headless query summaries.
 */
export function getUnitRuntimeState(record) {
	const status = record.status ?? inferStatusFromPhase(record.phase, record);
	const retryCount = record.retryCount ?? record.recoveryAttempts ?? 0;
	const maxRetries = record.maxRetries ?? DEFAULT_UNIT_RUNTIME_MAX_RETRIES;
	return {
		status,
		retryCount,
		maxRetries,
		lastHeartbeatAt: record.lastHeartbeatAt ?? null,
		lastProgressAt: record.lastProgressAt,
		lastOutputAt: record.lastOutputAt ?? null,
		outputPath: record.outputPath ?? null,
		watchdogReason: record.watchdogReason ?? null,
		notifiedAt: record.notifiedAt ?? null,
	};
}
/**
 * Returns true for synthetic units that must be reset before rerun.
 *
 * Purpose: prevent synthetic orchestration units such as parallel research from
 * looping after failure while preserving normal task retry behavior.
 *
 * Consumer: decideUnitRuntimeDispatch.
 */
export function isSyntheticUnitRuntime(record) {
	if (!record) return false;
	return (
		record.unitType === "synthetic" ||
		(record.unitId ?? "").includes("parallel-research")
	);
}
/**
 * Decides whether a unit runtime record permits dispatch, retry, notify, or block.
 *
 * Purpose: enforce retry budgets and explicit reset requirements before callers
 * schedule another copy of a failed or stale unit.
 *
 * Consumer: unit-runtime projection tests and headless query runtime summaries.
 */
export function decideUnitRuntimeDispatch(record, options = {}) {
	if (!record) {
		return {
			action: "dispatch",
			reasonCode: "no-runtime-record",
			retryCount: 0,
			maxRetries: DEFAULT_UNIT_RUNTIME_MAX_RETRIES,
			retryBudgetRemaining: DEFAULT_UNIT_RUNTIME_MAX_RETRIES,
		};
	}
	const state = getUnitRuntimeState(record);
	const remaining = retryBudgetRemaining(state.retryCount, state.maxRetries);
	const common = {
		retryCount: state.retryCount,
		maxRetries: state.maxRetries,
		retryBudgetRemaining: remaining,
	};
	if (state.notifiedAt !== null) {
		return { action: "skip", reasonCode: "already-notified", ...common };
	}
	if (state.status === "notified") {
		return { action: "skip", reasonCode: "notified", ...common };
	}
	if (state.status === "queued") {
		return { action: "dispatch", reasonCode: "queued", ...common };
	}
	if (!isTerminalUnitRuntimeStatus(state.status)) {
		return { action: "skip", reasonCode: "active-or-claimed", ...common };
	}
	const synthetic = options.synthetic ?? isSyntheticUnitRuntime(record);
	if (synthetic && state.status !== "completed") {
		return {
			action: "block",
			reasonCode: "synthetic-reset-required",
			...common,
		};
	}
	if (state.status === "runaway-recovered") {
		return {
			action: "block",
			reasonCode: "runaway-recovery-reset-required",
			...common,
		};
	}
	if (RETRYABLE_TERMINAL_STATUSES.has(state.status)) {
		if (remaining > 0) {
			return {
				action: "retry",
				reasonCode: "retry-budget-available",
				...common,
			};
		}
		return { action: "block", reasonCode: "retry-budget-exhausted", ...common };
	}
	if (
		state.status === "completed" ||
		state.status === "blocked" ||
		state.status === "cancelled"
	) {
		return {
			action: "notify",
			reasonCode: "terminal-ready-to-notify",
			...common,
		};
	}
	return { action: "skip", reasonCode: "terminal-nonretryable", ...common };
}
function runtimeDir(basePath) {
	return join(sfRoot(basePath), "runtime", "units");
}
function runtimePath(basePath, unitType, unitId) {
	const sanitizedUnitType = unitType.replace(/[/]/g, "-");
	const sanitizedUnitId = unitId.replace(/[/]/g, "-");
	return join(
		runtimeDir(basePath),
		`${sanitizedUnitType}-${sanitizedUnitId}.json`,
	);
}
// ─── In-memory runtime record cache ─────────────────────────────────────────
// Avoids repeated disk reads for the same unit within a single dispatch cycle.
const _runtimeCache = new Map();
const MAX_RUNTIME_CACHE_SIZE = 5000;

function enforceRuntimeCacheBounds() {
	if (_runtimeCache.size <= MAX_RUNTIME_CACHE_SIZE) return;
	// LRU eviction: remove oldest entries (first 20% of cache)
	const entriesToRemove = Math.floor(MAX_RUNTIME_CACHE_SIZE * 0.2);
	const keys = _runtimeCache.keys();
	for (let i = 0; i < entriesToRemove; i++) {
		const next = keys.next();
		if (next.done) break;
		_runtimeCache.delete(next.value);
	}
}
function readUnitRuntimeRecordFromDisk(path) {
	if (!existsSync(path)) return null;
	try {
		return JSON.parse(readFileSync(path, "utf-8"));
	} catch {
		return null;
	}
}
export function writeUnitRuntimeRecord(
	basePath,
	unitType,
	unitId,
	startedAt,
	updates = {},
) {
	const dir = runtimeDir(basePath);
	mkdirSync(dir, { recursive: true });
	const path = runtimePath(basePath, unitType, unitId);
	const prev = _runtimeCache.get(path) ?? null;
	const phase =
		updates.phase ??
		(updates.status ? phaseForStatus(updates.status) : prev?.phase) ??
		"dispatched";
	const status =
		updates.status ??
		(updates.phase || !prev?.status
			? inferStatusFromPhase(phase, {
					runawayGuardPause:
						updates.runawayGuardPause ?? prev?.runawayGuardPause,
				})
			: prev.status);
	const recoveryAttempts = hasUpdate(updates, "recoveryAttempts")
		? (updates.recoveryAttempts ?? 0)
		: (prev?.recoveryAttempts ?? 0);
	const retryCount = hasUpdate(updates, "retryCount")
		? (updates.retryCount ?? 0)
		: hasUpdate(updates, "recoveryAttempts")
			? (updates.recoveryAttempts ?? 0)
			: (prev?.retryCount ?? recoveryAttempts ?? 0);
	const lineage = updates.lineageEvent
		? recordUnitLineageEvent(prev?.lineage ?? { unitType, unitId }, {
				unitType,
				unitId,
				...updates.lineageEvent,
			})
		: updates.lineage
			? normalizeUnitLineage({ unitType, unitId, ...updates.lineage })
			: prev?.lineage;
	const next = {
		version: 1,
		unitType,
		unitId,
		startedAt,
		updatedAt: Date.now(),
		phase,
		status,
		wrapupWarningSent:
			updates.wrapupWarningSent ?? prev?.wrapupWarningSent ?? false,
		continueHereFired:
			updates.continueHereFired ?? prev?.continueHereFired ?? false,
		timeoutAt: hasUpdate(updates, "timeoutAt")
			? (updates.timeoutAt ?? null)
			: (prev?.timeoutAt ?? null),
		lastHeartbeatAt: hasUpdate(updates, "lastHeartbeatAt")
			? (updates.lastHeartbeatAt ?? null)
			: (prev?.lastHeartbeatAt ?? startedAt),
		lastProgressAt:
			updates.lastProgressAt ?? prev?.lastProgressAt ?? Date.now(),
		progressCount: updates.progressCount ?? prev?.progressCount ?? 0,
		lastProgressKind:
			updates.lastProgressKind ?? prev?.lastProgressKind ?? "dispatch",
		lastOutputAt: hasUpdate(updates, "lastOutputAt")
			? (updates.lastOutputAt ?? null)
			: (prev?.lastOutputAt ?? null),
		outputPath: hasUpdate(updates, "outputPath")
			? (updates.outputPath ?? null)
			: (prev?.outputPath ?? null),
		watchdogReason: hasUpdate(updates, "watchdogReason")
			? (updates.watchdogReason ?? null)
			: (prev?.watchdogReason ?? null),
		notifiedAt: hasUpdate(updates, "notifiedAt")
			? (updates.notifiedAt ?? null)
			: (prev?.notifiedAt ?? null),
		recovery: updates.recovery ?? prev?.recovery,
		recoveryAttempts,
		retryCount,
		maxRetries:
			updates.maxRetries ??
			prev?.maxRetries ??
			DEFAULT_UNIT_RUNTIME_MAX_RETRIES,
		lastRecoveryReason: updates.lastRecoveryReason ?? prev?.lastRecoveryReason,
		runawayGuardPause: updates.runawayGuardPause ?? prev?.runawayGuardPause,
		...(lineage ? { lineage } : {}),
	};
	writeFileSync(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
	_runtimeCache.set(path, next);
	enforceRuntimeCacheBounds();
	return next;
}
export function readUnitRuntimeRecord(basePath, unitType, unitId) {
	const path = runtimePath(basePath, unitType, unitId);
	const cached = _runtimeCache.get(path);
	if (cached !== undefined) return cached;
	const record = readUnitRuntimeRecordFromDisk(path);
	if (record !== null) {
		_runtimeCache.set(path, record);
		enforceRuntimeCacheBounds();
	}
	return record;
}
export function clearUnitRuntimeRecord(basePath, unitType, unitId) {
	const path = runtimePath(basePath, unitType, unitId);
	_runtimeCache.delete(path);
	if (existsSync(path)) unlinkSync(path);
}
/**
 * Removes every unit-runtime file stuck in `runaway-recovered` so dispatch can resume.
 *
 * Purpose: runaway watchdog recovery leaves terminal records that
 * `decideUnitRuntimeDispatch` intentionally blocks (`runaway-recovery-reset-required`)
 * until an operator/session boundary clears them. Clearing at autonomous entry and
 * guided-flow self-heal matches treating a new launch as that boundary so workflows
 * are not wedged indefinitely.
 *
 * Consumer: autoLoop bootstrap and guided-flow `selfHealRuntimeRecords`.
 *
 * Returns: count of runtime files removed.
 */
export function clearRunawayRecoveredRuntimeRecords(basePath) {
	let cleared = 0;
	for (const record of listUnitRuntimeRecords(basePath)) {
		const unitType = record.unitType;
		const unitId = record.unitId;
		if (
			typeof unitType !== "string" ||
			unitType.length === 0 ||
			typeof unitId !== "string" ||
			unitId.length === 0
		) {
			continue;
		}
		if (getUnitRuntimeState(record).status !== "runaway-recovered") continue;
		clearUnitRuntimeRecord(basePath, unitType, unitId);
		cleared++;
	}
	return cleared;
}
/**
 * Return all runtime records currently on disk for `basePath`.
 * Returns an empty array if the runtime directory does not exist.
 */
export function listUnitRuntimeRecords(basePath) {
	const dir = runtimeDir(basePath);
	if (!existsSync(dir)) return [];
	const results = [];
	for (const file of readdirSync(dir)) {
		if (!file.endsWith(".json")) continue;
		try {
			const raw = readFileSync(join(dir, file), "utf-8");
			const record = JSON.parse(raw);
			results.push(record);
		} catch {
			// Skip malformed files
		}
	}
	return results;
}
export async function inspectExecuteTaskDurability(basePath, unitId) {
	const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
	if (!mid || !sid || !tid) return null;
	const planAbs = resolveSliceFile(basePath, mid, sid, "PLAN");
	const summaryAbs = resolveTaskFile(basePath, mid, sid, tid, "SUMMARY");
	const planPath = relSliceFile(basePath, mid, sid, "PLAN");
	const summaryPath = relTaskFile(basePath, mid, sid, tid, "SUMMARY");
	const planContent = planAbs ? await loadFile(planAbs) : null;
	const summaryExists = !!(summaryAbs && existsSync(summaryAbs));
	const escapedTid = tid.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
	const taskChecked =
		!!planContent &&
		new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m").test(planContent);
	// Prefer DB task status (authoritative) over STATE.md (display projection
	// that can lag by a loop iteration). Fall back to STATE.md when DB is
	// unavailable (e.g. in unit tests that don't initialise the database).
	let nextActionAdvanced;
	if (isDbAvailable()) {
		const taskRow = getTask(mid, sid, tid);
		const taskStatus = taskRow?.status ?? "pending";
		nextActionAdvanced =
			taskStatus !== "pending" && taskStatus !== "in_progress";
	} else {
		const stateAbs = join(sfRoot(basePath), "STATE.md");
		const stateContent = existsSync(stateAbs)
			? readFileSync(stateAbs, "utf-8")
			: "";
		nextActionAdvanced = !new RegExp(`Execute ${escapedTid}\\b`).test(
			stateContent,
		);
	}
	// Must-have coverage: load task plan and count mentions in summary
	let mustHaveCount = 0;
	let mustHavesMentionedInSummary = 0;
	const taskPlanAbs = resolveTaskFile(basePath, mid, sid, tid, "PLAN");
	if (taskPlanAbs) {
		const taskPlanContent = await loadFile(taskPlanAbs);
		if (taskPlanContent) {
			const mustHaves = parseTaskPlanMustHaves(taskPlanContent);
			mustHaveCount = mustHaves.length;
			if (mustHaveCount > 0 && summaryExists && summaryAbs) {
				const summaryContent = await loadFile(summaryAbs);
				if (summaryContent) {
					mustHavesMentionedInSummary = countMustHavesMentionedInSummary(
						mustHaves,
						summaryContent,
					);
				}
			}
		}
	}
	return {
		planPath,
		summaryPath,
		summaryExists,
		taskChecked,
		nextActionAdvanced,
		mustHaveCount,
		mustHavesMentionedInSummary,
	};
}
export function formatExecuteTaskRecoveryStatus(status) {
	const missing = [];
	if (!status.summaryExists)
		missing.push(`summary missing (${status.summaryPath})`);
	if (!status.taskChecked)
		missing.push(`task checkbox unchecked in ${status.planPath}`);
	if (!status.nextActionAdvanced)
		missing.push("state next action still points at the timed-out task");
	if (
		status.mustHaveCount > 0 &&
		status.mustHavesMentionedInSummary < status.mustHaveCount
	) {
		missing.push(
			`must-have gap: ${status.mustHavesMentionedInSummary} of ${status.mustHaveCount} must-haves addressed in summary`,
		);
	}
	return missing.length > 0
		? missing.join("; ")
		: "all durable task artifacts present";
}

/**
 * Read the runtime record for a unit and return structured recovery diagnostics.
 *
 * Purpose: surface runtime record state for post-mortem debugging of autonomous
 * failures without requiring humans to parse `.sf/runtime/units/*.json` manually.
 *
 * Consumer: `sf status recovery` CLI command and verification evidence enrichment.
 */
export function getRecoveryDiagnostics(basePath, unitType, unitId) {
	const record = readUnitRuntimeRecord(basePath, unitType, unitId);
	if (!record) {
		return null;
	}
	const state = getUnitRuntimeState(record);
	const lineageSummary = record.lineage
		? {
				status: record.lineage.status,
				workerCount: record.lineage.workerSessionIds?.length ?? 0,
				eventCount: record.lineage.events?.length ?? 0,
			}
		: null;
	return {
		unitType,
		unitId,
		status: state.status,
		retryCount: state.retryCount,
		maxRetries: state.maxRetries,
		lastRecoveryReason: record.lastRecoveryReason ?? null,
		progressCount: record.progressCount ?? 0,
		lastProgressKind: record.lastProgressKind ?? "dispatch",
		recoveryAttempts: record.recoveryAttempts ?? 0,
		lineageSummary,
		updatedAt: record.updatedAt ?? null,
		startedAt: record.startedAt ?? null,
	};
}
// ─── Stale slice runtime record reconciliation ──────────────────────────────
/**
 * Clear unit runtime records for complete-slice units that are in a terminal
 * non-completed state (cancelled, failed, stale) but whose slice is actually
 * complete in the DB and has a valid SUMMARY.md.
 *
 * Purpose: prevent the pi runtime flow-audit from emitting false-positive
 * stale-dispatch warnings for slices that completed successfully on retry.
 * The flow-audit reads journal/runtime state but does not check for later
 * successful retries or existing artifact files (#sf-moqv5o7h-vaabu6).
 *
 * Consumer: bootstrapAutoSession in auto-start.ts, called after
 * cleanStaleRuntimeUnits.
 */
export function reconcileStaleCompleteSliceRecords(basePath) {
	const dir = runtimeDir(basePath);
	if (!existsSync(dir)) return { cleared: 0, details: [] };
	let cleared = 0;
	const details = [];
	for (const file of readdirSync(dir)) {
		if (!file.endsWith(".json")) continue;
		let record;
		try {
			record = JSON.parse(readFileSync(join(dir, file), "utf-8"));
		} catch {
			continue;
		}
		if (record.unitType !== "complete-slice") continue;
		const state = getUnitRuntimeState(record);
		// Only target terminal non-completed states that could trigger
		// flow-audit warnings.
		if (
			!["cancelled", "failed", "stale", "runaway-recovered"].includes(
				state.status,
			)
		)
			continue;
		const { milestone: mid, slice: sid } = parseUnitId(record.unitId);
		if (!mid || !sid) continue;
		// DB check: slice status must be "complete"
		let dbComplete = false;
		if (isDbAvailable()) {
			try {
				const sliceRow = getSlice(mid, sid);
				dbComplete = sliceRow?.status === "complete";
			} catch {
				// DB read failure — skip this record rather than risk data loss
				continue;
			}
		}
		if (!dbComplete) continue;
		// Artifact check: SUMMARY.md must exist with a valid completed_at
		const summaryPath = resolveSliceFile(basePath, mid, sid, "SUMMARY");
		let artifactValid = false;
		if (summaryPath && existsSync(summaryPath)) {
			try {
				const content = readFileSync(summaryPath, "utf-8");
				const summary = parseSummary(content);
				artifactValid = !!summary.frontmatter.completed_at;
			} catch {
				artifactValid = false;
			}
		}
		if (!artifactValid) continue;
		// All checks pass — clear the stale runtime record
		try {
			unlinkSync(join(dir, file));
			_runtimeCache.delete(join(dir, file));
			cleared++;
			details.push(`${record.unitId} (was ${state.status})`);
		} catch (_err) {
			// Non-fatal — record stays, but at least we tried
		}
	}
	return { cleared, details };
}

/**
 * Clear runtime records whose durable artifacts already prove completion.
 *
 * Purpose: recover from crashes, process timeouts, or hard exits that happen
 * after a unit wrote its durable completion artifacts but before the in-memory
 * finalizer cleared `.sf/runtime/units/*.json`.
 *
 * Consumer: autonomous mode bootstrap before dispatching the next autonomous unit.
 */
export async function reconcileDurableCompleteUnitRuntimeRecords(basePath) {
	const dir = runtimeDir(basePath);
	if (!existsSync(dir)) return { cleared: 0, details: [] };
	let cleared = 0;
	const details = [];
	for (const file of readdirSync(dir)) {
		if (!file.endsWith(".json")) continue;
		const abs = join(dir, file);
		let record;
		try {
			record = JSON.parse(readFileSync(abs, "utf-8"));
		} catch {
			continue;
		}
		if (!record.unitType || !record.unitId) continue;
		let durableComplete = false;
		if (record.unitType === "execute-task") {
			const status = await inspectExecuteTaskDurability(
				basePath,
				record.unitId,
			);
			durableComplete = !!(
				status?.summaryExists &&
				status.taskChecked &&
				status.nextActionAdvanced
			);
		} else if (record.unitType === "complete-slice") {
			const { milestone: mid, slice: sid } = parseUnitId(record.unitId);
			if (mid && sid) {
				let dbComplete = false;
				if (isDbAvailable()) {
					try {
						const sliceRow = getSlice(mid, sid);
						dbComplete = sliceRow?.status === "complete";
					} catch {
						dbComplete = false;
					}
				}
				const summaryPath = resolveSliceFile(basePath, mid, sid, "SUMMARY");
				let artifactValid = false;
				if (summaryPath && existsSync(summaryPath)) {
					try {
						const content = readFileSync(summaryPath, "utf-8");
						const summary = parseSummary(content);
						artifactValid = !!summary.frontmatter.completed_at;
					} catch {
						artifactValid = false;
					}
				}
				durableComplete = dbComplete && artifactValid;
			}
		}
		if (!durableComplete) continue;
		try {
			unlinkSync(abs);
			_runtimeCache.delete(abs);
			cleared++;
			const state = getUnitRuntimeState(record);
			details.push(`${record.unitType} ${record.unitId} (was ${state.status})`);
		} catch {
			// Non-fatal — leave the record for the next bootstrap/doctor pass.
		}
	}
	return { cleared, details };
}
