Skip to content

Architecture and guarantees

Layers

axm-edit separates typed file operations, filesystem execution and tool presentation:

Layer Responsibility
Root exports / models.operations Typed operations and result models
core.precheck, precheck_fs, preflight Tool payload diagnostics and severity partition
core.engine Resolve anchors, validate operations, snapshot and apply
core.checkpoint Capture original bytes and restore recorded paths
core.atomic_write Per-file whole-file rewrite replacement
tools.batch_edit Preflight, engine orchestration, post-edit Ruff and ToolResult
Other tools modules Read/write/search/list/process interfaces

The root batch_apply function is a lower-level typed API. It does not run the tool's preflight contract checks or post-edit Ruff. Tool classes are registered through axm.tools, so MCP and the generic CLI share execution.

Validate, then apply

The tool runs the shared preflight rules over authored dictionaries. Errors block before parsing and snapshotting; warnings are returned but do not block that phase. The engine then resolves and validates the operation set. Preflight is not a complete simulation: engine validation can reject a nonblocking check result.

For each replace, line hints and content anchors locate whole lines. Resolution tries exact content and normalization fallbacks; indentation-normalized matches are reindented when written. Resolved edits are spliced bottom to top so line shifts do not invalidate earlier line numbers. Overlap is rejected. Near-miss reports render tabs, spaces, nonbreaking spaces and newlines explicitly.

After validation, the engine snapshots resolved target paths, applies replaces, then creates/deletes, then rewrites. Operations are grouped by kind rather than executed as a sequential script in input order.

Atomicity has two different scopes

Batch validation rejects before target writes. Batch apply performs ordinary filesystem calls and attempts rollback on any apply exception. There is no multi-file filesystem transaction. Another process can observe a partially applied batch, and a crash can interrupt apply before Python recovers.

Replace/create use direct writes, delete uses unlink. Rewrite alone uses a temporary sibling and os.replace, preserving permission bits and attempting syncing. A concurrent reader of that file sees a replacement rather than a partially written rewrite, but this does not make the surrounding batch atomic.

Rollback restores recorded bytes, not all metadata, and can fail. Root BatchResult.rollback_failed reports incomplete automatic recovery; the tool currently omits that field from structured data. See rollback for consequences.

Concurrent writers

The engine rechecks replace anchors against the content read for apply. This detects some drift, not every race: another write can still occur after that read. Rewrite checksums are checked during validation, not at the final os.replace. Create/delete and path resolution are also not protected by locks.

Checkpoint recovery has no output checksum and can overwrite subsequent changes to a snapshotted path. Use one writer per project or external coordination. Neither the word “atomic” nor a successful preflight substitutes for that coordination.

Encoding and exactness

Operations accept text. Replace matches UTF-8 with universal-newline reads, then preserves the detected LF/CRLF style on write. Create writes the supplied text; rewrite encodes it as UTF-8 without anchor processing. Binary replace targets and undecodable replace content are rejected during validation; rewrite validation rejects detected binary targets.

Post-edit Ruff may change Python content after successful apply. Its errors and warnings are informational fields rather than an engine rollback trigger. The diff display is advisory and can miss formatting-only changes. Use lint=False and inspect bytes when exact output is required.

Path boundaries and resources

Root-relative tools resolve paths and refuse escapes. This does not lock symlinks against concurrent changes. file_bytes is an unconfined file reader; run_command only restricts its initial working directory and runs an executable with inherited permissions.

Checkpoints contain complete base64 file contents. File reads and subprocess capture also buffer data before output truncation. The tools' display caps control returned content, not total memory use or a security boundary.