Implement AXIS parity UI and execution baseline

This commit is contained in:
2026-08-23 07:28:37 +08:00
parent 1325504544
commit 06f96f85e5
463 changed files with 84893 additions and 692 deletions

View File

@@ -0,0 +1,48 @@
import { ReplayTimeline, REPLAY_SCHEMA, fnv1a64 } from "./replay.mjs";
import { validateEventLog, validateSnapshot } from "./contracts.mjs";
export const TRACE_SCHEMA = "simulation-trace.v1";
function clone(value) { return structuredClone(value); }
export function createTrace({ contextId, timeline, state, task, tick = null } = {}) {
if (typeof contextId !== "string" || contextId.length === 0) throw new Error("trace_context_invalid");
if (!(timeline instanceof ReplayTimeline)) throw new Error("trace_timeline_invalid");
if (state === undefined) throw new Error("trace_state_missing");
const exported = timeline.export();
const latest = timeline.snapshots.latest();
if (!latest || latest.seq !== timeline.events.cursor || latest.tick !== timeline.events.tick) throw new Error("trace_snapshot_required");
const value = {
schema: TRACE_SCHEMA,
context_id: contextId,
seq: timeline.events.cursor,
tick: tick ?? timeline.events.tick,
state: clone(state),
state_hash: fnv1a64(state),
task: task ? clone(task) : null,
timeline: exported,
event_hash: fnv1a64(exported.events),
snapshot_hash: fnv1a64(latest),
};
return { ...value, trace_hash: fnv1a64(value) };
}
export function validateTrace(trace, { contextId } = {}) {
if (!trace || trace.schema !== TRACE_SCHEMA) throw new Error("trace_schema");
if (contextId !== undefined && trace.context_id !== contextId) throw new Error("trace_context_mismatch");
if (!Number.isSafeInteger(trace.seq) || trace.seq < 0 || !Number.isSafeInteger(trace.tick) || trace.tick < 0) throw new Error("trace_cursor_invalid");
if (fnv1a64(trace.state) !== trace.state_hash) throw new Error("trace_state_hash");
if (!trace.timeline || trace.timeline.schema !== REPLAY_SCHEMA) throw new Error("trace_timeline_schema");
validateEventLog(trace.timeline.events);
const snapshots = trace.timeline.snapshots?.snapshots;
if (!Array.isArray(snapshots) || snapshots.length === 0) throw new Error("trace_snapshot_missing");
for (const snapshot of snapshots) validateSnapshot(snapshot);
const timeline = new ReplayTimeline(trace.timeline);
const latest = timeline.snapshots.latest();
if (timeline.events.cursor !== trace.seq || timeline.events.tick !== trace.tick || !latest || latest.seq !== trace.seq || latest.tick !== trace.tick) throw new Error("trace_cursor_mismatch");
if (trace.event_hash !== fnv1a64(trace.timeline.events)) throw new Error("trace_event_hash");
if (trace.snapshot_hash !== fnv1a64(latest)) throw new Error("trace_snapshot_hash");
const unsigned = { ...trace }; delete unsigned.trace_hash;
if (trace.trace_hash !== fnv1a64(unsigned)) throw new Error("trace_hash");
return { trace: clone(trace), timeline };
}