v0.1.8
[0.1.8] — 2026-05-22
Post-0.1.7 follow-through: residual cleanup-pass items + a CI
deprecation fix flagged by the 0.1.7 release run, plus a deep
correctness/durability audit pass over commit, structural, and
the CLI dispatch layer.
Changed
commit::apply_innerhidden behind#[cfg(test)]. Was
pub(crate)so the rollback test could inject a mid-commit
failure; that leaked the test-only seam into the production API.
Productionapply_changesandcommit_allcarry no hook
references;apply_inner+commit_all_withexist only under
cfg(test). The two paths share afinalize_applyhelper, and
commit_allnow delegates to a singlecommit_all_withimpl with
a no-op hook closure instead of duplicating the loop body.- Per-apply
NonceGenreplaces the staticAtomicU64counter
insidefn nonce(). The struct is constructed once per
apply_changescall and threaded by reference through stage and
commit phases; sibling-filename uniqueness across rayon workers
is unchanged, but the state is scoped to the invocation instead
of living in static mutable memory. NonceGen::newsamplesSystemTime::now()once and stores
it as a precomputed mix seed. The previous shape sampled the
clock inside everynext()call, so a 1k-file apply did 1k extra
syscalls just to disambiguate sibling filenames.emit_diff/emit_applynarrowed from&Clito
&OutputOptions, &Plan. Dropped the transitive dependency on
GuardOptions and StructuralCli substructs that those helpers
never touched.--threads Nhonored across the whole pipeline. Previously
only the regex planner ran inside the user-scoped rayon pool;
scripted-plan, structural-plan, and the commit/stage phase all
fell back to the global pool. The CLI now installs the pool once
and wraps every parallel phase, regardless of mode.- Primary capture fallback in
structural::applyis deterministic.
When a query lacks an explicit@root, the apply phase now picks
the outermost-by-byte-range capture (smallest start, then largest
end, then lowest capture index) instead of "the capture with the
largest index", which was declaration-order dependent.
Fixed
recover_sweepskips parentless sibling entries. Previously
fell back toPathBuf::new()as the HashMap target key when
path.parent()returned None, silently bucketing unrelated
parentless siblings together. Skip with atrace!line instead
so recovery never makes cross-target decisions.recover_sweepaggregates per-group errors instead of
bailing on the first failure. The user invokes--recover
precisely when the tree is in a partial state; aborting at the
first bad group left the rest unreconciled. All groups are
attempted; the first error is propagated after the sweep so the
exit code still reflects failure.structural::compile_friendly_querypasses child index, not
node id, tofield_name_for_named_child. Was calling
field_name_for_child(child.id() as u32), conflating the
pointer-derived opaque identifier with the positional child
index. Generated--astqueries could silently drop / mis-name
field annotations as a result.- UTF-8 corruption in three byte walkers.
structural::parse_template,structural::substitute_metavars,
andpattern::CompiledPattern::replacement_probeadvanced one
byte at a time and pushed each raw byte aschar, mojibaking
every multibyte codepoint. All three now advance by a full UTF-8
scalar via a sharedtemplate_scan::utf8_char_lenhelper. rollback_committedno longer unlinks the target before
restoring the backup. The explicitremove_filewas redundant
(renamereplaces atomically on Unix) and opened a window where
neither the old nor the new content existed on disk.finalize_applyfsyncs parent dirs before deleting backups.
The previous order unlinked the safety net before the rename
batch's directory entries were durable; a crash in the window
could leave the target absent with the backup already gone.structural::emit_nodeis iterative. The recursive
implementation grew the stack proportionally to the depth of the
user's--astpattern AST, giving pathological patterns a
stack-overflow vector. Replaced with an explicitOpen/Close
frame stack — identical output, bounded by heap.
CI
actions/checkout/actions/upload-artifact/
actions/download-artifactbumped from@v4to@v5across
audit, ci, docs, and release workflows. Closes the Node 20
deprecation annotation surfaced by the v0.1.7 release run before
the 2026-06-02 forced-Node-24 cutover.
Removed
- Windows as a supported target. No longer running
cargo testonwindows-latest; no longer cross-compiling
x86_64-pc-windows-msvcartifacts; no longer carrying the
#[cfg(windows)]/#[cfg(unix)]carve-outs in source. The
release matrix shrinks from seven targets to six (Linux gnu/musl
× x86_64/aarch64 plus macOS x86_64/aarch64). The parent-directory
fsync, the symlink walker tests, and the permission-preservation
test all assume unix unconditionally now.label_for_pathdrops
the backslash-to-forward-slash translation it carried for Windows
diff headers.