Add atomic multi-file mode with write-ahead journal and recovery#19
Merged
Add atomic multi-file mode with write-ahead journal and recovery#19
Conversation
Implement --atomic flag for all-or-nothing multi-file operations using a two-phase commit protocol with a JSON write-ahead journal. When enabled, all file changes are first staged to temp files (phase 1), then atomically renamed over originals (phase 2). If the process is interrupted, a subsequent --recover run can roll back or --recover-forward to complete. Key features: - --atomic flag implies --backup via hard-links for safe rollback - --no-backup opt-out with explicit warning about rollback limitations - .toggle-atomic.journal WAL in CWD for crash recovery - .toggle-atomic.lock advisory lock prevents concurrent --atomic runs - Signal handling (SIGTERM/SIGINT) for graceful interrupt with journal preservation - Platform helpers: macOS F_FULLFSYNC, Windows rename retry with backoff - SHA-256 integrity verification for forward recovery - into_temp_path() fd release pattern for large batch support (>500 files) - CLI validation: --atomic rejects --dry-run, --no-backup requires --atomic - 13 new integration tests covering happy path, recovery, validation, and edge cases New files: src/journal.rs, src/platform.rs New deps: sha2, signal-hook, fd-lock https://claude.ai/code/session_01JTuqZjgo3pPPF5YUeGKarz
- Replace io::Error::new(io::ErrorKind::Other, ...) with io::Error::other() - Remove identical if/else branches in stage() encoding check - Prefix unused encoding parameter with underscore - Apply cargo fmt formatting https://claude.ai/code/session_01JTuqZjgo3pPPF5YUeGKarz
The platform.rs macOS cfg block uses libc::fcntl and libc::F_FULLFSYNC but libc was missing from Cargo.toml, causing macOS CI builds to fail. https://claude.ai/code/session_01JTuqZjgo3pPPF5YUeGKarz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements atomic multi-file operations for the toggle CLI, enabling all-or-nothing semantics across multiple file modifications. If the process is interrupted during the commit phase, a write-ahead journal allows recovery via rollback or forward completion.
Key Changes
New
journalmodule (src/journal.rs): Write-ahead journal system that records the state of atomic batch operationsJournalandJournalEntrytypes track staged writes, backups, and per-file completion statusStaged(temp files written) andCommitting(renames in progress)recover_staged()(cleanup),recover_rollback()(restore from backups),recover_forward()(complete interrupted commit)New
platformmodule (src/platform.rs): Platform-specific file operation helpersdurable_sync(): UsesF_FULLFSYNCon macOS,sync_data()elsewhere for guaranteed persistencesync_dir(): Fsyncs directory metadata on Unix (no-op on Windows)rename_with_retry(): Retry logic for Windows file locking issues (antivirus, indexer)resolve_symlinks(): Canonical path resolutionAtomic batch operations in
iomodule (src/io.rs):AtomicBatchstruct manages two-phase commit lifecyclestage()method writes content to temp files with fsync and permission preservationcommit()method creates hard-link backups, persists journal, then atomically renames all filesLOCK_FILENAME) prevents concurrent atomic operationsCLI enhancements (
src/cli.rs):--atomic: Enable atomic multi-file mode--no-backup: Disable backup creation (only valid with--atomic)--recover: Recover from interrupted atomic operation--recover-forward: Complete interrupted commit instead of rolling backMain entry point updates (
src/main.rs):run_atomic()function: Computes all changes first, stages them, then commits atomicallycompute_file_changes(): Dry-run version of file processing for pre-commit validation--atomicincompatible with--dry-run,--no-backuprequires--atomic, etc.Integration tests (
tests/integration.rs):--no-backupwarningNotable Implementation Details
interruptedflag; checked between renameshttps://claude.ai/code/session_01JTuqZjgo3pPPF5YUeGKarz