# Recovery The invariant: **at every point where the process can die, you have the previous valid deployment or the new valid one, and the next start can detect and fix everything in between.** ## The journal Every mutation that touches the live install writes `.txn/.json` first, advances `phase` after each durable step, and deletes it only after the registry has been saved. A journal entry found at start means a process died. `Repair installation` (and every start) reads it: | Operation | Phases | If the process died after… | Recovery | |---|---|---|---| | **apply** | `begin → snapshot → prepared → activated → linking → (core-backup) → linked` | *begin…prepared* | nothing live changed; the unfinished snapshot is swept (it is rebuilt on the next Apply) | | | | *activated, linking* | the stable link points at a finished snapshot; dsh still uses the old link or none; run Apply again | | | | *core-backup* | the original package was moved to `.backup/` and nothing replaced it → **it is moved back** (dsh never stays without the package); your work is rescued first | | | | *linked* (registry not saved) | the registry is brought up to date from the facts (link + finished snapshot) | | **restore** | `begin → restoring → restored` | *restoring* | a core package folder that was unlinked but not yet restored is moved back from `.backup/`; a profile plugin that is still linked stays active | | **delete** | `begin → snapshots → moving → moved` | *moving* | nothing moved; the plugin is intact | | | | *moved* | the repo is in `.trash/`; the plugin is removed from the list and you are told where the repo is | Also swept on every start: temp links/files of dead processes (`*.tmp--…`), abandoned `.adding-*` downloads, unfinished snapshots of tracked plugins that nothing links to. **Never** deleted: repos, backups, stashes, branches, anything a stable link points at, anything belonging to something this plugin does not track. ## Crash loop (a freshly applied plugin makes dsh restart) After an Apply the plugin is on *probation*. The first start afterwards runs a 60 s health timer (`LPM_HEALTH_MS`); surviving it ends probation. A second start inside that window without a clean shutdown means the new code crashed dsh: 1. your work is saved: uncommitted edits → `git stash` (`lpm rescue …`), committed work → branch `lpm-rescue/