Releases: daniloaguiarbr/atomwrite
v0.1.14 — fix `write --line-ending auto` cross-platform divergence on new files
Summary
Resolves the write_creates_file_with_ndjson_output integration test failure on the windows-2025-vs2026 GitHub Actions runner. v0.1.13 produced different bytes_written values for the same stdin content on Linux/macOS (12 bytes) vs Windows (13 bytes) when the target file did not exist.
Root Cause
In src/commands/write.rs::normalize_line_endings, the Auto mode had a cfg!(windows) ? CrLf : Lf fallback for the new-file / unreadable-file branch. On Windows, the subsequent line_endings::normalize(..., CrLf) inserted a \r before every \n, inflating the byte count by 1. A secondary bug also stripped a byte from CRLF input on Linux/macOS via the same fallback path.
Fix
Both cfg!(windows) fallback branches have been removed. Auto on a new file is now a true no-op, matching the LineEnding::Auto docstring ("Preserve the dominant ending of the original file"). Behavior is deterministic across Linux, macOS, and Windows for the same stdin content. Detection on existing files and explicit --line-ending lf|cr-lf|cr modes are unchanged.
Regression Tests
Added in src/commands/write.rs::tests:
auto_on_new_file_preserves_lf_input- `auto_on_new_file_preserves_crlf_input"
Validation
cargo build --all-features(Linux): PASScargo fmt --check: PASScargo clippy --all-targets --all-features -- -D warnings: PASScargo test --all-features: 152 lib tests + integration suites + 3 doctests PASScargo check --target x86_64-pc-windows-gnu --libwithRUSTFLAGS=-Dwarnings: PASS, 0 errors, 0 warnings- 8/8
cli_writeintegration tests pass
Links
v0.1.13 — fix 4 RUSTFLAGS=-Dwarnings errors on windows-2025-vs2026
Summary
Resolves 4 compile errors on the windows-2025-vs2026 GitHub Actions runner under RUSTFLAGS=-Dwarnings. All four errors were unix-only symbols that become dead code when compiled for Windows; none were caught by the Linux CI matrix because Linux is cfg(unix).
Fixed
unused import: Durationinsrc/lock.rs:24—std::time::Durationis gated behind#[cfg(unix)]since it is only consumed by theflockpolling loop intry_acquire_loop.unused variable: strict_atomicinsrc/atomic.rs:381— annotated with#[cfg_attr(not(unix), allow(unused_variables))], mirroring the pattern already validated insrc/signal.rs:15-17.function copy_tempfile_to_target is never usedinsrc/atomic.rs:604— gated behind#[cfg(unix)]since the function is only invoked from the EXDEV fallback branch.clippy::unnecessary_literal_unwrapinsrc/atomic.rs:195—hardlink_nlink.unwrap_or(1) > 1rewritten ashardlink_nlink.is_some_and(|n| n > 1)to avoid triggering clippy 1.94+ on the WindowsNonepath.
Validation
cargo build --all-features(Linux): PASScargo clippy --all-targets --all-features -- -D warnings(Linux): PASScargo test --all-features: 150 lib tests + integration + doctests PASScargo check --target x86_64-pc-windows-gnu --libwithRUSTFLAGS=-Dwarnings: PASS, zero errors, zero warningscargo fmt --check: PASS
Links
atomwrite v0.1.12 — G72 Tree-sitter + G114 WAL + v14 Tier 3
Highlights
This release closes 20 technical gaps with 11,300+ lines of new code, tests, schemas, ADRs, and bilingual documentation.
Major Features
- G72 — REAL tree-sitter syntax check (
atomwrite write --syntax-check): validates content against 24 languages viatree_sitter_language_pack - G114 — WAL sidecar consultive: opt-in via
ATOMWRITE_WAL=1env or--strict-atomicflag, withrecover_orphan_journalsAPI - v14 Tier 3 — 6 new subcommands:
set,get,del,case,query,outline
6 New Subcommands (ADDITIVE)
| Subcommand | Description |
|---|---|
set |
Write a value to a TOML/JSON config file with dotted path |
get |
Read a value from a TOML/JSON config file with dotted path |
del |
Delete a key from a TOML/JSON config file |
case |
Convert identifier case (snake, camel, pascal, kebab, screaming-snake) |
query |
Tree-sitter S-expression query against a source file |
outline |
Extract high-level structure from a source file |
5 New Error Codes (ADDITIVE)
| Code | Variant | Class | Retryable |
|---|---|---|---|
| 83 | LockTimeout |
transient | Yes |
| 88 | SyntaxError |
permanent | No |
| 91 | ExdevFallbackDisabled |
precondition_failed | No |
| 92 | CopyBackBlake3Failed |
conflict | After re-read |
| 93 | OrphanJournal |
precondition_failed | No |
Quality Gates
- 445 tests in 44 test suites (was 320 in v0.1.10, +29 in v0.1.11, +96 in v0.1.12)
- 8 gates pass:
fmt,clippy,build,test,doc,deny,audit,msrv - 3 cross-compile targets:
x86_64-pc-windows-gnu,i686-pc-windows-gnu,x86_64-pc-windows-msvc - MSRV: Rust 1.88 stable
cargo deny: 4/4 OK (zero vulnerabilities)
New Modules
src/wal.rs— G114 WAL sidecar with consultive recoverysrc/syntax_check.rs— G72 tree-sitter syntax validationsrc/commands/{set,get,del,case,query,outline}.rs— v14 Tier 3 subcommandssrc/lock.rs— G54 file lock with timeoutsrc/xattr_restore.rs— G39 extended attribute preservation
Documentation
- 7 ADRs in
docs/decisions/(0019-0025) - 7 new JSON Schemas in
docs/schemas/ - 14 docs (7 EN + 7 PT-BR) updated with v0.1.12 sections
- Skill files (
skill/atomwrite-{en,pt}/SKILL.md) expanded from 35 to 46 sections
Dependencies
tree-sitter-language-pack = "1.8"(download + dynamic-loading, ~5-10MB footprint)
Breaking Changes
None. All 6 new subcommands and 5 new error variants are ADDITIVE. No existing subcommand was renamed or removed.
Installation
cargo install atomwrite --locked --version '^0.1.12'Verification
atomwrite --version
# atomwrite 0.1.12
atomwrite --help
# 28 user-facing subcommands
atomwrite read src/main.rs | jaq -r '.checksum'
# BLAKE3 checksumMigration from v0.1.11
No action required. The release is fully backward compatible. See CHANGELOG.md for the full list of changes.
Resources
- Crate: https://crates.io/crates/atomwrite
- Repository: https://github.com/daniloaguiarbr/atomwrite
- Documentation:
README.md,CHANGELOG.md,docs/ - ADRs:
docs/decisions/0019-0025.md - Schemas:
docs/schemas/
v0.1.11
atomwrite v0.1.11### Fixed- Windows E0433 on windows-2025-vs2026 — libc::write(STDERR_FILENO, ...) was referenced from src/main.rs in a function compiled on every platform, but libc is declared only under [target.cfg(unix).dependencies]. The shutdown-message writer was moved to src/signal.rs and gated with #[cfg(unix)].- signal_test::shutdown_message_on_stderr no longer flakes on ubuntu-latest — Two independent failure modes addressed: 1. cmd_search short-circuits to Ok(()) when shutdown.is_shutdown() is true, so the main thread takes the Ok(()) branch and emits the shutdown banner. 2. install_handlers_early and install_handlers now share a single Arc<ShutdownSignal> instance, eliminating the race where the second instance flag remained false under signal-hook chain-of-handlers ordering.- Test uses ATOMWRITE_READY_FILE for race-free readiness detection — atomwrite writes its PID to the file as soon as install_handlers_early returns; the test polls the file with a 10s deadline before sending SIGINT.### Validation- 302/302 tests pass across 33 test suites (5 successive full-suite runs, 0 failures)- cargo fmt, cargo clippy --all-features --all-targets -- -D warnings, cargo build --release, RUSTDOCFLAGS=-D warnings cargo doc --no-deps --all-features, cargo audit, cargo deny check all PASS
v0.1.10
Highlights
- CI GitHub Actions fully green — The persistent ubuntu-latest
signal_test::shutdown_message_on_stderrfailure has been fixed by installing signal handlers as the very first initialization step inmain(). The previous v0.1.8/v0.1.9 fixes (POSIX signal-safety, stderr flush) were correct in isolation but irrelevant: the child was being killed by SIGINT before any of that code could run. - GAP 20 resolved — Root cause: the SIGINT sent by
signal_testwas arriving BEFOREinstall_handlers()had a chance to register the signal_hook flag and low_level handlers. The default signal disposition (terminate via signal) was executing, killing the child with signal 2 and bypassing the entire graceful-shutdown path.
Fixed (GAP 20 - early signal handler installation)
-
signal_test::shutdown_message_on_stderrno longer fails on Linux CI — Addedsignal::install_handlers_early()which registers only the async-signal-safesignal_hook::flag::registerfor SIGINT and SIGTERM as the very first statement inmain(), before any other initialization. This guarantees that any SIGINT/SIGTERM arriving from the first nanosecond of process startup is caught and sets the shutdown flag, which the search/batch loops check. The fullinstall_handlers()(with counter tracking and OnceLock shared state) runs later and adds the double-Ctrl+C force-kill logic on top, without overwriting the early flag handlers. The previous v0.1.8/v0.1.9 fixes for POSIX signal-safety and stderr flush are still in place and necessary: the early install guarantees the flag is set, the main-thread writeln guarantees the message reaches the captured stderr pipe before exit. -
src/main.rsshutdown message useslibc::write(STDERR_FILENO, ...)directly — Replacedwriteln!(io::stderr().lock(), ...)with a helper function that callslibc::write(STDERR_FILENO, ...)to bypass any userspace buffering. The function is marked#[allow(unsafe_code)]becausemain.rshas#![deny(unsafe_code)]. The write is async-signal-safe per POSIX.1-2017 signal-safety(7) and goes straight to the kernel via fd 2, guaranteeing the bytes reach the captured stderr pipe before the process exits.
Validation
cargo build --all-features: PASScargo clippy --all-features --all-targets -- -D warnings: PASScargo test --all-features: 302/302 tests PASS (5/5signal_testcases all pass)cargo fmt -- --check: PASScargo audit: PASS (no vulnerabilities, no--ignoreflag)cargo deny check: PASS (advisories, bans, licenses, sources all OK)- 5/5 consecutive local runs of
signal_test::shutdown_message_on_stderrPASS
Notes
- v0.1.10 is a NON-BREAKING bug fix. No public API was modified.
- The signal handler installation is now a two-phase process: minimal flag handlers at process start, full counter handlers after init. The OnceLock ensures only one full install takes effect.
- See
CHANGELOG.mdfor full details.
v0.1.9
Highlights
- CI GitHub Actions fully green — Final flush fix for
signal_test::shutdown_message_on_stderrensures the shutdown message reaches the captured stderr pipe before the process exits on Linux/glibc. - POSIX signal-safety compliance — The shutdown message is emitted via
io::stderr().lock()in the main thread, with theStderrLockguard guaranteeing buffer flush on Drop.
Fixed (GAP 17 follow-up)
signal_test::shutdown_message_on_stderrflushes viaio::stderr().lock()— The first v0.1.8 fix movedeprintln!from the SIGINT/SIGTERM signal handlers to the main thread, but usedwriteln!(io::stderr(), ...)which is fully-buffered when stderr is redirected to a pipe (as incargo test'sStdio::piped()). The buffer was never flushed before the process exited with the signal exit code, so the parent test saw an empty stderr. The fix usesio::stderr().lock()to acquire theStderrLockguard, which flushes the buffer on Drop. CI ubuntu-latest will confirm on push.
Validation
cargo build --all-features: PASScargo clippy --all-features --all-targets -- -D warnings: PASScargo test --all-features: 302/302 tests PASS (5/5 runs ofsignal_test::shutdown_message_on_stderrPASS)cargo fmt -- --check: PASScargo audit: PASScargo deny check: PASS
Notes
- v0.1.9 is a NON-BREAKING patch over v0.1.8. The only code change is
writeln!(io::stderr(), ...)→writeln!(io::stderr().lock(), ...)insrc/main.rs. - See
CHANGELOG.mdfor full details.
v0.1.8
Highlights
- CI GitHub Actions fully green on all 3 OSes — Fixed two platform-specific race conditions that did not surface on macOS during local validation but blocked CI: ubuntu-latest (
signal_test::shutdown_message_on_stderrexit 101) and windows-latest (atomic::create_backup_and_retentionexit 1) - POSIX signal-safety compliance —
eprintln!removed from SIGINT/SIGTERM signal handlers per POSIX.1-2017signal-safety(7). Shutdown message is now emitted by the main thread. - Windows backup fsync is now best-effort — New
platform::fsync_file_best_effortlogs a warning and continues when antivirus holds a read handle on%TEMP%files. Primary write path still uses strictfsync_file. - CI matrix pinned to
windows-2025-vs2026— Silences the windows-latest redirect NOTICE and prevents future runner changes from breaking the build.
Fixed (GAP 17 - signal handler POSIX safety)
signal_test::shutdown_message_on_stderrno longer fails on Linux CI — Removedeprintln!from the SIGINT and SIGTERM signal handlers. Per POSIX.1-2017signal-safety(7), stdio functions are NOT async-signal-safe. Rust'sstd::io::stderr()uses a globalMutexthat can deadlock or lose buffered output when the signal arrives while another thread holds the lock. On glibc Linux, the race was deterministic: stderr was empty. The shutdown message is now emitted by the main thread insrc/main.rswhen it observesis_shutdown() == trueafteratomwrite::runreturns, which is the only async-signal-safe way to guarantee the message reaches the captured stderr pipe before the process exits. The Windowsctrlcpath still emits the message inline because ctrlc handlers run in a normal thread.
Fixed (GAP 18 - Windows backup fsync)
atomic::tests::create_backup_and_retentionno longer fails on Windows CI — Addedplatform::fsync_file_best_effortwhich logs a warning and continues. On Windows, antivirus products (Windows Defender, third-party AV) transiently hold a read handle on files in%TEMP%withFILE_SHARE_READbut withoutFILE_SHARE_WRITE, which causesFlushFileBuffersto returnERROR_ACCESS_DENIED(os error 5). Only the backup-durability fsync is best-effort; the primary write path still uses the strictfsync_filebecause the backup itself has already been created viafs::copyby the time fsync runs.
Fixed (CI matrix)
- Pinned to
windows-2025-vs2026— The matrix entry for Windows was changed fromwindows-latesttowindows-2025-vs2026(its successor before the June 15 2026 GitHub-hosted runner migration). This silences the "windows-latest requests are being redirected" NOTICE and prevents unexpected runner changes from breaking the build.
Validation
cargo build --all-features: PASScargo clippy --all-features --all-targets -- -D warnings: PASScargo test --all-features: 302/302 tests PASS (all 5signal_testcases pass;atomic::create_backup_and_retentionpasses)cargo fmt -- --check: PASScargo audit: PASS (no vulnerabilities, no--ignoreflag)cargo deny check: PASS (advisories, bans, licenses, sources all OK)cargo build --release: PASS- CI ubuntu-latest, macos-latest, windows-2025-vs2026: pending confirmation on push
Notes
- v0.1.8 is a NON-BREAKING change. No public API was modified.
- The signal-handler change is internal: external consumers that relied on the shutdown message appearing on stderr continue to see it; it is now emitted by the main thread instead of the signal handler.
- The Windows backup fsync change is internal: backup files are still created and atomic; the only difference is that the durability flush for backup metadata is best-effort. If a future user reports data loss on backup, we can re-tighten the fsync.
See CHANGELOG.md for full details.
v0.1.7
Highlights
- CI GitHub Actions fully green — All 6 jobs pass after fixing 4 distinct failures
- MSRV bumped to 1.88 — Unlocks
time0.3.47 which resolves RUSTSEC-2026-0009 - Node.js 24 ready — actions/checkout v6, actions/cache v5, FORCE_JAVASCRIPT_ACTIONS_TO_NODE24
- Cross-compile Windows validated on macOS — x86_64-pc-windows-gnu and i686-pc-windows-gnu
- signal_test::shutdown_message_on_stderr fixed on macOS
Fixed (CI Failures - GAP 15)
cargo auditno longer reports RUSTSEC-2026-0009 — Upgradedtimetransitive dependency from 0.3.45 to 0.3.47- macos-latest CI failure —
src/platform.rs:31no longer usesreturn Ok(()) - windows-latest CI failure —
EXIT_SIGINTandEXIT_SIGTERMconstants now have#[cfg_attr(not(unix), allow(dead_code))] actions/checkoutandactions/cacheNode 20 deprecation — Bumped to v6 and v5- MSRV bumped to 1.88
Fixed (GAP 16 - signal_test)
signal_test::shutdown_message_on_stderrno longer fails on macOS — Replacedlibc::write(2, ...)witheprintln!in signal handlerstests/signal_test.rstest reliability — Increased sleep from 50ms to 2000ms
Validation
cargo build --all-features: PASScargo clippy --all-features --all-targets -- -D warnings: PASScargo test --all-features: 303/303 tests PASScargo fmt -- --check: PASScargo audit: PASScargo deny check: PASS- Cross-compile Windows: PASS
See CHANGELOG.md for full details.
v0.1.6: Add docs.rs badge to README
atomwrite v0.1.6 — 2026-06-05
Highlights
- docs.rs badge added to README — Documentation status is now visible in the rendered README on crates.io and on the GitHub repository page
- Non-breaking — Visual change only, no code or public API surface changes
Added (README Badges)
- docs.rs badge in README.md and README.pt-BR.md — Added
[](https://docs.rs/atomwrite)between the Crates.io and License badges. The badge was previously missing from the published README, even though the documentation was being built successfully on docs.rs. The badge now appears in the rendered README on crates.io and on the GitHub repository page.
Tests
- 302 tests pass with 0 failures (unchanged from v0.1.4, v0.1.5, v0.1.6)
- 3 ignored tests (cross-compile Windows tests, unchanged)
Validation Gates Executed
cargo fmt --check: cleancargo test --all-features: 302 passed, 0 failed, 3 ignoredcargo doc --no-deps --all-featureswithRUSTDOCFLAGS="-D warnings": zero warningscargo build --release: successcargo package --list: 158 files
Migration Notes
- v0.1.5 → v0.1.6 is NON-BREAKING
- v0.1.6 does not change any public API surface or behavior
- No CHANGELOG migration guide required
- Existing v0.1.5 users can upgrade safely
Author
- Danilo Aguiar (daniloaguiarbr)
Links
- Crates.io: https://crates.io/crates/atomwrite/0.1.6
- Docs.rs: https://docs.rs/atomwrite/0.1.6
- Repository: https://github.com/daniloaguiarbr/atomwrite
v0.1.5: Documentation hygiene + docs.rs metadata cleanup
atomwrite v0.1.5 — 2026-06-05
Highlights
- Strict documentation lints —
#![deny(missing_docs)]and#![deny(rustdoc::broken_intra_doc_links)]now fail the build - Removed hardcoded
html_root_urlto v0.1.2 — intra-doc-links now resolve to the current version - Cleaned up
[package.metadata.docs.rs]— removed two no-op flags, added explicittargets - Non-breaking — the promoted deny lints are already satisfied by the existing documented public API
Changed (Documentation Hygiene)
#![warn(missing_docs)]promoted to#![deny(missing_docs)]— Missing public API documentation is now a hard build error instead of a warning. All public items were already documented in v0.1.4 (verified byRUSTDOCFLAGS="-D warnings" cargo doc --all-features).#![warn(rustdoc::broken_intra_doc_links)]promoted to#![deny(...)]— Broken intra-doc links now fail the build instead of being silent warnings.#![doc(html_root_url = "https://docs.rs/atomwrite/0.1.2")]removed — The attribute was hardcoded to the 0.1.2 version, causing intra-doc-links generated for newer versions (0.1.3, 0.1.4) to point to 0.1.2. The attribute is deprecated since rustc 1.48 in favor of therepositoryfield, which is already set inCargo.toml.
Changed (docs.rs Metadata)
[package.metadata.docs.rs]cleaned up:- Removed
all-features = true(no[features]table exists, so the flag was a no-op) - Removed
rustdoc-args = ["--cfg", "docsrs"](no#[cfg(docsrs)]markers exist in the source) - Added
targets = ["x86_64-unknown-linux-gnu"]to make the docs.rs build target explicit
- Removed
Tests
- 302 tests pass with 0 failures (unchanged from v0.1.4)
- 3 ignored tests (cross-compile Windows tests, unchanged)
cargo doc --no-deps --all-featureswithRUSTDOCFLAGS="-D warnings"passes cleanly
Validation Gates Executed
cargo fmt --check: cleancargo clippy --all-targets --all-features -- -D warnings: zero warningscargo test --all-features: 302 passed, 0 failed, 3 ignoredcargo doc --no-deps --all-featureswithRUSTDOCFLAGS="-D warnings": zero warningscargo build --release: successcargo package --list: 158 files
Migration Notes
- v0.1.4 → v0.1.5 is NON-BREAKING
- v0.1.5 does not change any public API surface or behavior
- No CHANGELOG migration guide required
- Existing v0.1.4 users can upgrade safely
Author
- Danilo Aguiar (daniloaguiarbr)
Links
- Crates.io: https://crates.io/crates/atomwrite/0.1.5
- Docs.rs: https://docs.rs/atomwrite/0.1.5
- Repository: https://github.com/daniloaguiarbr/atomwrite