-
-
Notifications
You must be signed in to change notification settings - Fork 0
File Utilities
Eshan Roy edited this page Jun 16, 2026
·
2 revisions
M31A uses atomic file operations throughout to prevent data corruption on crash or power loss.
Source: internal/fileutil/atomic.go
func AtomicWrite(path string, data []byte) error
func AtomicWriteWithPerm(path string, data []byte, perm os.FileMode) error1. Stat target file → preserve existing permissions (if exists)
2. Create temp file in same directory (.m31a_tmp_*)
3. chmod temp file to desired permissions
4. Write data to temp file
5. fsync() temp file
6. Close temp file
7. Rename temp → target (atomic on POSIX filesystems)
When overwriting an existing file, AtomicWriteWithPerm reads the current file's permissions and preserves them:
if info, err := os.Stat(path); err == nil {
perm = info.Mode().Perm() // H-19: preserve original permissions
}The rename operation is atomic on POSIX filesystems. If the process crashes:
- Before rename: original file is intact, temp file is orphaned (cleaned on next run)
- After rename: new file is in place, no partial state possible
Atomic writes are used for all persistent state:
| File | Written By |
|---|---|
session.json |
session.Manager.SaveSession() |
messages.json |
session.Manager.SaveMessages() |
checkpoint.json |
session.Manager.SaveCheckpoint() |
LEDGER.md |
ledger.Ledger.rewriteFile() |
recent_models.json |
session.Manager.SaveRecentModels() |
config.toml |
Settings editor save |
| Exported sessions | session.Manager.ExportSession*() |
The defer os.Remove(tmpPath) ensures the temp file is cleaned up if any step fails before the rename. After rename succeeds, the temp path no longer exists so the deferred remove is a no-op.