import { fnv1a64 } from "./replay.mjs"; export const LEDGER_SCHEMA = "operation-ledger.v1"; export const LEDGER_PHASES = Object.freeze(["prepared", "executing", "verified", "committed", "aborted", "failed"]); function clone(value) { return structuredClone(value); } function text(value) { return typeof value === "string" && value.length > 0 ? value : null; } export class OperationLedger { constructor(entries = []) { this.entries = new Map(); for (const entry of entries) this.importEntry(entry); } intent(input) { return fnv1a64({ kind: input.kind, payload: input.payload ?? {}, context_id: input.context_id ?? null, session_id: input.session_id ?? null }); } prepare(input) { if (!text(input?.operation_id)) return { status: "untracked", replay: false, entry: null }; const intentHash = this.intent(input); const existing = this.entries.get(input.operation_id); if (existing) { if (existing.kind !== input.kind) throw new Error("ledger_operation_conflict"); return { status: existing.status, replay: existing.phase === "committed" || existing.status === "verified", entry: clone(existing) }; } const entry = { operation_id: input.operation_id, kind: input.kind, seq: input.seq, intent_hash: intentHash, status: "prepared", phase: "prepared", side_effect_count: 0, result_hash: null, commit_seq: null, response: null, }; this.entries.set(entry.operation_id, entry); return { status: entry.status, replay: false, entry: clone(entry) }; } execute(operationId) { const entry = this.require(operationId); if (entry.phase === "committed" || entry.phase === "aborted") return clone(entry); if (entry.phase !== "prepared") throw new Error("ledger_execute_phase"); entry.phase = "executing"; entry.status = "executing"; return clone(entry); } verify(operationId, response) { const entry = this.require(operationId); if (entry.phase === "committed") return clone(entry); if (entry.phase !== "executing" && entry.phase !== "prepared") throw new Error("ledger_verify_phase"); entry.phase = "verified"; entry.status = response?.payload?.status === "error" || response?.kind === "ERROR" ? "failed" : "verified"; entry.result_hash = fnv1a64(response?.payload ?? {}); const payload = response?.payload; // High-frequency fixed-tick responses are deltas. Keep only the compact // operation evidence in the ledger; the full delta is already delivered // to the client and retained by the frontend trace window. entry.response = payload?.snapshot_mode === "motion_delta" ? { ...response, payload: { status: payload.status, snapshot_mode: payload.snapshot_mode, simulation_tick: response.simulation_tick ?? null, program: payload.program ? { current_line: payload.program.current_line, program_status: payload.program.program_status, state_hash: payload.program.state_hash } : null } } : clone(response); return clone(entry); } commit(operationId, seq) { const entry = this.require(operationId); if (entry.phase === "committed") return clone(entry); if (entry.phase !== "verified") throw new Error("ledger_commit_phase"); if (entry.status === "failed") { entry.phase = "failed"; return clone(entry); } entry.phase = "committed"; entry.status = "verified"; entry.commit_seq = seq; entry.side_effect_count = 1; return clone(entry); } abort(operationId, reason = "abort_requested") { const entry = this.require(operationId); if (entry.phase === "committed") return { status: "too_late", entry: clone(entry) }; if (entry.phase === "aborted") return { status: "aborted", entry: clone(entry) }; entry.phase = "aborted"; entry.status = "aborted"; entry.abort_reason = reason; return { status: "aborted", entry: clone(entry) }; } reconcile(operationId) { if (!text(operationId)) throw new Error("ledger_operation_required"); const entry = this.entries.get(operationId); return entry ? { status: entry.phase === "committed" ? "committed" : entry.phase, entry: clone(entry) } : { status: "missing", entry: null }; } require(operationId) { const entry = this.entries.get(operationId); if (!entry) throw new Error("ledger_operation_missing"); return entry; } importEntry(entry) { if (!entry || typeof entry !== "object" || !text(entry.operation_id) || !text(entry.kind) || !LEDGER_PHASES.includes(entry.phase) || !["prepared", "executing", "verified", "aborted", "failed"].includes(entry.status)) throw new Error("ledger_entry_invalid"); if (this.entries.has(entry.operation_id)) throw new Error("ledger_operation_duplicate"); this.entries.set(entry.operation_id, clone(entry)); } export() { return { schema: LEDGER_SCHEMA, entries: [...this.entries.values()].sort((a, b) => a.operation_id.localeCompare(b.operation_id)).map(clone) }; } }