Code Assistant updates its edit history after a formatter changes the file
A code formatter can make an agent fail immediately after a successful edit. The agent writes one string, Prettier or rustfmt rewrites it, and the next exact-match operation searches for text that no longer exists. Nothing is wrong with the file. The stale part is the agent's account of what it just did.
Code Assistant, a small open-source Rust project, handles that mismatch inside the tool history. It formats the file on disk, then updates the edit tool's input when it can reconstruct the formatter's changes safely. The next model call sees the formatted replacement rather than the text the model originally proposed.
Coding agents act from transcripts, tool results, and working memory. When those records disagree with the filesystem, later calls can fail even though the earlier write worked.
How one successful edit goes stale
Suppose a JavaScript file contains const total = 1;. The agent submits an exact replacement with const total=42; as the new text. A configured formatter writes const total = 42; to disk.
If the tool reports only the requested string, the transcript still says const total=42;. A later replacement may copy that string into its search block. The tool cannot find it, so the agent rereads the file, repairs the search, and tries again. A formatter that improved the code has added a failure and another model round trip.
The Code Assistant repository describes format-on-save as a way to keep the model's picture synchronized without paying for a full reread after every edit. Running the formatter is ordinary. Feeding its deterministic transformation back into model-visible state is the useful design choice.
Reconstruct only what the anchors support
The project's implementation note describes the algorithm. Before applying a replacement, the updater records stable ranges around the changed text. After formatting, it locates those anchors in the new file and extracts the slice between them.
The replacement parameters change only when that slice is equivalent to the proposed replacement modulo whitespace. If anchors cannot be resolved confidently, or replacements touch or overlap, the formatted file remains but the history rewrite is skipped. The system does not infer a replacement from ambiguous evidence.
Configuration maps file globs to commands. A formatter that accepts a path uses the {path} placeholder, while a project-wide command can omit it:
{
"format_on_save": {
"**/*.rs": "cargo fmt",
"**/*.ts": "prettier --write {path}"
}
}
The current edit tool source mutates old_text, new_text, and replace_all when the formatter-aware updater returns a reconstructed replacement. The multi-block replace tool rebuilds its diff from the reconstructed blocks. For a whole-file write, the write tool rereads the formatted file and replaces input.content with the bytes now on disk. Since those inputs enter later model context, the agent reasons over the formatted result.
Tests reveal the intended limit
The repository's format-on-save tests include a JavaScript edit where const y=42; becomes const y = 42; in the updated tool input. Another test submits two unformatted TOML replacements and checks that the regenerated diff contains the formatted forms with spaces around =.
Other cases exercise refusal to reconstruct. When formatting changes content that was supposed to remain stable, the updater returns no replacement update. Files outside the configured glob patterns do not run a formatter. This is a narrow reconciliation step, not a semantic merge engine.
One test comment says the whole-file write tool does not implement format-on-save. Current source shows otherwise: it runs the command, rereads the file, and updates input.content. The stale comment is not evidence against the implementation, but it is a reason to read the executing path and assertions instead of relying on one note.
Races and executable configuration remain
The implementation note lists concurrent external edits and formatters as an open risk. If a developer or second agent changes the file between read and write, whitespace reconstruction cannot establish which actor owns the new text. The project's roadmap separately proposes blocking replacement in stale files. That proposal is distinct from formatter reconciliation and should not be described as shipped.
Formatter commands are user-configured and execute on the user's machine. Broader sandboxing and access control are outside this feature. Teams should review those commands with the same care as other executable repository configuration.
The state lesson resembles Summer Engine's durable receipts separate a completed edit from its lost acknowledgement. Both projects treat the record of an action as part of correctness. It also complements TypeLens puts TypeScript context after the read, not before the session, where useful context follows the file the agent actually opened.
Return the committed result
Another harness can borrow the principle without copying this stable-range algorithm. After deterministic post-processing, capture the final artifact, update working memory, and attach the transformation to the tool result. A compact reconstructed edit works when the evidence is unambiguous. A targeted reread is safer when it is not.
Formatters are only one source of post-edit change. Import sorters, code generators, migration tools, and lockfile updaters can all modify files after the model's nominal action. The receipt for that action should describe the final state that later reasoning will use.
For developers using ish.chat or building an agent loop through api.ish.chat, the same tool contract applies: return what the system committed, not merely what the model requested. Otherwise the agent spends another model call rediscovering its own last edit.



