Files
malkuthroandClaude Opus 5 090eb460eb fix(snapshot): boot drift check no longer false-positives on unsaved edits (#278)
* fix(snapshot): boot drift check no longer false-positives on unsaved edits (#276)

The #161 boot-time drift guard flagged any canonical difference between
the tracked snapshot file and the live /userdata+picks state — which is
also what ordinary "kept working after the last named save" looks like,
so nearly every session booted into a false "drifted" warning and
periodic autosaves were misrouted to _unsaved_autosave/.

detectBootDrift() is now three-way: a mismatch counts as drift only when
the live state's fingerprint also matches the persisted last-named-save
baseline (it claims to be saved, yet the file disagrees — pinning the
out-of-band change to the file side). Otherwise it reports the honest
"unsaved". First-session baseline seeding in koolook_sidebar.js moved
after the drift check so a freshly seeded baseline can't fabricate the
claims-saved condition.

Tests: two new cases (unsaved-edits, no-baseline); drift-expecting cases
now establish the claims-saved baseline; the states-match and
canonicalization tests baseline first and assert reason is absent so
they pin the match branch rather than the new early exit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(snapshot): gate first-session baseline seeding on a proven boot check (#276)

Review follow-up. Ordering the drift check before the seeding was not
enough: seeding still ran whenever the fingerprint key was absent, so a
session whose tracked-preset name outlived its baseline (upgrade from
before the key existed, or a persist that lost to localStorage quota)
baselined whatever the live state happened to be. That both showed
"saved" for state never saved to the named file and handed the NEXT boot
a live state claiming saved against a file it never matched — a fresh
false "drifted", i.e. #276 re-opened from the other side.

Seeding is now gated on bootCheckProvedAligned(), a pure predicate
exported from snapshot.js (the entry point imports ComfyUI's app.js and
can't be loaded headlessly — same split as extension_guard.js's
claimSidebarRegistration). Only a boot check that reached the
fingerprint comparison and matched authorizes seeding; null and
reason-carrying outcomes prove nothing.

Also: branch the !claimsSaved log so the no-baseline case stops claiming
"unsaved edits since the last named save" when there is no such save,
and refresh the _autosaveSubdir redirect rationale, which still framed
the live state as the corrupt side.

Tests: predicate truth table, an end-to-end blocking case carrying a
counterfactual leg that proves the gate is load-bearing (ungated seeding
does flag drift on the next boot), and the allow case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 12:23:49 +02:00

812 lines
31 KiB
Python

"""Tests for the boot-time tracked-snapshot drift guard (#161, #276).
Covers the observable contracts the rest of the codebase depends on:
1. ``detectBootDrift()`` flags drift on file-vs-live mismatch — but
ONLY when the live state also matches the persisted last-named-save
baseline ("claims saved", #276). A mismatch with unsaved edits (or
with no baseline at all) reports "unsaved", never drift.
2. ``detectBootDrift()`` does NOT flag drift when the named file and
live state agree.
3. ``getSnapshotStatus()`` returns ``state: "drifted"`` while the
flag is set — with precedence over the localStorage-baseline
``"saved"`` state.
4. ``markStateSaved()`` clears the drift flag — Save and Load both
route through it, so this is the single point that retires the
warning.
The pre-load autosave routing (``_autosaveSubdir`` returning
``_unsaved_autosave/`` while drifted) is covered as a 5th case via
``writePreLoadAutosave`` — calling that exercises the same private
helper the periodic timer uses.
All tests stub ``fetch`` so the snapshot module operates against an
in-memory "library" without touching the real network.
"""
from __future__ import annotations
import subprocess
import textwrap
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[2]
def _run_node(source: str) -> subprocess.CompletedProcess[str]:
return subprocess.run(
["node", "--input-type=module"],
input=source,
cwd=REPO_ROOT,
text=True,
capture_output=True,
check=False,
)
# Common browser-globals + fetch-stub preamble used by every test. Kept
# as a python f-string so each test can append its own scenario body.
_PREAMBLE = """
import assert from "node:assert/strict";
import {
detectBootDrift,
getSnapshotStatus,
markStateSaved,
clearBootDrift,
isBootDrifted,
bootCheckProvedAligned,
setCurrentPresetName,
writePreLoadAutosave,
} from "./web/sidebar/snapshot.js";
import {
loadWorkflowsStore,
replaceAllWorkflows,
getAllWorkflowsForExport,
} from "./web/sidebar/workflows_store.js";
import { setAllPicks } from "./web/sidebar/picks_store.js";
setupBrowserStubs();
// In-memory "server" the fetch stub reads from / writes to. Tests
// arrange their scenario by mutating these before calling the SUT.
const SERVER = {
workflowsStore: { directories: {} },
presets: new Map(), // fileName (with .json) -> snapshot object
writes: [], // every POST captured for assertion
};
globalThis.fetch = async (url, init = {}) => {
const u = new URL(String(url), "http://localhost");
const pathOnly = u.pathname;
if (pathOnly.startsWith("/userdata/koolook_workflows.json")) {
if (init.method === "POST") {
SERVER.workflowsStore = JSON.parse(init.body);
return okJson("");
}
return okJson(JSON.stringify(SERVER.workflowsStore));
}
if (pathOnly === "/koolook/presets/file") {
const name = u.searchParams.get("name");
const dir = u.searchParams.get("dir") || "";
const key = (dir ? dir + "/" : "") + name;
if (init.method === "POST") {
SERVER.presets.set(key, JSON.parse(init.body));
SERVER.writes.push({ name, dir, body: JSON.parse(init.body) });
return okJson("");
}
if (init.method === "HEAD") {
return SERVER.presets.has(key)
? { ok: true, status: 200 }
: { ok: false, status: 404 };
}
const stored = SERVER.presets.get(key);
if (!stored) return { ok: false, status: 404, text: async () => "not found" };
return okJson(JSON.stringify(stored));
}
if (pathOnly === "/koolook/presets/info") {
return okJson(JSON.stringify({ path: "/tmp/koolook-presets", isDefault: true }));
}
return { ok: false, status: 404, text: async () => "", json: async () => ({}) };
};
function okJson(body) {
return {
ok: true,
status: 200,
text: async () => body,
json: async () => JSON.parse(body || "{}"),
};
}
function setupBrowserStubs() {
const storage = new Map();
globalThis.localStorage = {
getItem: (key) => storage.has(key) ? storage.get(key) : null,
setItem: (key, value) => storage.set(key, String(value)),
removeItem: (key) => storage.delete(key),
};
globalThis.CustomEvent = class CustomEvent { constructor(type) { this.type = type; } };
globalThis.window = { dispatchEvent() {}, addEventListener() {} };
Object.defineProperty(globalThis, "navigator", {
value: { clipboard: { writeText: async () => {} } },
configurable: true,
});
const makeEl = () => ({
className: "", textContent: "", style: {},
classList: { add() {}, remove() {} },
appendChild() {}, remove() {}, addEventListener() {},
setAttribute() {},
});
globalThis.document = {
createElement: makeEl,
body: { appendChild() {} },
head: { appendChild() {} },
querySelectorAll: () => [],
getElementById: () => null,
hidden: false,
};
}
"""
def _run(scenario_body: str) -> subprocess.CompletedProcess[str]:
return _run_node(_PREAMBLE + textwrap.dedent(scenario_body))
# =============================================================================
# 1. Drift detection on file-vs-live mismatch
# =============================================================================
def test_detect_drift_flags_mismatch() -> None:
"""The named snapshot file holds 2 workflows; the live store holds
a different 1-workflow shape, and the live state claims to be saved
(baseline matches). ``detectBootDrift()`` must flag this as drift
and console.warn with the diagnostics."""
result = _run(
"""
// Arrange: named file with workflows A + B.
SERVER.presets.set("Foo.json", {
kind: "koolook-snapshot",
version: 1,
name: "Foo",
exportedAt: "2026-05-29T10:00:00.000Z",
picks: ["EasyResize_Koolook"],
workflows: {
directories: {
"Renders": {
workflows: {
"A": { graph: { nodes: [{ id: 1 }] }, archived: false, module: false, tags: [] },
"B": { graph: { nodes: [{ id: 2 }] }, archived: false, module: false, tags: [] },
},
directories: {},
},
},
},
});
// Arrange: live store carries a single C workflow — clearly different.
SERVER.workflowsStore = {
directories: {
"Renders": {
workflows: {
"C": { graph: { nodes: [{ id: 3 }] }, archived: false, module: false, tags: [] },
},
directories: {},
},
},
};
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Foo");
// The live state must claim "saved" for the mismatch to count as
// drift (#276) — baseline it as a prior session's Save would have.
markStateSaved();
// Act
const outcome = await detectBootDrift("Foo");
// Assert
assert.equal(outcome.drifted, true, "outcome should report drift");
assert.equal(outcome.trackedName, "Foo");
assert.equal(isBootDrifted(), true, "module flag should be set");
assert.ok(outcome.diagnostics.fileFingerprintBytes > 0);
assert.ok(outcome.diagnostics.liveFingerprintBytes > 0);
"""
)
assert result.returncode == 0, result.stderr
def test_detect_drift_skips_when_states_match() -> None:
"""Named file and live state are byte-equal in payload (modulo the
snapshot envelope) — no drift flagged."""
result = _run(
"""
const sharedStore = {
directories: {
"Renders": {
workflows: {
"A": { graph: { nodes: [{ id: 1 }] }, archived: false, module: false, tags: [] },
},
directories: {},
},
},
};
SERVER.workflowsStore = sharedStore;
SERVER.presets.set("Bar.json", {
kind: "koolook-snapshot",
version: 1,
name: "Bar",
exportedAt: "2026-05-29T11:00:00.000Z",
picks: [],
workflows: sharedStore,
});
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Bar");
// Baseline the state so the claims-saved gate (#276) cannot be
// the reason for `drifted: false` — this test must pin the
// fingerprint MATCH branch, not the unsaved-edits early exit.
markStateSaved();
const outcome = await detectBootDrift("Bar");
assert.equal(outcome.drifted, false);
assert.equal(outcome.reason, undefined,
"match branch must not carry an unsaved-edits reason");
assert.equal(isBootDrifted(), false);
"""
)
assert result.returncode == 0, result.stderr
def test_detect_drift_skips_when_named_file_missing() -> None:
"""Tracked preset name but no file on disk — that's a distinct
failure mode the regular Load dialog already surfaces. Don't flag
drift; let the localStorage fingerprint baseline drive status."""
result = _run(
"""
SERVER.workflowsStore = { directories: {} };
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Missing");
const outcome = await detectBootDrift("Missing");
// outcome is null on the "file not readable" path
assert.equal(outcome, null);
assert.equal(isBootDrifted(), false);
"""
)
assert result.returncode == 0, result.stderr
# =============================================================================
# 2. Status precedence — "drifted" wins over "saved"
# =============================================================================
def test_status_drifted_wins_over_saved_baseline() -> None:
"""Even when the persisted localStorage fingerprint matches the
current live state (which would otherwise report "saved"), the
drift flag promotes the status to "drifted". This is the core
precedence rule that protects against a stale-but-matching
fingerprint baseline."""
result = _run(
"""
SERVER.workflowsStore = { directories: {} };
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Baz");
// Baseline: live state matches itself, so markStateSaved persists
// a fingerprint that satisfies the "saved" check.
markStateSaved();
let status = getSnapshotStatus();
assert.equal(status.state, "saved", "baseline should report saved");
// Now arrange a divergent named file and run the drift check.
// markStateSaved was just called, so the fingerprint baseline
// matches the live state — but the named file on disk doesn't,
// and that's the authoritative comparison.
SERVER.presets.set("Baz.json", {
kind: "koolook-snapshot",
version: 1,
name: "Baz",
exportedAt: "2026-05-29T12:00:00.000Z",
picks: ["EasyResize_Koolook", "EasyAIPipeline"],
workflows: { directories: { "X": { workflows: { "Y": { graph: { nodes: [] }, archived: false, module: false, tags: [] } }, directories: {} } } },
});
const outcome = await detectBootDrift("Baz");
assert.equal(outcome.drifted, true);
status = getSnapshotStatus();
assert.equal(status.state, "drifted", "drift should win precedence over saved baseline");
assert.equal(status.name, "Baz");
"""
)
assert result.returncode == 0, result.stderr
# =============================================================================
# 3. markStateSaved clears the drift flag (Save/Load realignment)
# =============================================================================
def test_mark_state_saved_clears_drift_flag() -> None:
"""A successful Save/Load means the named file and live state are
realigned. ``markStateSaved()`` is the single point both paths call;
it must retire the drift warning so the pill returns to "saved"
without the user having to dismiss anything."""
result = _run(
"""
SERVER.workflowsStore = { directories: {} };
SERVER.presets.set("Qux.json", {
kind: "koolook-snapshot",
version: 1,
name: "Qux",
exportedAt: "2026-05-29T13:00:00.000Z",
picks: ["EasyResize_Koolook"],
workflows: { directories: { "X": { workflows: { "Y": { graph: { nodes: [] }, archived: false, module: false, tags: [] } }, directories: {} } } },
});
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Qux");
// Claims-saved baseline from a prior session's Save (#276).
markStateSaved();
await detectBootDrift("Qux");
assert.equal(isBootDrifted(), true);
// User realigns by Saving — production path runs markStateSaved().
markStateSaved();
assert.equal(isBootDrifted(), false, "Save/Load must retire the drift warning");
const status = getSnapshotStatus();
assert.equal(status.state, "saved");
"""
)
assert result.returncode == 0, result.stderr
def test_clear_boot_drift_public_export_works() -> None:
"""The exported ``clearBootDrift()`` escape hatch (for direct
test-side reset) must flip the flag and refresh status."""
result = _run(
"""
SERVER.workflowsStore = { directories: {} };
SERVER.presets.set("Zap.json", {
kind: "koolook-snapshot",
version: 1,
name: "Zap",
exportedAt: "2026-05-29T14:00:00.000Z",
picks: ["EasyResize_Koolook"],
workflows: { directories: { "X": { workflows: { "Y": { graph: { nodes: [] }, archived: false, module: false, tags: [] } }, directories: {} } } },
});
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Zap");
// Claims-saved baseline from a prior session's Save (#276).
markStateSaved();
await detectBootDrift("Zap");
assert.equal(isBootDrifted(), true);
clearBootDrift();
assert.equal(isBootDrifted(), false);
"""
)
assert result.returncode == 0, result.stderr
# =============================================================================
# 4. Pre-load autosave routes to _unsaved_autosave/ while drifted
# =============================================================================
def test_pre_load_autosave_redirects_to_unsaved_when_drifted() -> None:
"""The same ``_autosaveSubdir`` helper that controls the periodic
timer also controls pre-load autosaves. While drifted, both must
route to ``_unsaved_autosave/`` so the named preset's recovery
folder stays clean."""
result = _run(
"""
SERVER.workflowsStore = { directories: {} };
SERVER.presets.set("Drifty.json", {
kind: "koolook-snapshot",
version: 1,
name: "Drifty",
exportedAt: "2026-05-29T15:00:00.000Z",
picks: ["EasyResize_Koolook"],
workflows: { directories: { "X": { workflows: { "Y": { graph: { nodes: [] }, archived: false, module: false, tags: [] } }, directories: {} } } },
});
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Drifty");
// Claims-saved baseline from a prior session's Save (#276).
markStateSaved();
await detectBootDrift("Drifty");
assert.equal(isBootDrifted(), true);
SERVER.writes.length = 0;
await writePreLoadAutosave("test-redirect");
const writtenDirs = SERVER.writes.map((w) => w.dir).filter(Boolean);
assert.ok(writtenDirs.length > 0, "pre-load autosave should write at least one file");
for (const d of writtenDirs) {
assert.equal(d, "_unsaved_autosave",
`pre-load autosave routed to "${d}" while drifted; expected _unsaved_autosave`);
}
"""
)
assert result.returncode == 0, result.stderr
def test_detect_drift_does_not_fire_on_normalize_only_shape_differences() -> None:
"""Cross-machine snapshot import + hand-edited files (the documented
NFS/SMB/Dropbox sync workflow this module supports) save fields in
shapes the live cache passes through ``normalizeWorkflowsStore`` —
``archived: "false"`` becomes ``archived: false``; missing
``directories: {}`` is filled in; non-string tag entries get dropped.
Without normalising the file side too, the first boot after a fresh
cross-machine import would false-positive as drift and route every
autosave to ``_unsaved_autosave/`` until the user re-saved.
This guards the symmetric-fingerprint contract: when the disk shape
and the live shape are equivalent *after* normalisation, no drift
is flagged.
"""
result = _run(
"""
// Live: post-normalize shape — strict bool, deduped tags, present
// directories: {}. This is what workflowsCache looks like the
// moment loadWorkflowsStore() returns.
SERVER.workflowsStore = {
directories: {
"Renders": {
workflows: {
"A": {
graph: { nodes: [{ id: 1 }] },
archived: false,
module: false,
tags: ["lit", "exterior"],
},
},
directories: {},
},
},
};
// File: pre-normalize shape — `archived` as a string literal,
// duplicate + whitespace-padded tag entries, missing
// `directories: {}` on the leaf node. Hand-edited / cross-
// machine import shape that should normalize to the live shape.
SERVER.presets.set("CrossMachine.json", {
kind: "koolook-snapshot",
version: 1,
name: "CrossMachine",
exportedAt: "2026-05-29T17:00:00.000Z",
picks: [],
workflows: {
directories: {
"Renders": {
workflows: {
"A": {
graph: { nodes: [{ id: 1 }] },
archived: "false",
tags: ["lit", " exterior", "exterior ", "lit"],
},
},
// directories field deliberately omitted — pre-v0.3
// shape that normalize fills with `{}`.
},
},
},
});
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("CrossMachine");
// Baseline the state so the claims-saved gate (#276) cannot be
// the reason for `drifted: false` — this test guards the
// canonicalization contract, so it must reach the fingerprint
// comparison and prove the shapes MATCH.
markStateSaved();
const outcome = await detectBootDrift("CrossMachine");
assert.equal(outcome.drifted, false,
"shape differences that normalize to the same canonical form should NOT trigger drift");
assert.equal(outcome.reason, undefined,
"canonically equal shapes must take the match branch, not the unsaved-edits exit");
assert.equal(isBootDrifted(), false);
"""
)
assert result.returncode == 0, result.stderr
def test_no_drift_when_live_has_unsaved_edits_since_baseline() -> None:
"""#276: the everyday case — the user saved a snapshot, then kept
working. The live state diverges from BOTH the named file and the
persisted last-named-save baseline. That is ordinary work-in-progress
("unsaved"), not corruption — flagging it as "drifted" made the pill
cry wolf on every boot and misrouted periodic autosaves to
``_unsaved_autosave/`` instead of the preset's recovery folder."""
result = _run(
"""
// Arrange: live store + baseline agree at "save time".
SERVER.workflowsStore = {
directories: {
"Renders": {
workflows: {
"A": { graph: { nodes: [{ id: 1 }] }, archived: false, module: false, tags: [] },
},
directories: {},
},
},
};
SERVER.presets.set("Wip.json", {
kind: "koolook-snapshot",
version: 1,
name: "Wip",
exportedAt: "2026-08-10T08:00:00.000Z",
picks: [],
workflows: SERVER.workflowsStore,
});
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Wip");
markStateSaved(); // baseline == live == file
// Act: the user keeps working — live store gains a workflow the
// named file does not have. Baseline is now stale on purpose.
await replaceAllWorkflows({
directories: {
"Renders": {
workflows: {
"A": { graph: { nodes: [{ id: 1 }] }, archived: false, module: false, tags: [] },
"B-new": { graph: { nodes: [{ id: 2 }] }, archived: false, module: false, tags: [] },
},
directories: {},
},
},
});
const outcome = await detectBootDrift("Wip");
// Assert: no drift — this is the "unsaved" state, not corruption.
assert.equal(outcome.drifted, false,
"post-save edits must not flag boot drift");
assert.equal(outcome.reason, "unsaved-edits");
assert.equal(isBootDrifted(), false);
const status = getSnapshotStatus();
assert.equal(status.state, "unsaved",
`expected honest "unsaved", got "${status.state}"`);
"""
)
assert result.returncode == 0, result.stderr
def test_no_drift_without_saved_baseline() -> None:
"""#276: with no persisted last-named-save baseline at all, the live
state cannot *claim* to be saved — so a file-vs-live mismatch is
indistinguishable from ordinary unsaved work and must not flag."""
result = _run(
"""
SERVER.workflowsStore = {
directories: {
"Renders": {
workflows: {
"C": { graph: { nodes: [{ id: 3 }] }, archived: false, module: false, tags: [] },
},
directories: {},
},
},
};
SERVER.presets.set("NoBase.json", {
kind: "koolook-snapshot",
version: 1,
name: "NoBase",
exportedAt: "2026-08-10T08:00:00.000Z",
picks: ["EasyResize_Koolook"],
workflows: { directories: {} },
});
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("NoBase");
// Deliberately NO markStateSaved() — no baseline exists.
const outcome = await detectBootDrift("NoBase");
assert.equal(outcome.drifted, false,
"mismatch without a claims-saved baseline must not flag drift");
assert.equal(outcome.reason, "unsaved-edits");
assert.equal(isBootDrifted(), false);
"""
)
assert result.returncode == 0, result.stderr
# =============================================================================
# 5. Seeding gate — bootCheckProvedAligned (#276 review)
# =============================================================================
def test_boot_check_proved_aligned_only_on_verified_match() -> None:
"""``koolook_sidebar.js`` seeds a first-session "saved" baseline only
when the boot check PROVED file ≡ live. Every other outcome — no
preset tracked / unreadable file (``null``), or a mismatch that fell
through to the unsaved-edits exit — must return false.
Seeding on an unproven boot baselines whatever the live state happens
to be, which hands the NEXT boot a live state that "claims saved"
against a file it never matched: a fresh false "drifted", i.e. #276
re-opened from the other side."""
result = _run(
"""
// Proven match — the only true case.
assert.equal(bootCheckProvedAligned({ drifted: false, trackedName: "A" }), true);
// Mismatch that fell through to the unsaved-edits exit.
assert.equal(
bootCheckProvedAligned({ drifted: false, trackedName: "A", reason: "unsaved-edits" }),
false, "unsaved-edits outcome proves nothing — must not seed");
// Real drift.
assert.equal(bootCheckProvedAligned({ drifted: true, trackedName: "A" }), false);
// No preset tracked, or the named file was unreadable.
assert.equal(bootCheckProvedAligned(null), false,
"null outcome proves nothing — must not seed");
assert.equal(bootCheckProvedAligned(undefined), false);
"""
)
assert result.returncode == 0, result.stderr
def test_seeding_gate_blocks_baseline_when_file_differs() -> None:
"""End-to-end on the real module: a session whose tracked-preset name
outlived its fingerprint baseline (upgrade path, or a persist that
lost to localStorage quota) and whose live state differs from the
named file must NOT be seeded — and a second boot against the same
unchanged state must still report "unsaved", never "drifted".
This is the regression the seeding gate exists to prevent; without it
the first boot writes a baseline equal to live, and the second boot
reads live == baseline + file != live and flags drift."""
result = _run(
"""
SERVER.workflowsStore = {
directories: {
"Renders": {
workflows: {
"live-only": { graph: { nodes: [{ id: 9 }] }, archived: false, module: false, tags: [] },
},
directories: {},
},
},
};
SERVER.presets.set("Upgraded.json", {
kind: "koolook-snapshot",
version: 1,
name: "Upgraded",
exportedAt: "2026-08-10T08:00:00.000Z",
picks: [],
workflows: { directories: {} },
});
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Upgraded");
// No markStateSaved() — the tracked name exists but no baseline
// was ever persisted. That is the reachable upgrade/quota state.
// --- Boot 1
const first = await detectBootDrift("Upgraded");
assert.equal(first.drifted, false);
assert.equal(bootCheckProvedAligned(first), false,
"unproven boot must not authorize baseline seeding");
// The sidebar entry point would call markStateSaved() here only if
// the gate returned true. It didn't, so the state stays unbaselined.
assert.equal(getSnapshotStatus().state, "unsaved");
// --- Boot 2 against the same unchanged state.
const second = await detectBootDrift("Upgraded");
assert.equal(second.drifted, false,
"second boot must not flag drift — nothing changed and nothing was saved");
assert.equal(isBootDrifted(), false);
assert.equal(getSnapshotStatus().state, "unsaved");
// --- Counterfactual: prove the gate is load-bearing, not decorative.
// Replay what the UNGATED seeding did — markStateSaved() on an
// unproven boot — and show the very next boot false-positives.
// If this leg ever stops flagging drift, the gate above has become
// redundant and this whole test can go.
markStateSaved();
const third = await detectBootDrift("Upgraded");
assert.equal(third.drifted, true,
"counterfactual must flag drift — otherwise the seeding gate guards nothing");
assert.equal(getSnapshotStatus().state, "drifted");
"""
)
assert result.returncode == 0, result.stderr
def test_seeding_gate_allows_baseline_when_file_matches() -> None:
"""The other side of the gate: when the boot check proves file ≡ live,
seeding is authorized and the pill reads "saved" from the start —
the original reason the seeding exists."""
result = _run(
"""
const sharedStore = {
directories: {
"X": {
workflows: {
"Y": { graph: { nodes: [] }, archived: false, module: false, tags: [] },
},
directories: {},
},
},
};
SERVER.workflowsStore = sharedStore;
SERVER.presets.set("Aligned.json", {
kind: "koolook-snapshot",
version: 1,
name: "Aligned",
exportedAt: "2026-08-10T08:00:00.000Z",
picks: [],
workflows: sharedStore,
});
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Aligned");
const outcome = await detectBootDrift("Aligned");
assert.equal(bootCheckProvedAligned(outcome), true,
"a proven match must authorize seeding");
// Entry point seeds here.
markStateSaved();
assert.equal(getSnapshotStatus().state, "saved");
"""
)
assert result.returncode == 0, result.stderr
def test_pre_load_autosave_uses_preset_subdir_when_not_drifted() -> None:
"""Sanity check the other branch: when drift is NOT set,
pre-load autosaves go to ``<preset>_autosave/`` per the regular
contract — the drift redirect is the only override."""
result = _run(
"""
const sharedStore = {
directories: {
"X": {
workflows: {
"Y": { graph: { nodes: [] }, archived: false, module: false, tags: [] },
},
directories: {},
},
},
};
SERVER.workflowsStore = sharedStore;
SERVER.presets.set("Clean.json", {
kind: "koolook-snapshot",
version: 1,
name: "Clean",
exportedAt: "2026-05-29T16:00:00.000Z",
picks: [],
workflows: sharedStore,
});
await loadWorkflowsStore();
setAllPicks([]);
setCurrentPresetName("Clean");
await detectBootDrift("Clean");
assert.equal(isBootDrifted(), false);
SERVER.writes.length = 0;
await writePreLoadAutosave("baseline");
const writtenDirs = SERVER.writes.map((w) => w.dir).filter(Boolean);
assert.ok(writtenDirs.length > 0);
for (const d of writtenDirs) {
assert.equal(d, "Clean_autosave",
`non-drifted autosave should route to Clean_autosave/, got "${d}"`);
}
"""
)
assert result.returncode == 0, result.stderr