Skip to content

refactor: consolidate atomic-write primitives onto fsutil (iss-32) - #44

Merged
REPPL merged 1 commit into
mainfrom
fix/iss-32-atomic-write-consolidation
Jul 12, 2026
Merged

refactor: consolidate atomic-write primitives onto fsutil (iss-32)#44
REPPL merged 1 commit into
mainfrom
fix/iss-32-atomic-write-consolidation

Conversation

@REPPL

@REPPL REPPL commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Resolves ledger issue iss-32 atomic-write-consolidation — one-canonical-primitive.

Before

Four independent temp-file+rename atomic-write implementations with divergent durability, plus a duplicated isRealDir:

Copy Divergence
internal/fsutil.WriteFileAtomic canonical (file fsync + parent-dir fsync)
ahoy/marker.go writeFileAtomic no parent fsync, mode-preserving
capture/roots.go writeFileAtomic no parent fsync (despite a doc claiming durability)
memory/writer.go durableWrite parent fsync, string-oriented, fixed 0644
ahoy/store.go isRealDir byte-identical duplicate of fsutil.IsRealDir

internal/fsutil itself had zero tests.

After — detector-first

  • Detector: TestNoNonCanonicalAtomicWritePrimitives (fsutil) walks internal/ and fails on any named writeFileAtomic/durableWrite/isRealDir outside fsutil. Watched fail (flagged all four) → green after consolidation.
  • fsutil crash-safety suite added: perm, overwrite, symlink-replacement (target not written through), parent-dir creation, mode preservation, IsRealDir symlink refusal.
  • Consolidation: fsutil gains WriteFileAtomicPreserveMode; ahoy/capture callers route to it (same (path, []byte) signature); memory's durableWrite becomes a one-line writeStringAtomic adapter over fsutil.WriteFileAtomic. Deleted the four copies; removed the orphaned tmpCounter + sync/atomic.

Behaviour deltas (documented, not smuggled)

  • ahoy + capture writers gain a parent-directory fsync (a crash-durability strengthening their copies lacked — capture/roots.go's doc had already claimed it).
  • Memory pages are written at an explicit 0644 chmod where the old writer left the mode subject to the process umask (identical under the default 022; formerly 0600 under a 077 umask). Deterministic and consistent with every other abcd store file.
  • Symlink-refusal and same-directory atomic rename unchanged; os.CreateTemp's random name is stronger than the old fixed temp path.

Review

  • Correctness (ruthless-reviewer): PROMOTE (after fixing a stale package-doc, a detector-comment scope note, and a stray import blank line).
  • Security (security-reviewer): PASS — symlink-refusal preserved, O_EXCLCreateTemp at least as strong, no permission downgrade, temp stays in the target dir (atomic rename).

make preflight clean (build, gofmt, vet, test, -race).

Scope note

A fifth inline durable-write (memory/ingest.go storeOriginal) is outside this issue's named 4-copy corpus and is inline (the named-function detector can't catch it); it is filed as iss-79 rather than claimed consolidated.

Branches from main (independent of the open #43). Do not merge / no auto-merge — maintainer merges.

Assisted-by: Claude:claude-opus-4-8

Four divergent temp-file+rename copies and a duplicate isRealDir are
routed through the canonical internal/fsutil, behind a detector:

- fsutil gains WriteFileAtomicPreserveMode (stat existing mode, default
  0644, delegate to WriteFileAtomic).
- ahoy marker.go writeFileAtomic + store.go isRealDir deleted; callers
  use fsutil.WriteFileAtomicPreserveMode / fsutil.IsRealDir.
- capture roots.go writeFileAtomic deleted; callers use fsutil.
- memory durableWrite replaced by a one-line writeStringAtomic adapter
  over fsutil.WriteFileAtomic; orphaned tmpCounter var + sync/atomic
  import removed.
- TestNoNonCanonicalAtomicWritePrimitives flags any named redefinition
  outside fsutil; a new fsutil crash-safety suite covers the primitive
  (fsutil previously had zero tests).

Behaviour: the ahoy and capture writers GAIN a parent-directory fsync
(a durability strengthening; their copies never did it). Memory pages
are now written at an explicit 0644 chmod where the old writer left the
mode subject to the process umask (0644 under the default 022; formerly
0600 under a 077 umask) — deterministic and consistent with every other
abcd store file. Symlink-refusal and same-directory atomic rename are
unchanged; os.CreateTemp's random name is stronger than the old fixed
temp path. security-reviewed PASS, ruthless-reviewed PROMOTE.

A fifth inline copy (memory/ingest.go storeOriginal), outside this
issue's named corpus, is tracked as iss-79.

Assisted-by: Claude:claude-opus-4-8
@REPPL
REPPL merged commit 54fef5f into main Jul 12, 2026
12 checks passed
@REPPL
REPPL deleted the fix/iss-32-atomic-write-consolidation branch July 12, 2026 20:11
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