475 lines
12 KiB
JavaScript
475 lines
12 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import {
|
|
existsSync,
|
|
mkdirSync,
|
|
mkdtempSync,
|
|
readFileSync,
|
|
rmSync,
|
|
} from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { afterEach, test } from "vitest";
|
|
import {
|
|
closeDatabase,
|
|
getUokRuns,
|
|
openDatabase,
|
|
recordUokRunExit,
|
|
recordUokRunStart,
|
|
} from "../sf-db.js";
|
|
import {
|
|
recordUokKernelTermination,
|
|
runAutoLoopWithUok,
|
|
} from "../uok/kernel.js";
|
|
import {
|
|
buildParityReport,
|
|
hasCurrentParityWarning,
|
|
parseParityEvents,
|
|
UNMATCHED_RUN_STALE_MS,
|
|
writeParityReport,
|
|
} from "../uok/parity-report.js";
|
|
|
|
const NOW = Date.parse("2026-05-06T00:00:00.000Z");
|
|
const tmpRoots = [];
|
|
|
|
afterEach(() => {
|
|
closeDatabase();
|
|
for (const dir of tmpRoots.splice(0)) {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
function makeProject() {
|
|
const root = mkdtempSync(join(tmpdir(), "sf-uok-parity-"));
|
|
tmpRoots.push(root);
|
|
mkdirSync(join(root, ".sf", "runtime"), { recursive: true });
|
|
return root;
|
|
}
|
|
|
|
function testDeps(preferences = {}) {
|
|
return {
|
|
loadEffectiveSFPreferences() {
|
|
return {
|
|
preferences: {
|
|
uok: {
|
|
enabled: true,
|
|
audit_envelope: { enabled: false },
|
|
audit_unified: { enabled: false },
|
|
...preferences.uok,
|
|
},
|
|
},
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
function testCtx(sessionId = "session-test") {
|
|
return {
|
|
sessionManager: {
|
|
getSessionId() {
|
|
return sessionId;
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
function readProjectParityEvents(projectRoot) {
|
|
const path = join(projectRoot, ".sf", "runtime", "uok-parity.jsonl");
|
|
assert.equal(existsSync(path), true);
|
|
return parseParityEvents(readFileSync(path, "utf-8"));
|
|
}
|
|
|
|
test("buildParityReport_legacy_anonymous_missing_exit_is_historical_not_current", () => {
|
|
const report = buildParityReport(
|
|
[
|
|
{ ts: "2026-05-05T17:21:52.209Z", path: "uok-kernel", phase: "enter" },
|
|
{
|
|
ts: "2026-05-05T17:25:52.439Z",
|
|
path: "uok-kernel",
|
|
phase: "exit",
|
|
status: "ok",
|
|
},
|
|
{ ts: "2026-05-05T17:25:18.803Z", path: "uok-kernel", phase: "enter" },
|
|
],
|
|
"/tmp/uok-parity.jsonl",
|
|
NOW,
|
|
);
|
|
|
|
assert.equal(report.legacyMissingExitEvents, 1);
|
|
assert.equal(report.missingExitEvents, 0);
|
|
assert.deepEqual(report.criticalMismatches, []);
|
|
assert.equal(hasCurrentParityWarning(report), false);
|
|
});
|
|
|
|
test("buildParityReport_fresh_run_missing_exit_is_current_warning", () => {
|
|
const report = buildParityReport(
|
|
[
|
|
{
|
|
ts: new Date(NOW - 5_000).toISOString(),
|
|
runId: "uok-run-1",
|
|
path: "uok-kernel",
|
|
phase: "enter",
|
|
},
|
|
],
|
|
"/tmp/uok-parity.jsonl",
|
|
NOW,
|
|
);
|
|
|
|
assert.equal(report.missingExitEvents, 1);
|
|
assert.equal(report.freshUnmatchedRuns[0].runId, "uok-run-1");
|
|
assert.match(report.criticalMismatches[0], /uok-run-1/);
|
|
assert.equal(hasCurrentParityWarning(report), true);
|
|
});
|
|
|
|
test("buildParityReport_stale_run_missing_exit_is_historical_not_current", () => {
|
|
const report = buildParityReport(
|
|
[
|
|
{
|
|
ts: new Date(NOW - UNMATCHED_RUN_STALE_MS - 1_000).toISOString(),
|
|
runId: "uok-old-run",
|
|
path: "uok-kernel",
|
|
phase: "enter",
|
|
},
|
|
],
|
|
"/tmp/uok-parity.jsonl",
|
|
NOW,
|
|
);
|
|
|
|
assert.equal(report.missingExitEvents, 0);
|
|
assert.equal(report.historicalUnmatchedRuns[0].runId, "uok-old-run");
|
|
assert.deepEqual(report.criticalMismatches, []);
|
|
assert.equal(hasCurrentParityWarning(report), false);
|
|
});
|
|
|
|
test("buildParityReport_run_exit_balances_enter", () => {
|
|
const report = buildParityReport(
|
|
[
|
|
{
|
|
ts: new Date(NOW - 10_000).toISOString(),
|
|
runId: "uok-run-2",
|
|
path: "uok-kernel",
|
|
phase: "enter",
|
|
},
|
|
{
|
|
ts: new Date(NOW - 5_000).toISOString(),
|
|
runId: "uok-run-2",
|
|
path: "uok-kernel",
|
|
phase: "exit",
|
|
status: "ok",
|
|
},
|
|
],
|
|
"/tmp/uok-parity.jsonl",
|
|
NOW,
|
|
);
|
|
|
|
assert.equal(report.missingExitEvents, 0);
|
|
assert.deepEqual(report.unmatchedRuns, []);
|
|
assert.equal(hasCurrentParityWarning(report), false);
|
|
});
|
|
|
|
test("uok_run_ledger_records_lifecycle_as_primary_state", () => {
|
|
openDatabase(":memory:");
|
|
recordUokRunStart({
|
|
runId: "uok-ledger-1",
|
|
sessionId: "session-ledger",
|
|
path: "uok-kernel",
|
|
flags: { enabled: true },
|
|
startedAt: new Date(NOW - 10_000).toISOString(),
|
|
});
|
|
recordUokRunExit({
|
|
runId: "uok-ledger-1",
|
|
sessionId: "session-ledger",
|
|
path: "uok-kernel",
|
|
flags: { enabled: true },
|
|
status: "ok",
|
|
endedAt: new Date(NOW - 5_000).toISOString(),
|
|
});
|
|
|
|
const runs = getUokRuns();
|
|
assert.equal(runs.length, 1);
|
|
assert.equal(runs[0].runId, "uok-ledger-1");
|
|
assert.equal(runs[0].status, "ok");
|
|
assert.equal(runs[0].sessionId, "session-ledger");
|
|
assert.equal(runs[0].flags.enabled, true);
|
|
|
|
const report = buildParityReport(
|
|
[],
|
|
"/tmp/uok-parity.jsonl",
|
|
NOW,
|
|
30_000,
|
|
runs,
|
|
);
|
|
assert.equal(report.ledgerRunCount, 1);
|
|
assert.equal(report.missingExitEvents, 0);
|
|
assert.equal(hasCurrentParityWarning(report), false);
|
|
});
|
|
|
|
test("buildParityReport_ledger_overrides_jsonl_lifecycle_counts", () => {
|
|
const report = buildParityReport(
|
|
[
|
|
{
|
|
ts: new Date(NOW - 5_000).toISOString(),
|
|
runId: "uok-ledger-clean",
|
|
path: "uok-kernel",
|
|
phase: "enter",
|
|
status: "unknown",
|
|
},
|
|
{
|
|
ts: new Date(NOW - 5_000).toISOString(),
|
|
path: "uok-kernel",
|
|
phase: "enter",
|
|
status: "unknown",
|
|
},
|
|
],
|
|
"/tmp/uok-parity.jsonl",
|
|
NOW,
|
|
30_000,
|
|
[
|
|
{
|
|
runId: "uok-ledger-clean",
|
|
sessionId: "session-ledger",
|
|
path: "uok-kernel",
|
|
status: "ok",
|
|
startedAt: new Date(NOW - 10_000).toISOString(),
|
|
endedAt: new Date(NOW - 5_000).toISOString(),
|
|
flags: { enabled: true },
|
|
},
|
|
],
|
|
);
|
|
|
|
assert.equal(report.ledgerRunCount, 1);
|
|
assert.equal(report.legacyMissingExitEvents, 1);
|
|
assert.equal(report.missingExitEvents, 0);
|
|
assert.deepEqual(report.criticalMismatches, []);
|
|
assert.equal(hasCurrentParityWarning(report), false);
|
|
});
|
|
|
|
test("buildParityReport_ledger_error_is_current_warning", () => {
|
|
const report = buildParityReport([], "/tmp/uok-parity.jsonl", NOW, 30_000, [
|
|
{
|
|
runId: "uok-ledger-error",
|
|
sessionId: "session-ledger",
|
|
path: "uok-kernel",
|
|
status: "error",
|
|
error: "ledger failure",
|
|
startedAt: new Date(NOW - 10_000).toISOString(),
|
|
endedAt: new Date(NOW - 5_000).toISOString(),
|
|
flags: { enabled: true },
|
|
},
|
|
]);
|
|
|
|
assert.equal(report.currentErrorEvents, 1);
|
|
assert.deepEqual(report.criticalMismatches, ["ledger failure"]);
|
|
assert.equal(hasCurrentParityWarning(report), true);
|
|
});
|
|
|
|
test("buildParityReport_fresh_error_is_current_but_stale_error_is_historical", () => {
|
|
const report = buildParityReport(
|
|
[
|
|
{
|
|
ts: new Date(NOW - 5_000).toISOString(),
|
|
runId: "uok-fresh-error",
|
|
path: "uok-kernel",
|
|
phase: "exit",
|
|
status: "error",
|
|
error: "fresh failure",
|
|
},
|
|
{
|
|
ts: new Date(NOW - UNMATCHED_RUN_STALE_MS - 1_000).toISOString(),
|
|
runId: "uok-old-error",
|
|
path: "uok-kernel",
|
|
phase: "exit",
|
|
status: "error",
|
|
error: "old failure",
|
|
},
|
|
],
|
|
"/tmp/uok-parity.jsonl",
|
|
NOW,
|
|
);
|
|
|
|
assert.equal(report.currentErrorEvents, 1);
|
|
assert.equal(report.historicalErrorEvents, 1);
|
|
assert.deepEqual(report.criticalMismatches, ["fresh failure"]);
|
|
assert.deepEqual(report.historicalCriticalMismatches, ["old failure"]);
|
|
assert.equal(hasCurrentParityWarning(report), true);
|
|
});
|
|
|
|
test("buildParityReport_stale_parity_diff_is_historical_not_current", () => {
|
|
const report = buildParityReport(
|
|
[
|
|
{
|
|
kind: "parity-diff",
|
|
ts: new Date(NOW - UNMATCHED_RUN_STALE_MS - 1_000).toISOString(),
|
|
plane: "gitops",
|
|
turnId: "T01",
|
|
match: false,
|
|
divergence: "legacy=commit uok=status-only",
|
|
},
|
|
],
|
|
"/tmp/uok-parity.jsonl",
|
|
NOW,
|
|
);
|
|
|
|
assert.equal(report.divergencesByPlane.gitops, 1);
|
|
assert.deepEqual(report.criticalMismatches, []);
|
|
assert.deepEqual(report.historicalCriticalMismatches, [
|
|
"[gitops] legacy=commit uok=status-only",
|
|
]);
|
|
assert.equal(hasCurrentParityWarning(report), false);
|
|
});
|
|
|
|
test("runAutoLoopWithUok_success_writes_balanced_run_id_heartbeats", async () => {
|
|
const projectRoot = makeProject();
|
|
const state = { basePath: projectRoot, autoStartTime: NOW };
|
|
let observerProvided = false;
|
|
|
|
await runAutoLoopWithUok({
|
|
ctx: testCtx("session-success"),
|
|
pi: {},
|
|
s: state,
|
|
deps: testDeps(),
|
|
async runKernelLoop(_ctx, _pi, _s, deps) {
|
|
observerProvided = Boolean(deps.uokObserver);
|
|
},
|
|
async runStandardLoop() {
|
|
throw new Error("standard loop should not run when uok is enabled");
|
|
},
|
|
});
|
|
|
|
assert.equal(observerProvided, true);
|
|
assert.equal(state.currentUokRunId, undefined);
|
|
const events = readProjectParityEvents(projectRoot);
|
|
assert.equal(events.length, 2);
|
|
assert.equal(events[0].phase, "enter");
|
|
assert.equal(events[1].phase, "exit");
|
|
assert.equal(events[1].status, "ok");
|
|
assert.equal(events[0].runId, events[1].runId);
|
|
const report = JSON.parse(
|
|
readFileSync(
|
|
join(projectRoot, ".sf", "runtime", "uok-parity-report.json"),
|
|
"utf-8",
|
|
),
|
|
);
|
|
assert.equal(report.missingExitEvents, 0);
|
|
assert.equal(hasCurrentParityWarning(report), false);
|
|
});
|
|
|
|
test("runAutoLoopWithUok_ignores_legacy_disabled_flag_and_uses_kernel_path", async () => {
|
|
const projectRoot = makeProject();
|
|
const state = { basePath: projectRoot, autoStartTime: NOW };
|
|
let kernelRan = false;
|
|
|
|
await runAutoLoopWithUok({
|
|
ctx: testCtx("session-force-uok"),
|
|
pi: {},
|
|
s: state,
|
|
deps: testDeps({ uok: { enabled: false } }),
|
|
async runKernelLoop(_ctx, _pi, _s, deps) {
|
|
kernelRan = Boolean(deps.uokObserver);
|
|
},
|
|
async runStandardLoop() {
|
|
throw new Error("legacy standard loop should not run");
|
|
},
|
|
});
|
|
|
|
assert.equal(kernelRan, true);
|
|
const events = readProjectParityEvents(projectRoot);
|
|
assert.equal(events[0].path, "uok-kernel");
|
|
assert.equal(events[1].path, "uok-kernel");
|
|
});
|
|
|
|
test("runAutoLoopWithUok_throw_still_writes_exit_and_current_error_report", async () => {
|
|
const projectRoot = makeProject();
|
|
const state = { basePath: projectRoot, autoStartTime: NOW };
|
|
|
|
await assert.rejects(
|
|
() =>
|
|
runAutoLoopWithUok({
|
|
ctx: testCtx("session-error"),
|
|
pi: {},
|
|
s: state,
|
|
deps: testDeps(),
|
|
async runKernelLoop() {
|
|
throw new Error("boom");
|
|
},
|
|
async runStandardLoop() {
|
|
throw new Error("standard loop should not run when uok is enabled");
|
|
},
|
|
}),
|
|
/boom/,
|
|
);
|
|
|
|
assert.equal(state.currentUokRunId, undefined);
|
|
const events = readProjectParityEvents(projectRoot);
|
|
assert.equal(events.length, 2);
|
|
assert.equal(events[0].phase, "enter");
|
|
assert.equal(events[1].phase, "exit");
|
|
assert.equal(events[1].status, "error");
|
|
assert.equal(events[1].error, "boom");
|
|
assert.equal(events[0].runId, events[1].runId);
|
|
const report = JSON.parse(
|
|
readFileSync(
|
|
join(projectRoot, ".sf", "runtime", "uok-parity-report.json"),
|
|
"utf-8",
|
|
),
|
|
);
|
|
assert.equal(report.missingExitEvents, 0);
|
|
assert.equal(report.currentErrorEvents, 1);
|
|
assert.deepEqual(report.criticalMismatches, ["boom"]);
|
|
assert.equal(hasCurrentParityWarning(report), true);
|
|
});
|
|
|
|
test("recordUokKernelTermination_marks_started_ledger_run_exited_on_signal", () => {
|
|
const projectRoot = makeProject();
|
|
openDatabase(":memory:");
|
|
recordUokRunStart({
|
|
runId: "uok-signal-run",
|
|
sessionId: "session-signal",
|
|
path: "uok-kernel",
|
|
flags: { enabled: true },
|
|
startedAt: new Date(NOW - 5_000).toISOString(),
|
|
});
|
|
|
|
const report = recordUokKernelTermination({
|
|
basePath: projectRoot,
|
|
runId: "uok-signal-run",
|
|
sessionId: "session-signal",
|
|
flags: { enabled: true },
|
|
status: "signal",
|
|
});
|
|
const runs = getUokRuns();
|
|
|
|
assert.equal(runs.length, 1);
|
|
assert.equal(runs[0].status, "signal");
|
|
assert.equal(typeof runs[0].endedAt, "string");
|
|
assert.equal(report.missingExitEvents, 0);
|
|
assert.equal(hasCurrentParityWarning(report), false);
|
|
const events = readProjectParityEvents(projectRoot);
|
|
assert.equal(events.length, 1);
|
|
assert.equal(events[0].phase, "exit");
|
|
assert.equal(events[0].status, "signal");
|
|
});
|
|
|
|
test("writeParityReport_recovers_started_ledger_runs_when_no_auto_lock_owner_exists", () => {
|
|
const projectRoot = makeProject();
|
|
openDatabase(":memory:");
|
|
recordUokRunStart({
|
|
runId: "uok-orphan-run",
|
|
sessionId: "session-orphan",
|
|
path: "uok-kernel",
|
|
flags: { enabled: true },
|
|
startedAt: new Date(NOW - 5_000).toISOString(),
|
|
});
|
|
|
|
const report = writeParityReport(projectRoot, NOW);
|
|
const runs = getUokRuns();
|
|
|
|
assert.equal(runs.length, 1);
|
|
assert.equal(runs[0].status, "recovered");
|
|
assert.equal(
|
|
runs[0].error,
|
|
"uok recovered missing exit: no live auto.lock owner",
|
|
);
|
|
assert.equal(report.missingExitEvents, 0);
|
|
assert.equal(report.freshUnmatchedRuns.length, 0);
|
|
assert.equal(hasCurrentParityWarning(report), false);
|
|
});
|