From 2df5e0ce26e514d017294c8307928c7ab193eae7 Mon Sep 17 00:00:00 2001 From: elhoim Date: Mon, 3 Aug 2026 05:42:36 +0000 Subject: [PATCH] fix(atlas): write the Pulse snapshot atomically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Atlas.ts wrote snapshot.json with writeFileSync at both call sites (sync and export), which truncates the target before writing. Pulse reads that same file concurrently — PULSE/modules/atlas.ts JSON.parses it on every GET /api/atlas — so a request landing mid-write saw a truncated file and threw. Store.ts and AtlasSystem.md both document Pulse as the reader of record ("one writer class, one reader artifact"). Switch both writes to atomicWriteText from PULSE/lib/atomic-write (temp file + rename), matching how LIFEOS/TOOLS reaches that helper. Byte output is unchanged: atomicWriteText writes the string as given, so the snapshot stays compact JSON with no trailing newline. --- LifeOS/install/LIFEOS/ATLAS/Atlas.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/LifeOS/install/LIFEOS/ATLAS/Atlas.ts b/LifeOS/install/LIFEOS/ATLAS/Atlas.ts index b1aea3d8e2..7fa1cb51a0 100644 --- a/LifeOS/install/LIFEOS/ATLAS/Atlas.ts +++ b/LifeOS/install/LIFEOS/ATLAS/Atlas.ts @@ -17,7 +17,8 @@ * the authority; `atlas owns` runs alongside it, never instead of it (Algorithm claim 16). */ -import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs"; +import { existsSync, readFileSync, renameSync } from "node:fs"; +import { atomicWriteText } from "../PULSE/lib/atomic-write"; import { EVENTS_PATH, SNAPSHOT_PATH, Store, type Collector } from "./Store"; import { cloudflare } from "./collectors/Cloudflare"; import { github } from "./collectors/Github"; @@ -57,7 +58,7 @@ async function runSync(names: string[], scope: string): Promise { console.error(`✗ ${name}: ${err instanceof Error ? err.message : String(err)}`); } } - writeFileSync(SNAPSHOT_PATH, JSON.stringify(store.exportSnapshot())); + atomicWriteText(SNAPSHOT_PATH, JSON.stringify(store.exportSnapshot())); } finally { store.close(); } @@ -174,7 +175,7 @@ switch (cmd) { } case "export": { const store = new Store(); - writeFileSync(SNAPSHOT_PATH, JSON.stringify(store.exportSnapshot())); + atomicWriteText(SNAPSHOT_PATH, JSON.stringify(store.exportSnapshot())); console.log(`snapshot → ${SNAPSHOT_PATH}`); store.close(); break;