/** * StateKernel – thin façade over the run state that produces and validates * serialisable KernelSnapshot objects. * * The kernel never imports `state.ts` at the top level. Instead, callers * pass a `RunLoader` function (or the default dynamic-import loader is used) * so that circular dependency edges are avoided at module-initialisation time. */ import type { ActiveRun } from "../types.ts"; import type { EvidenceIndex } from "../evidence.ts"; import type { KdLedgerEvent } from "../ledger.ts"; import { generateSnapshot, validateSnapshot, type KernelSnapshot, type ConsistencyCheckResult, } from "./snapshots.ts"; // ── Re-export snapshot types so consumers only need this module ─────── export type { KernelSnapshot, ConsistencyCheckResult } from "./snapshots.ts"; // ── Run loader contract ────────────────────────────────────────────── /** Result returned by a RunLoader – the minimal data the kernel needs. */ export interface RunLoaderResult { /** The active run, or undefined if none exists. */ run: ActiveRun | undefined; /** Evidence index for the run (may be empty). */ evidenceIndex: EvidenceIndex; /** Ledger events for the run (may be empty). */ ledgerEvents: KdLedgerEvent[]; } /** * A function that loads the current run context. * * The default implementation uses `dynamic import()` to pull in * `state.ts`, `evidence.ts`, and `ledger.ts` at call-time rather than * at module-initialisation time, which breaks potential circular deps. */ export type RunLoader = (cwd: string) => Promise; // ── Default lazy loader ────────────────────────────────────────────── async function defaultRunLoader(cwd: string): Promise { const { readActiveRun } = await import("../state.ts"); const run = readActiveRun(cwd); if (!run) { return { run: undefined, evidenceIndex: { version: 1, entries: [] }, ledgerEvents: [] }; } const { readEvidenceIndex } = await import("../evidence.ts"); const { readLedgerEvents } = await import("../ledger.ts"); return { run, evidenceIndex: readEvidenceIndex(cwd, run), ledgerEvents: readLedgerEvents(cwd, run), }; } // ── StateKernel ────────────────────────────────────────────────────── export class StateKernel { private readonly loader: RunLoader; constructor(loader?: RunLoader) { this.loader = loader ?? defaultRunLoader; } /** * Produce a point-in-time snapshot of the active run. * * The returned object is a plain, fully-serialisable data bag – no * functions, no circular references, no class instances. */ async snapshot(cwd: string): Promise { const { run, evidenceIndex, ledgerEvents } = await this.loader(cwd); if (!run) return undefined; return generateSnapshot(run, evidenceIndex, ledgerEvents); } /** * Validate a snapshot for internal consistency. * * This is a pure function – it does not touch the filesystem. */ validate(snapshot: KernelSnapshot): ConsistencyCheckResult { return validateSnapshot(snapshot); } }