REFERENCE / 03

Atomic writes & version history

A data-safety-critical tool should behave like one. Every save passes four checkpoints, and the file it replaces is kept as a full snapshot you can diff and restore.

Retention
50
Snapshot format
full JSON
Write retries
5
Conflict code
409

01 / PIPELINE

From draft to disk, in order.

  1. STEP 01

    Acquire the advisory lock

    proper-lockfile takes a lock beside the target path, with retries and a stale timeout. Concurrent saves to the same config are serialized.

  2. STEP 02

    Validate the whole document

    Ajv 2020-12 compiles the canonical schema once, then validates the complete config. If validation fails, a typed error is thrown before any write call — no partial file on invalid input.

  3. STEP 03

    Write atomically

    write-file-atomic writes to a temp file in the same directory, fsyncs it, then renames it over the target. The rename is atomic, so a crash leaves either the old file or the new one — never a half-written one.

  4. STEP 04

    Release, then snapshot

    The lock is released in a finally block. After a successful save, the previous on-disk content is recorded as a snapshot. A snapshot failure is reported as a warning and never invalidates the save.

WHY SAME-DIRECTORY TEMP FILES

A temp file on another filesystem would turn the final rename into a non-atomic copy-plus-delete (and risk EXDEV). The writer deliberately never overrides the library's same-directory default.

02 / FAILURE MODES

What happens when something goes wrong.

Each failure has a defined, non-destructive outcome. Nothing silently resets your history.

Failure modes and outcomes
SituationOutcome
The document fails schema validationThe save is blocked and the field errors are returned. The file on disk is untouched.
The process crashes during a writeThe target is never modified in place; the atomic rename means the previous complete file survives.
The file changed on disk while you were editingThe save carries the revision you started from. A mismatch returns HTTP 409 and asks you to review the current file rather than overwrite it.
Two helpers or two tabs save at onceAn advisory lock plus an in-process path queue and a cross-process transaction lock serialize the whole read → write → snapshot sequence, so history entries are not dropped.
Snapshot recording failsThe save still succeeds; the caller receives a warning built only from the error code and message. No config content is logged.
The config has keys the schema does not knowValidation does not reject unknown keys — the schema never sets additionalProperties: false. Unknown and future keys are preserved on save and shown in the editor.
Windows briefly locks the target during renameTransient EPERM, EBUSY and EACCES errors are retried up to five times with exponential backoff starting at 50 ms.

03 / SNAPSHOTS

Full copies, not a fragile diff chain.

What is stored

One complete JSON file per snapshot, plus a per-config index.json. Each index entry carries:

  • seq — monotonic sequence starting at 1
  • timestamp — ISO-8601 time the snapshot was recorded
  • contentHash — SHA-256 of the snapshotted content
  • file — the snapshot's filename, <seq>.json

Snapshots are written under the OS app-data directory, keyed by the SHA-256 of the config's absolute path — never inside your project or its .git.

Retention and de-duplication

The default policy keeps the last 50 snapshots per config and skips recording when the content hash matches the most recent snapshot. The index format carried sequence, timestamp and hash from the beginning, so retention changes need no migration.

A malformed index throws rather than silently resetting: history degrades to a warning, it is not erased.

04 / RESTORE

Restoring is another safe write.

Restore does not copy bytes behind the editor's back. The selected snapshot is loaded, validated against the active schema, and written through the same lock → validate → atomic-write pipeline as an ordinary save.

It also requires the current revision you were shown. If the file changed since, the restore is rejected with a conflict instead of overwriting a newer edit.

Before any of that, the UI computes a structural diff between the snapshot and the current document — key-by-key, not text-line-by-line — so reordering or formatting noise does not masquerade as a change.

Nothing is touched until you confirm the restore. The confirmation flow restores focus on cancel and supports Escape.

FIRST SAVE

A brand-new config has no prior content, so there is nothing to snapshot. A stub file is touched only to satisfy the advisory lock; the real write still runs through validation and atomic replacement.

05 / GUARANTEES

What this design does and does not promise.

It does

  • Block writes that fail schema validation.
  • Replace files atomically, never in place.
  • Refuse to overwrite a newer on-disk revision.
  • Keep the previous content of each successful save.
  • Store snapshots outside the tracked project.

It does not

  • Back up files you edit outside the tool.
  • Replace git history or version control.
  • Guarantee snapshot retention beyond the configured limit.
  • Upload, sync or share your configuration.