// Hermetic NG-7 tests: goal-state incarnation fencing (Mercer F1 class). // Run: node --test test/fencing.test.ts // // SAFETY (NG7-SAFETY-V1W2): every arm uses an ISOLATED temp directory — // no live seat state, no Mercer/Marcie/marcie-2 files, no running sessions. // The extension source edit is confined to store resolution (store.ts) and // the one-line index.ts wiring. import assert from "node:assert/strict"; import { test } from "node:test"; import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync, readdirSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { resolveStatePath, fencedStateFilePath, loadState, saveState, stateFilePath, } from "../lib/store.ts"; import { setGoal, pauseGoal, initialState } from "../lib/state.ts"; function fixtureDir(): string { return mkdtempSync(join(tmpdir(), "goal-fence-")); } function writeLegacy(dir: string, text: string): void { writeFileSync(stateFilePath(dir), JSON.stringify(setGoal(initialState(), text), null, 2) + "\n"); } // ---- (a) hostile two-incarnation collision: the measured class --------------- test("(a) two incarnations of one seat never share or clobber state", () => { const dir = fixtureDir(); try { const a = resolveStatePath(dir, { incarnationId: "inc-marcie" }); const b = resolveStatePath(dir, { incarnationId: "inc-marcie-2" }); assert.notEqual(a, b, "distinct incarnations must resolve distinct files"); // incarnation A sets an active goal const stateA = setGoal(initialState(), "ship the canary"); saveState(stateA, a); // incarnation B (a second process of the SAME seat) loads ITS state const stateB = loadState(b); assert.equal(stateB.status, "none", "B must not inherit A's active focus"); assert.equal(stateB.text, "", "B sees no goal text of A's"); // B sets its own goal; A's file is untouched saveState(setGoal(initialState(), "B's own goal"), b); assert.equal(loadState(a).text, "ship the canary", "A's focus survives B's activity"); assert.equal(loadState(b).text, "B's own goal"); } finally { rmSync(dir, { recursive: true, force: true }); } }); test("(a) B cannot pause or settle A's focus through any store operation", () => { const dir = fixtureDir(); try { const a = resolveStatePath(dir, { incarnationId: "inc-a" }); const b = resolveStatePath(dir, { incarnationId: "inc-b" }); saveState(setGoal(initialState(), "A active"), a); // Every mutation B performs addresses b's path only; A's bytes cannot // change through B's store API — proven by writing from B and re-reading A. saveState(pauseGoal(loadState(b), "blocked:sibling", "cap"), b); const aNow = loadState(a); assert.equal(aNow.status, "active", "A's focus is still active after B's pause"); assert.equal(aNow.pausedReason, undefined); // And the directory contains exactly the two fenced files. const names = readdirSync(dir).filter((n) => n.startsWith("goal-state")); assert.equal(names.length, 2); } finally { rmSync(dir, { recursive: true, force: true }); } }); test("(a) session_start-style reload stays incarnation-scoped", () => { const dir = fixtureDir(); try { const a = resolveStatePath(dir, { incarnationId: "inc-a" }); saveState(setGoal(initialState(), "A goal v1"), a); // simulate: A's session reloads (new/resume/reload all re-load from ITS path) const reloaded = loadState(resolveStatePath(dir, { incarnationId: "inc-a" })); assert.equal(reloaded.text, "A goal v1"); // a sibling's state never leaks into A's reload saveState(setGoal(initialState(), "sibling"), resolveStatePath(dir, { incarnationId: "inc-sib" })); assert.equal(loadState(resolveStatePath(dir, { incarnationId: "inc-a" })).text, "A goal v1"); } finally { rmSync(dir, { recursive: true, force: true }); } }); // ---- (b) legacy migration fails safe ------------------------------------------- test("(b) legacy unfenced state is QUARANTINED, never claimed (NG-7 F4)", () => { const dir = fixtureDir(); try { writeLegacy(dir, "legacy active goal"); const a = resolveStatePath(dir, { incarnationId: "inc-first" }); // FAIL SAFE: the first incarnation must NOT inherit the unknown owner's // ACTIVE focus — it starts fresh. const state = loadState(a); assert.equal(state.status, "none", "no active focus inherited from legacy"); assert.equal(state.text, ""); // EVIDENCE PRESERVED: the legacy bytes live on in the quarantine file. const quarantined = JSON.parse(readFileSync(join(dir, "goal-state.legacy.json"), "utf8")) as { text: string }; assert.equal(quarantined.text, "legacy active goal"); const names = readdirSync(dir); assert.equal(names.includes("goal-state.json"), false, "legacy name vacated"); } finally { rmSync(dir, { recursive: true, force: true }); } }); test("(b/F4) ACTIVE legacy + fresh incarnation: no active focus inherited, evidence preserved", () => { const dir = fixtureDir(); try { // the live canary shape: legacy state carrying an ACTIVE focus of a // previous (unknown-owner) incarnation writeLegacy(dir, "mercer's previous focus"); for (const inc of ["inc-canary-1", "inc-canary-2"]) { const p = resolveStatePath(dir, { incarnationId: inc }); assert.equal(loadState(p).status, "none", `${inc} inherits no active focus`); } // second quarantine slot when another legacy reappears (counter path) writeLegacy(dir, "second legacy"); resolveStatePath(dir, { incarnationId: "inc-third" }); assert.equal( readFileSync(join(dir, "goal-state.legacy.1.json"), "utf8").includes("second legacy"), true, "counter slot preserves the second legacy", ); } finally { rmSync(dir, { recursive: true, force: true }); } }); test("(b/F3) migration race: rename attempted with legacy present, failure -> fresh", () => { const dir = fixtureDir(); try { writeLegacy(dir, "legacy goal"); let renameCalls = 0; const path = resolveStatePath(dir, { incarnationId: "inc-raced", io: { // sequenced: fenced absent (first probe), legacy present (second) existsSync: (p: string) => p === stateFilePath(dir), renameSync: () => { renameCalls += 1; throw new Error("EEXIST: sibling quarantined it first"); }, }, }); assert.equal(path, fencedStateFilePath(dir, "inc-raced")); assert.equal(renameCalls, 1, "the rename branch EXECUTED (not the fenced early-return)"); assert.equal(loadState(path).status, "none", "race loses to fresh"); } finally { rmSync(dir, { recursive: true, force: true }); } }); test("(b/F2) traversal-shaped launcher claim is rejected at the identity source", async () => { const { incarnationIdentity, resetIncarnationGlobal } = await import("../../mosaic-core/lib/incarnation.ts"); const prev = process.env.MOSAIC_LAUNCH_INCARNATION; resetIncarnationGlobal(); try { for (const hostile of ["../../etc/pwn", "..", "/abs", "a/b", ".hidden", "x".repeat(200)]) { process.env.MOSAIC_LAUNCH_INCARNATION = hostile; const id = incarnationIdentity(() => `minted-for-${hostile.slice(0, 3)}`); assert.notEqual(id, hostile, `claim ${JSON.stringify(hostile)} must not become the identity`); assert.equal(id.startsWith("minted-for-"), true, `falls through to mint for ${JSON.stringify(hostile)}`); resetIncarnationGlobal(); } } finally { if (prev === undefined) delete process.env.MOSAIC_LAUNCH_INCARNATION; else process.env.MOSAIC_LAUNCH_INCARNATION = prev; resetIncarnationGlobal(); } }); test("(b/F2) hostile claim keeps BOTH consumer paths inside the state root", async () => { const { createJournal } = await import("../../mosaic-core/lib/journal.ts"); const { incarnationIdentity, resetIncarnationGlobal } = await import("../../mosaic-core/lib/incarnation.ts"); const prev = process.env.MOSAIC_LAUNCH_INCARNATION; resetIncarnationGlobal(); try { process.env.MOSAIC_LAUNCH_INCARNATION = "../../outside"; const id = incarnationIdentity(); // no-arg production form (F1) assert.notEqual(id, "../../outside"); const j = createJournal({ incarnationId: id, stateHome: "/xdg/state", io: { mkdirSync() {}, appendFileSync() {} } }); assert.equal(j.path.startsWith("/xdg/state/mosaic-core/"), true, "journal contained"); assert.equal(j.path.includes(".."), false); const dir = fixtureDir(); const gp = resolveStatePath(dir, { incarnationId: id }); assert.equal(gp.startsWith(dir), true, "goal path contained"); assert.equal(gp.includes(".."), false); rmSync(dir, { recursive: true, force: true }); } finally { if (prev === undefined) delete process.env.MOSAIC_LAUNCH_INCARNATION; else process.env.MOSAIC_LAUNCH_INCARNATION = prev; resetIncarnationGlobal(); } }); test("(b) corrupt legacy never blocks fencing", () => { const dir = fixtureDir(); try { writeFileSync(stateFilePath(dir), "{corrupt"); const a = resolveStatePath(dir, { incarnationId: "inc-x" }); assert.equal(loadState(a).status, "none", "corrupt legacy -> fresh initial state"); } finally { rmSync(dir, { recursive: true, force: true }); } }); // ---- (c) PI_SESSION_ID is never the incarnation -------------------------------- test("(c) fenced paths key on the process-launch identity, not PI_SESSION_ID", async () => { const dir = fixtureDir(); try { const prev = process.env.PI_SESSION_ID; process.env.PI_SESSION_ID = "session-should-not-appear"; const { incarnationIdentity } = await import("../../mosaic-core/lib/incarnation.ts"); const id = incarnationIdentity(() => "minted-check"); process.env.PI_SESSION_ID = prev === undefined ? "" : prev; if (prev === undefined) delete process.env.PI_SESSION_ID; assert.notEqual(id, "session-should-not-appear"); const p = resolveStatePath(dir, { incarnationId: id }); assert.doesNotMatch(p, /session-should-not-appear/); } finally { rmSync(dir, { recursive: true, force: true }); } }); // ---- compatibility --------------------------------------------------------------- test("goal_report compatibility: the state machine surface is unchanged", async () => { // The pure state machine (setGoal/pauseGoal/resume/recordReport/validateReport) // is untouched by fencing; the existing goal suite covers it. Here we pin // the compatibility contract fencing must not break: a state saved and // loaded through the fenced path round-trips exactly as the legacy path did. const dir = fixtureDir(); try { const p = resolveStatePath(dir, { incarnationId: "inc-compat" }); const s = setGoal(initialState(), "round trip"); saveState(s, p); assert.deepEqual(loadState(p), s); } finally { rmSync(dir, { recursive: true, force: true }); } });