Skip to content

v1.5.2 — Atomic File Writes (Corruption Prevention)

Choose a tag to compare

@greenarmor greenarmor released this 20 Jun 06:28
· 21 commits to master since this release

Release v1.5.2 (Patch)

Patch release. All .ges/ state files are now written atomically using write-to-temp-then-rename, preventing data corruption if the process crashes mid-write (power loss, OOM kill, SIGKILL). No breaking changes. No configuration changes. Existing projects continue to work unchanged.

Previous release: v1.5.1
Release date: 2026-06-20


Highlights

Atomic File Writes — Corruption Prevention

GESF stores all compliance state (governance records, fix assignments, audit results, activity logs, control overrides) as JSON files in the project's .ges/ directory. Previously, every save used fs.writeFileSync() directly.

If the process crashed mid-write, the file would be left half-written and unreadable — silently losing all data on the next read. The next loadFixAssignments() or loadGovernanceRecords() call would return an empty array, as if no data ever existed.

This release eliminates that risk. Every write now goes through a two-step atomic pattern:

1. Write full content to filePath.tmp
2. rename(filePath.tmp → filePath)

The rename() syscall is atomic at the OS level — the file is either the old version or the new version, never partially written. Temp files are created in the same directory (cross-device renames are not atomic) and cleaned up on error.


What's New

New Utility Module

packages/core/src/utils/index.ts — three exported functions:

Function Purpose
safeWriteJson(filePath, data) Serializes JSON and writes atomically
safeWriteFile(filePath, content) Writes string content atomically
safeReadJson(filePath, fallback) Safe JSON read with fallback on error

All three automatically create parent directories if they don't exist.

Files Replaced (31 writes across 14 files)

Package Files Modified Write Paths Covered
core activity-log, fix-history, governance, fix-assignments, controls (3 writes), recommendations All .ges/ state persistence
mcp-server init_project, run_audit, auto_fix, apply_control_override, badge, policy install, install_hooks All MCP tool mutations
cli project.ts (wrapper), badge, fix, mcp-setup All CLI file output
git-hooks installHooks Pre-commit hook installation

E2E Verification

Every write path was tested end-to-end to confirm zero functionality degraded:

Path Tool Result
ges init CLI 25 files created, all valid JSON ✓
ges audit CLI last-audit.json + activity-log.json ✓
ges governance add/approve/evidence CLI governance-records.json updated ✓
ges assign + --resolve CLI fix-assignments.json created + updated ✓
ges policy install CLI controls.json + config.json ✓
ges control CLI control-overrides.json ✓
MCP init_project MCP All init files ✓
MCP run_audit MCP last-audit.json, score.json, metadata.json ✓
MCP create_governance_record MCP governance-records.json ✓
MCP assign_fix_to_governance MCP fix-assignments.json ✓
MCP resolve_fix_assignment MCP fix-assignments.json updated ✓
MCP auto_fix MCP fix-history.json + last-audit.json ✓
MCP apply_control_override MCP control-overrides.json ✓
Dashboard GET (all endpoints) HTTP 13/13 checks ✓
Dashboard POST mutations HTTP 12/12 checks (create, assign, resolve) ✓

Zero .tmp files left behind across all operations.
All JSON files valid after every write.


Test Results

Packages:    16 buildable, all clean
Tests:        483 passing (was 465, +18)

New tests (18):
  - safeWriteJson: round-trip, parent dir creation, atomic overwrite,
    no temp leftover, empty/null/undefined handling
  - safeWriteFile: string content, nested dirs, overwrite, empty string
  - safeReadJson: valid read, missing file fallback, malformed JSON fallback,
    type preservation
  - Atomic guarantee: temp cleanup on failure, data integrity

Files Changed

File Change
packages/core/src/utils/index.ts NewsafeWriteJson, safeWriteFile, safeReadJson
packages/core/src/index.ts Export utils module
packages/core/src/index.test.ts +18 tests for atomic write utilities
packages/core/src/activity-log/index.ts appendActivityLog uses safeWriteJson
packages/core/src/fix-history/index.ts appendFixHistory uses safeWriteJson
packages/core/src/governance/index.ts saveGovernanceRecords uses safeWriteJson
packages/core/src/fix-assignments/index.ts saveFixAssignments uses safeWriteJson
packages/core/src/controls/index.ts 3 writes → safeWriteJson (overrides, config add/remove)
packages/core/src/recommendations/index.ts Uses safeWriteFile
packages/mcp-server/src/server.ts 15 writes → safeWriteJson/safeWriteFile
packages/cli/src/utils/project.ts Wrapper delegates to safeWriteFile/safeWriteJson
packages/cli/src/commands/badge.ts Uses CLI wrapper (atomic)
packages/cli/src/commands/fix.ts last-audit.json via safeWriteJson
packages/cli/src/commands/mcp-setup.ts Config writes via safeWriteFile
packages/git-hooks/src/index.ts Hook installation via safeWriteFile

Upgrade Guide

This is a patch release. No migration required.

npm install -g @greenarmor/ges@latest

If you use the MCP server:

npm install -g @greenarmor/ges-mcp-server@latest

All existing .ges/ files continue to work — the atomic write layer is transparent and doesn't change the file format or location.