atriumatrium

Persistence & recovery

How atrium journals state and restores after quits, crashes, and reboots.

atrium's durability guarantee is simple: if you can quit and reopen the app and see your workspace unchanged, a crash or a reboot should produce the same result.

What gets persisted

Every interesting piece of state lands on disk under ~/.atrium/:

  • App-level state (state.json) — which workspaces are open, which workspace is focused, window geometry.
  • Per-workspace snapshots (workspaces/{id}/workspace.{timestamp}.json) — workspace metadata, the list of rooms, each room's mosaic tree, every pane's snapshot (type, cwd, scrollback, session IDs, editor buffer state, browser URL and history, per-note viewport state for notepad panes).
  • Configuration (config.json) — theme, keybinding overrides, terminal and markdown editor settings, telemetry preferences, LSP overrides, worktree defaults.
  • Tasks and runs (tasks.db) — SQLite database with cards, statuses, labels, comments, runs, segments.
  • Reviews (review.db) — SQLite database with diff-review comment threads, attribution, and state changes.
  • Themes (themes/*-custom.json) — user-created custom themes.
  • Adapters (adapters/<name>/) — installed adapter manifests and scripts.
  • Snapshots (snapshots/) — a content-addressed git-style repository of point-in-time state captures (see Snapshots & Vault below).
See Data directory for the full layout and schemas.

How journaling works

atrium does not wait for you to hit save. The backend writes through three layers:

  1. Dirty tracking — every state mutation bumps a dirty flag. A heartbeat writer checks the flag and skips writes when nothing changed.
  2. Atomic writes — snapshots are written to a temp file and renamed. Partial writes are impossible on a crash because the rename is atomic at the filesystem level.
  3. Timestamped rotation — workspace snapshots are written with timestamps, and multiple recent timestamps are retained. If the latest snapshot is corrupt, atrium can fall back to the previous one.
Permissions are locked down: config is 0600, state files and directories are 0700.

Restoring on launch

On launch:

  1. Read state.json to find open workspaces and the focused one.
  2. Load each workspace's most recent snapshot from workspaces/{id}/.
  3. Rebuild the mosaic tree for each room.
  4. Materialize each pane:
- Terminal panes spawn a fresh PTY, replay scrollback into the xterm grid, and call the adapter's resume command if a session ID is stored. - Editor panes reopen the file, restore scroll and cursor position, and rehydrate Monaco state (including undo history). - Browser panes navigate to their last URL and replay their nav history. - Notepad panes rehydrate the active note plus per-note scroll, zoom, and cursor. - Git, search, tasks panes rehydrate their view state (expanded sections, queries, filters).

While the app starts, the launching screen shows live restore progress with the workspace currently being rehydrated and a per-step status. If a step hangs for more than a few seconds the screen surfaces a stuck? reload escape hatch — clicking it triggers a fresh launch instead of leaving you staring at a spinner.

The RestoreChooser is the recovery UI shown when atrium detects that the last exit was unclean. It lists rooms and panes with running-process indicators so you can choose which to restore.

Lazy pane mount

Inside a running workspace, atrium lazy-mounts panes at two levels:

  • Workspace level — panes in a workspace other than the active one aren't mounted at all until you switch to that workspace. Their snapshots are loaded into atoms, so the layout previews are correct, but their React components don't run.
  • Tab level — within the active workspace, only the panes in the focused room (and a small window of recent rooms) are mounted. Panes that scroll off the focused room remain in the workspace atoms; their components remount when the room becomes active again.
Pane state lives in app-level atoms rather than component-local React state, so a remount restores the pane to exactly the state it was in. The editor, markdown, git, tasks, review, and search panes all preserve their cursor, selection, scroll, expanded sections, modal state, and in-flight drafts across remount. The browser pane hides its CEF NSView on unmount and restores it on remount instead of tearing down the engine.

Agent session resume

Agent panes go through an extra step:

  1. atrium looks up the pane's stored session ID.
  2. The adapter's launch.resumeFlag is applied — for Claude Code this is --resume <session-id>, for Codex it is --resume <session-id>, for Gemini it is its own convention.
  3. The adapter's session store is checked (for example Claude's ~/.claude/history/); if the session is not found, atrium stops and marks the pane resume failed instead of silently starting a fresh session.
This gives agents the same survivability as plain terminals: your conversation history is preserved, not just the terminal scrollback.

What does not resume

  • Unsaved editor buffers are flushed to disk on close; there is no separate unsaved-buffer journal.
  • In-flight HTTP requests and long-running foreground shell commands (not adapter-managed) do not resume — the command finishes or is killed when the process dies. The pane records the last command so the activity sidebar can offer a Replay action.
  • Browser pane cookies do not carry over across CEF engine upgrades (for example, moving from WKWebView to Chromium). Sign-ins made before the upgrade must be redone once.
  • Reaped adapter sessions — when restoring a workspace whose adapter session ID has been garbage-collected by the adapter (Claude's ~/.claude/history/, Codex's local state), atrium fresh-launches a new session in that pane instead of leaving it stuck trying to resume. Any launcher overrides you'd configured (per-pane YOLO mode, custom flags, env vars) are re-applied to the auto-resumed session so the fresh launch matches the original launch's profile.

Snapshots and Vault

Above the per-write snapshot rotation sits a second-tier history: a content-addressed snapshot repository at ~/.atrium/snapshots/ that captures the full state of every workspace, the tasks database, and configuration as a single commit. The Library surfaces this history as the Vault.

A snapshot is committed when any of the following triggers fires:

  • Interval — every 30 minutes when state has changed since the last commit.
  • Shutdown — on clean exit, after the final state flush.
  • Pre-update — before tauri-plugin-updater installs a new build.
  • Pre-restore — automatic safety net before the user restores from any earlier snapshot.
  • Manual — when you click Take snapshot now in the Vault.
  • Event — when something destructive happens (workspace deleted, settings reset, bulk-delete from the Library).
Skippable triggers (Interval, Shutdown, event-driven) no-op when the canonical state hash matches the previous snapshot. User-initiated and safety-net triggers always commit. A retention engine prunes old snapshots according to a tiered policy (more recent commits kept densely, older commits sparsely).

Restoring from a snapshot opens a restore dialog that semantically diffs the snapshot against current state — file by file for workspace JSON, row by row for tasks.db — so you can preview exactly what will change before confirming. A pre-restore snapshot is committed first, so the restore is itself reversible. A shutdown gate prevents the app from exiting mid-snapshot.

The on-disk format is intentionally git-shaped: each snapshot is a commit referencing tree objects for each workspace plus a tasks.db exporter (VACUUM INTO). This makes snapshots cheap to take, fast to diff, and trivially deduplicated — re-snapshotting an unchanged workspace is essentially free.

Manually clearing state

  • Remove a specific workspace: delete its directory under ~/.atrium/workspaces/{id}/.
  • Reset all app state but keep configuration: rm -rf ~/.atrium/workspaces ~/.atrium/state.json.
  • Full reset: rm -rf ~/.atrium. This deletes configuration, snapshots, the Vault history, custom themes, installed adapters, and task data.