v1.5.2 — Atomic File Writes (Corruption Prevention)
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 |
New — safeWriteJson, 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@latestIf you use the MCP server:
npm install -g @greenarmor/ges-mcp-server@latestAll existing .ges/ files continue to work — the atomic write layer is transparent and doesn't change the file format or location.