feat: Add atomic write pattern for state files (gt-wled7)

Prevents data loss from concurrent/interrupted state file writes by using
atomic write pattern (write to .tmp, then rename).

Changes:
- Add internal/util package with AtomicWriteJSON/AtomicWriteFile helpers
- Update witness/manager.go saveState to use atomic writes
- Update refinery/manager.go saveState to use atomic writes
- Update crew/manager.go saveState to use atomic writes
- Update daemon/types.go SaveState to use atomic writes
- Update polecat/namepool.go Save to use atomic writes
- Add comprehensive tests for atomic write utilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-28 15:57:53 -08:00
parent 36dbea9618
commit 213b3bab20
7 changed files with 146 additions and 35 deletions

View File

@@ -13,6 +13,8 @@ import (
"os"
"path/filepath"
"time"
"github.com/steveyegge/gastown/internal/util"
)
// Config holds daemon configuration.
@@ -82,7 +84,7 @@ func LoadState(townRoot string) (*State, error) {
return &state, nil
}
// SaveState saves daemon state to disk.
// SaveState saves daemon state to disk using atomic write.
func SaveState(townRoot string, state *State) error {
stateFile := StateFile(townRoot)
@@ -91,12 +93,7 @@ func SaveState(townRoot string, state *State) error {
return err
}
data, err := json.MarshalIndent(state, "", " ")
if err != nil {
return err
}
return os.WriteFile(stateFile, data, 0644)
return util.AtomicWriteJSON(stateFile, state)
}
// LifecycleAction represents a lifecycle request action.