Skip to content

refactor: consolidate atomic-file-write helpers#430

Merged
tomasz-tomczyk merged 1 commit intomainfrom
refactor/consolidate-atomic-write
May 2, 2026
Merged

refactor: consolidate atomic-file-write helpers#430
tomasz-tomczyk merged 1 commit intomainfrom
refactor/consolidate-atomic-write

Conversation

@tomasz-tomczyk
Copy link
Copy Markdown
Owner

Summary

Three independent atomic-file-write helpers had drifted across the codebase. This PR consolidates them into one canonical helper and fixes a real durability bug along the way.

  • Move atomicWriteFile from daemon.go to a dedicated atomic_write.go. No behavior change — better location, given it's used by review files, config, sessions, plans, and aider integration.
  • Delete writeFileMkdirAtomic in aider_install.go. Replace its three call sites with atomicWriteFile(path, data, 0o644). The old helper hardcoded perm via O_CREATE flags and skipped Chmod; the canonical helper now guarantees 0o644 regardless of umask.
  • Durability fix in plans.savePlanSessions: was using inline os.WriteFile + os.Rename with no fsync. On power loss between the rename and the kernel flushing the data block, the inode rename committed but the data did not — leaving a zero-byte plan-sessions file at the target. Now routes through atomicWriteFile (temp + write + fsync + rename). The advisory flock around the function is preserved.
  • Drop redundant os.MkdirAll in saveCritJSONatomicWriteFile already creates the parent directory.

Hidden assumptions verified

  • writeFileMkdirAtomic's call sites (CONVENTIONS.md, .aider.conf.yml) only ever wanted 0o644. No executables, so no perm regression.
  • Old helper used MkdirAll(0o755); canonical uses 0o700. Parent dirs (./.crit/, ~) already exist in practice — no observable change.
  • Tempfile naming changed from .tmp.<pid> to os.CreateTemp random suffix. Test TestAiderInstallAtomicWrite_CleansUpTempfile updated to assert no .tmp siblings remain.

Review

  • gofmt -l . clean
  • golangci-lint run ./... — 0 issues
  • go vet ./... clean
  • go test -race -count=1 ./... passes (54.8s)
  • Pre-commit hook passes (gofmt, lint, tests, ESLint, Stylelint, CSS vars)

Test plan

  • Existing daemon_test.go atomicWriteFile tests still pass (unchanged surface)
  • Aider install atomic write test renamed + retargeted to canonical helper
  • Plan-session save path exercised by existing plans_test.go

🤖 Generated with Claude Code

Three independent atomic-write helpers had drifted across the codebase.
Consolidate them into a single canonical helper in atomic_write.go.

- Move atomicWriteFile from daemon.go to a dedicated atomic_write.go
  (no behavior change, better location — it's used by review files,
  config, sessions, plans, and aider integration).
- Delete writeFileMkdirAtomic in aider_install.go. Replace its three
  call sites with atomicWriteFile(path, data, 0o644). The old helper
  hardcoded perm via O_CREATE flags and skipped Chmod; canonical helper
  now guarantees 0o644 regardless of umask.
- Rewrite plans.savePlanSessions to use atomicWriteFile instead of
  inline os.WriteFile + os.Rename. This adds the missing fsync — a real
  durability bug. Without fsync between rename and the kernel flushing
  the data block, a power loss left a zero-byte file at the target
  (the inode rename committed but the data did not). The advisory
  flock around the function is preserved.
- Drop redundant os.MkdirAll in saveCritJSON; atomicWriteFile already
  creates the parent directory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 2, 2026

Codecov Report

❌ Patch coverage is 19.35484% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.35%. Comparing base (38ee4e0) to head (b247aae).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
atomic_write.go 19.23% 14 Missing and 7 partials ⚠️
aider_install.go 33.33% 1 Missing and 1 partial ⚠️
plans.go 0.00% 1 Missing and 1 partial ⚠️

❌ Your patch status has failed because the patch coverage (19.35%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #430      +/-   ##
==========================================
+ Coverage   67.18%   67.35%   +0.16%     
==========================================
  Files          29       30       +1     
  Lines        9814     9785      -29     
==========================================
- Hits         6594     6591       -3     
+ Misses       2698     2681      -17     
+ Partials      522      513       -9     
Flag Coverage Δ
e2e 33.99% <16.12%> (+0.10%) ⬆️
unit 63.61% <19.35%> (+0.15%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@tomasz-tomczyk tomasz-tomczyk merged commit 22ce3c9 into main May 2, 2026
7 of 8 checks passed
@tomasz-tomczyk tomasz-tomczyk deleted the refactor/consolidate-atomic-write branch May 2, 2026 12:08
tomasz-tomczyk added a commit that referenced this pull request May 5, 2026
…ode (#464)

- installOneFile: use atomicWriteFile (consistent with all other writes post-#430)
- aider_install: collapse byte-identical force/!force write branches
- cli_install: fix order-sensitive flag parsing (--force before agent name now works)
- main: remove dead _ = keys[i] (pre-existing leftover)
- share: delete test-only buildLocalFingerprints shim; tests now call buildLocalFingerprintIndex directly
- cli_serve: add comment clarifying bindListener retry semantics (explicit port retries, ephemeral fails fast)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant