> ## Documentation Index
> Fetch the complete documentation index at: https://ctrlrun.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Recovery after a crash

> A restarted process repairs nothing and cannot know the holder is dead. An expired lease is a refusal, not a reclaim.

When a process dies holding an effect key, nothing happens. No sweeper frees it, no timer fires,
and the process that comes back does not scan or repair — it waits to be asked for an effect key,
exactly as it did before it died. That is deliberate, and this page is why.

## What a process may conclude

A process that finds a record reserved or executing may conclude exactly two things: **what state
the record is in**, and **whether its lease is still live**. It may not conclude that the holder
is gone.

*"The holder is dead"* is not knowable from the store, and no column makes it knowable. The record
carries an action id, which names an **attempt** and not a process. There is deliberately no
holder host and no holder pid, because the first thing anyone writes against those columns is a
reclaimer — *if the holder is not alive, free the key* — and that reclaimer is wrong on the day it
matters, when a container that stopped answering a health check still has an open socket to a
payment API. A field that invites a false inference is worse than no field.

## What actually happens

```
process dies holding refund:txn_9  →  record stays EXECUTING, lease ticking
   ↓ nobody contends
`ctrlrun effects` shows:  executing (lease expired)      ← a display, not a transition
   ↓ the next attempt on that key arrives
plan refuses it, and the record becomes AMBIGUOUS         ← a refusal, not a reclaim
   ↓
a person: ctrlrun resolve refund:txn_9 --committed|--failed   → resolved_by cli:local
or a hook: asks the remote, and only where the answer points  → resolved_by reconcile:<action>
```

An expired lease that is never contended stays executing in the table, and `ctrlrun effects`
prints it as `executing (lease expired)`. Reading is not a transition: listing effects, inspecting
one, or opening the store moves nothing.

The effect record's `resolved_by` column holds `cli:local` for a person, not their username.
CTRLRun does not authenticate the person at the terminal, so a name there would be a claim about
somebody made from a value that somebody controls. `cli:local` says exactly what is known:
somebody with the operator's terminal. The `EFFECT_RESOLVED` event carries the same string in its
`resolver` field, which is what lets you join the two, and the coarser `human` or `reconcile` in
its own `resolved_by`.

## A continuation held by a dead process

An elicitation holds a reservation open across a round trip. The row survives the process, and
with a shared store a **different** process may finish it. One resumption is admitted and no
more, because the token is consumed in the transaction that admits it. If the lease lapsed first, the resumption is refused
and the effect becomes `AMBIGUOUS` by the ordinary path.

## What this does not do

* **Nothing sweeps.** No background thread, no timer, no cron, no reap command. The lapse is
  noticed lazily, by the next contender, and never by anything running on a schedule.
* **An expired lease frees nothing.** It is a refusal, not a reclaim. The next attempt is refused
  and the record is now unknown.
* **A restart repairs nothing and reports nothing.** Opening the store migrates it and reads
  nothing else.
* **No receipt records the resolution.** A receipt is written when the action reaches a terminal
  state; a resolution happens afterwards, often days later and by somebody who was not the actor.
  Mutating the receipt would be an alteration the chain would correctly report. `ctrlrun effects`
  and `ctrlrun inspect` are where you find it.

**Verified by** `T159` — `AMBIGUOUS` survives a restart and still refuses a blind retry, against
every store — `T160` for the expired lease that frees nothing and the reads that transition
nothing, `T161` for the two authorities and what each is recorded as, `T162` for the process
identity that is deliberately absent, `T163` for the continuation held by a dead process, and
`T177d` for the display that is not a transition. `G5` in `ctrlrun verify` grades the blocked
blind retry on your store's backend, in a scratch store of its own.

## Next

* [Resolve an AMBIGUOUS effect](/guides/resolve-an-ambiguous-effect): the commands, step by step.
* [Operations](/production/operations): what to watch so you find these before a customer does.
* [Get started](/get-started/quickstart) · [Why](/why).


## Related topics

- [Operations](/production/operations.md)
- [The soak, and what it does not establish](/production/soak.md)
- [How reservation works](/production/how-reservation-works.md)
- [Run it in production](/production/index.md)
- [CTRLRun and durable workflow engines](/compare/durable-workflows.md)
