Skip to main content
All posts

Engineering

How Discard puts a file back byte for byte

What Nebula Writer saves before the agent writes, how Discard restores the exact original bytes, and why it refuses to overwrite your newer edits.

  • Nebula Writer Team
  • October 14, 2026
  • 8 min read

When you press Discard edit, Nebula Writer writes the file's original bytes back to disk. It captures those bytes before the agent's first write, keeps them with a copy of the agent's result, and restores the original only if the file on disk still matches that result. If you changed the file after the agent did, Discard stops and your newer work stays. This post walks through how that works in the code, and how we test it.

1

The agent edits the file

Nebula Writer saves a restore point first, then writes each change into the file.

2

The review bar appears

It names the format and the number of changes. Inspect changes shows each one with Before and After.

3a

Keep edit

The change stays in the file.

3b

Discard edit

The original comes back from the restore point, byte for byte.

Changed your mind later? Use File changes, then Revert, or History, then Restore points.
What happens between the agent's edit and your decision.

Before the first write

When the agent edits or deletes a file, the tool starts a transaction. The first step reads the file's current bytes into the action's record. Only then does the tool write anything. If that capture fails, the edit does not start, and the code is careful about what it cleans up: "No mutation has started. Rollback here could overwrite newer user work", so it cancels only the action it began.

When the agent's edit is committed, Nebula Writer writes two copies of the file into version history:

CopyLabel in the codeWhat it holds
Before"Before agent edit"The bytes from before the agent's first write
After"Agent edit"The bytes the agent left on disk

The pair is linked to the agent action and to the number of files that action touched. That count matters later.

The agent writes into the file while it works, so another app that opens the file before you decide sees the agent's version. The before copy is what makes the decision reversible.

Where restore points live

Version history keeps a full copy of the file each time. The code calls it "the durable safety ledger": every supported format can fall back to "immutable full-file snapshots stored under the app data directory". That directory is Nebula Writer's own data folder on your computer, separate from your project. Your files stay in your folder, and the history of them sits beside the app.

A few rules govern that store:

  • Each copy is written to a temporary file, flushed to disk, and then renamed into place, so a copy is never written over in pieces.
  • Each copy carries a content hash. When a copy is loaded for a restore, the hash is checked again, and a copy that fails "its integrity check" is never restored.
  • Each file keeps up to 50 copies. Past that, the oldest copies are removed. Restore points you save yourself with Save a restore point are kept ahead of routine saves, and an agent's before and after copies are removed together as one unit.
  • If an agent action's copies have been removed, the app no longer offers to revert that action.

The history code knows these file types: Markdown, plain text, LaTeX (including .bib, .sty and .cls), Word, PowerPoint, Excel, CSV and JSON.

What Discard does

Discard edit sends the agent action's ID to one backend command. That command does all its checks before it touches any file.

First, it collects the before and after copies for every file in the action. If any pair is missing, or the number of files does not match the count recorded with the action, it stops with an error that ends "The edit was not rejected." It never acts on an incomplete set.

Second, it checks each file on disk against the agent's result. Two things must match. The file must still be the same file (the code compares its identity on disk, so a file that was replaced by another app fails). And its bytes must equal the after copy exactly. If either check fails, Discard refuses with this message:

This file changed after the reviewed agent edit. Its review remains pending so newer work is not overwritten.

The review stays open. Nothing is written. You can save your newer version, or use History to go back to an earlier state on purpose.

Third, if every file passes, it writes each file's before bytes back, using the same temporary-file-and-rename step as the store. For a request that changed several files, the checks run for every file before any file is changed. If a write fails halfway, the files already written are put back to the bytes they had a moment earlier.

When one file was edited by several agent actions in a row, they are undone newest first, and each step must match the result of the step after it. That is how a chain of edits unwinds to the first original.

Word, Excel and PowerPoint edits always resolve this way, as one decision. The review code marks every Office change to resolve through the file's saved copy. For PowerPoint it gives the reason: undoing a single change is "not yet guaranteed". Markdown, LaTeX and plain text can also accept or reject changes one at a time.

New files and deleted files

The same before and after pair covers files the agent creates or deletes. For a new file, the before copy records that the file did not exist.

  • Discard file on a new file removes it, after the same check that the file still matches what the agent wrote.
  • When the agent deletes a file, it goes to the system Trash. Permanent deletion is not available to the agent: "Files can only be moved to trash." Directory deletion is not available either.
  • Restore file on a deletion writes the saved bytes back to the original path.

Restoring from History

History, then Restore points, uses the same store with one extra rule. Before it writes the old version, it saves the current file as a "Before restore" copy, then saves the result as "Restored". If saving that second copy fails, the restore is rolled back. A restore that would change nothing is skipped.

Word and PowerPoint copies also get a structural check before they replace a file. The copy must open as a package, contain its main part (word/document.xml or ppt/presentation.xml), and every part in it must be readable. Otherwise the restore is cancelled and the current file is left alone.

How we test it

The rules above are covered by unit tests in src-tauri/src/version_history.rs. Each one sets up a file, records agent copies, and checks the bytes on disk afterwards:

TestWhat it checks
reverts_agent_action_when_current_file_matches_after_stateDiscard writes back the exact before bytes
refuses_agent_revert_after_a_human_editA human edit after the agent's blocks Discard and survives unchanged
multi_file_revert_checks_every_file_before_changing_any_fileOne changed file in a two-file action leaves both files as they were
reverts_a_chain_of_agent_actions_in_reverse_orderTwo edits in a row unwind to the first original
rejecting_a_created_file_removes_only_the_matching_agent_resultDiscarding a new file removes it
rejecting_a_deleted_file_restores_its_bytesRestoring a deleted file brings back its bytes
incomplete_agent_snapshot_pair_fails_closedA missing before copy stops Discard without touching the file
corrupted_snapshot_is_not_loaded_or_restoredA copy that fails its hash is never used
invalid_office_snapshot_cannot_replace_a_documentA broken Word or PowerPoint copy cannot replace a good file

There is also an end-to-end check that runs the real app headless. The edits go straight to the agent's edit tool with no AI model involved, so the check tests the file handling alone. It takes a Markdown file, a LaTeX file, and sample Word, PowerPoint and Excel files, and makes one small agent edit to each: a replaced line in the text files, an inserted paragraph in Word, a new text box in PowerPoint, a new cell value in Excel. Each edit runs twice, once kept and once discarded. Before the edit it records the file's SHA-256 hash. After Discard, the check passes only if the hash is identical, the edit's marker text is gone, and the file is listed as restored. After Keep, the hash must differ and the marker must be present. In the Markdown case, "The result is preliminary." became "The validated result is ready for review.", and after Discard the file hashed the same as before the edit.

This is the check behind the line we use on the site: Every change waits for your decision. Discard puts the file back exactly as it was.

What this does not cover

  • The copies live in the app's data folder on one computer. Keep your own backup or version control for anything you can't lose.
  • Older automatic copies are removed over time, and once an action's copies are gone it can't be reverted.
  • Discard is for the agent's edits. Changes you make in another app are protected from being overwritten by Discard, but Discard will not undo them.

For the user side of all this, see the review and history guide and Keep or discard: how to check every AI edit. For why the agent's Office edits are small enough to review in the first place, see why our agent edits Office files with native operations. To try it, download Nebula Writer.

Related