# Session persistence Lime saves the **desk**, separately from your Lisp configuration: Activities, named Views and Viewsets, current and cached drafts, hidden/shown membership, home Activity and shared eligibility, layout order/options/weights, floating rectangles, focus, dirty flags, partial View state, and logical Head selections. It does not serialize CLX objects, fonts, key bindings, callbacks, running processes, or application contents. `lime/dev` enables this automatically. The normal location is `$XDG_STATE_HOME/lime/sessions/`, falling back to `~/.local/state/lime/sessions/`. Files are separate for each DISPLAY (for example `display-_3A0.sexp` for `:0`, `display-_3A99.sexp` for `:99`). Your nested test desk does not overwrite your normal desktop's desk. A lifetime writer lock prevents two backends from using one explicit file simultaneously. Select a different file or disable persistence with launcher keywords—not an additional Lime environment variable: ```lisp (lime.dev:run-development-session :config-file #p"/home/adan/.config/lime/config.lisp" :session-file #p"/home/adan/.local/state/lime/my-desk.sexp") ;; For a disposable test: (lime.dev:start-development-session :display-name ":99" :session-file nil) ``` The lower-level `lime.x11:start-lime` / `start-backend` APIs default to no persistence; pass `:session-file :default` or a pathname to enable it. An explicit `:world` and a session file are mutually exclusive startup sources. Configuration reload does not change the selected session file. Configured startup hooks still run normally; hooks that create named Activities should tolerate those Activities already existing in a restored desk. `scripts/run-xephyr-dev` supports `--session-file PATH` and `--no-session`. Without either option it uses the display-specific default. ## What a restart restores On a **Lime-only restart within the same X server**, surviving clients reconnect to their Activities, checkpoints, and drafts—including unmapped hidden clients. The adapter verifies a random server token plus each client's XID and random window token. An XID, title, WM_CLASS, or PID alone is never enough: reused ids must not attach an unrelated application's window to an old checkpoint. On **logout, reboot, or X-server replacement**, the names and non-client layout settings survive, but the old windows do not. Missing clients are retired from the saved arrangements; an empty View remains a named checkpoint. This first version does **not** relaunch applications, restore terminal/browser contents, reserve placeholder slots for dead windows, or guess how newly launched windows should fill an old arrangement. Application/session-relaunch rules are a separate feature. Current physical Heads remain authoritative. Matching logical Head ids resume their saved selections. Drafts from removed Heads are suspended, not moved to an arbitrary other monitor or deleted; new Heads get ordinary blank drafts. Viewsets retain their logical mappings even when an output is absent. Leases are reconstructed and validated, never accepted as serialized ownership objects. ## Saving and failures Autosave observes model/focus changes on the owner thread. After 750 ms of quiet, or five seconds of ongoing edits, it captures a validated plain-data snapshot. Active pointer drags postpone capture until release. A single worker writes snapshots off the X event thread, keeping only the latest pending one. An unchanged desk produces no disk writes. A crash can lose edits since the last completed autosave; this is a snapshot system, not a per-operation journal. Normal shutdown flushes the final valid desk. A normal developer restart saves **before closing X**; if that save fails, restart aborts with the old backend still available for repair. Restart after an event-loop fault instead uses the last good disk snapshot, avoiding an automatic save of partially mutated state. `resume-development-session` still uses the retained in-memory world, not disk. For an explicit flush or inspection from SLY: ```lisp (lime.dev:save-development-session) (lime.dev:with-development-backend (backend) (lime.x11:backend-session-status backend)) ``` The status includes the file, restore/missing-client report, last-save time, submitted/completed write generations, and restore/snapshot/write errors. Disk write failures are nonfatal to ordinary operation and retry with five-second backoff; explicit flush reports the error to its caller. Invalid in-memory snapshots block automatic saving until repaired and explicitly saved again. Files are mode `0600`. Writes use a private temporary file in the same directory, file `fsync`, atomic rename, and directory `fsync` on Linux. A failed write before rename leaves the old file untouched; readers cannot see a partial replacement. The format is versioned, bounded plain data. Reading never calls `load`/`eval`, disables read-time evaluation, and rejects reader dispatch, circular structures, excessive nesting, and trailing data. Unknown versions, invalid relationships, duplicate leases, or malformed files are rejected before replacing the startup world. A rejected file is **not overwritten**: Lime starts a fresh usable desk with autosave blocked and reports the error. Inspect/back up the file first. To deliberately replace it with the current desk: ```lisp (lime.dev:save-development-session :replace-invalid-p t) ``` This does not override another session's file lock. Stop that other session or choose a different path. Back up important files separately; atomic writes are not a backup/history system. Names and window associations can be private even though the file contains no application buffers or process contents. ## Installing and testing Membership support adds a client structure slot and ASDF components. Relaunch Lime in a fresh Lisp process; configuration reload alone cannot install it. World snapshots use schema version 3. The X11 session envelope uses version 3, with ordered Activity positions, stable local-View shortcut slots, last-arrangement history, and portable/extended Activity navigation policy. Version-1/2 envelopes remain readable. Old Activities with one substantive arrangement become portable; genuinely split arrangements remain extended. This inference happens before missing-client pruning, preserving multi-monitor intent across reboot. Old empty drafts are retained, not recalled or deleted. Version-1 envelopes also receive initial shortcut assignments. Migration does not move a window or alter what the Heads display. Navigation metadata is validated before a saved world is installed. An explicit extended choice stays extended when all its windows close; it is never re-inferred from window count. Version 1 worlds load with all clients private. Version 1/2 worlds consolidate historical drafts into one current arrangement per Activity/Head. Active state wins, then saved navigation history, then the newest generated Presentation id. Actual leases win duplicated window locations; missing checkpoint slots are not live locations. Windows and saved Views are retained, and old source labels are detached where a checkpoint was recalled elsewhere. Migration is performed on a candidate world before installation; version 3 rejects duplicate live locations rather than silently repairing them. Keep a backup before upgrading or downgrading; older Lime builds may not read these schema versions. The same-X-server identity rules above still apply to moved/shared clients. ```lisp (asdf:test-system "lime/persistence") ``` ```sh sbcl --non-interactive --load test/x11-persistence-smoke.lisp sbcl --non-interactive --load test/x11-membership-smoke.lisp ``` The smoke test uses its own Xvfb, temporary state files, and fake client windows. It tests graceful/abrupt restart, hidden clients, dirty checkpoints, partial leases, changed topology/identity, corrupt files, disk failures, and the actual developer restart path. It does not connect to your running desktop.