Chio/Docs
LOGIN · JOIN

What Survives a Crash

When a call stops halfway, Chio keeps enough state to tell work it can reverse from work that may already have happened.

Concept chio@5f63e6e

On this page

Long-running agent work needs an execution history that survives interruption. If a process stops during a priced compute call, the tool may have run and the budget may have been charged even though the caller received no response. Continuing the work requires resolving that operation before risking the same effect again.

With durable admission configured, every admitted request is bound to an operation that survives the process, together with its reservations and its payment journal. Recovery reads those records and acts according to one line in the request’s life: dispatch.

Before dispatch, no tool has run, so the kernel can release what it held. After dispatch, an external effect is possible, so the kernel keeps what it held and records the uncertainty for someone to resolve.

Every admitted request has a durable identity

AdmissionOperationV1 binds a request to an operation, its participants, its current state, and any dispatch commitment. Versions and coordinator leases let recovery confirm that it is acting on the operation it actually read, not on a copy another coordinator has since advanced.

That identity is what makes the interrupted compute call recoverable. A retry resolves the existing operation instead of minting a new one. A request issued only because the caller never saw a response could repeat the work or spend a second allocation; the operation record is how the kernel avoids both. The captured compute example shows admission and charging on that path, without an injected crash.

Admission states and the transitions is_legal_transition accepts for the selected requirementsapprovalabudget_capturebbroker_attemptkrequirements
∣V∣=13,∣E∣=20|V| = 13,\quad |E| = 20
kind = ToolDispatchbefore dispatchafter dispatchfrom every enabledpre-dispatch statePreparedBrokerAttemptRegisteredApprovalRequiredBudgetAuthorizedApprovalReservedReadyToDispatchCapturePendingCompensatedBeforeDispatchDispatchCommittedFinalizingNotAcceptedAfterDispatchCommitOutcomeUnknownAfterDispatchCompletedDeniedAfterDelivery
ReadyToDispatch←{ApprovalReservedaBudgetAuthorized¬a∧bPrepared¬a∧¬b\texttt{ReadyToDispatch} \leftarrow \begin{cases} \htmlClass{role-call}{\texttt{ApprovalReserved}} & \htmlClass{role-grant}{a} \\ \texttt{BudgetAuthorized} & \lnot \htmlClass{role-grant}{a} \land \htmlClass{role-grant}{b} \\ \texttt{Prepared} & \lnot \htmlClass{role-grant}{a} \land \lnot \htmlClass{role-grant}{b} \end{cases}
and BudgetAuthorized when a∧b\text{and } \htmlClass{role-call}{\texttt{BudgetAuthorized}} \text{ when } \htmlClass{role-grant}{a} \land \htmlClass{role-grant}{b}
DispatchCommitted←{CapturePendingbReadyToDispatch¬b\texttt{DispatchCommitted} \leftarrow \begin{cases} \htmlClass{role-call}{\texttt{CapturePending}} & \htmlClass{role-grant}{b} \\ \texttt{ReadyToDispatch} & \lnot \htmlClass{role-grant}{b} \end{cases}
Admission states and the transitions is_legal_transition accepts for the selected requirementsapprovalabudget_capturebbroker_attemptkkind = ToolDispatchdispatchPreparedBrokerAttemptRegisteredApprovalRequiredBudgetAuthorizedApprovalReservedReadyToDispatchCapturePendingCompensatedBeforeDispatchDispatchCommittedFinalizingNotAcceptedAfterDispatchCommitOutcomeUnknownAfterDispatchCompletedDeniedAfterDelivery

13 states and 20 legal transitions. ReadyToDispatch follows ApprovalReserved or BudgetAuthorized; DispatchCommitted follows CapturePending.

ReadyToDispatch←{ApprovalReservedaBudgetAuthorized¬a∧bPrepared¬a∧¬band BudgetAuthorized when a∧bDispatchCommitted←{CapturePendingbReadyToDispatch¬bx→Compensated…  ⟺  pre(x)∧enabled(x)\begin{aligned} &\texttt{ReadyToDispatch} \leftarrow \\ &\quad \begin{cases} \htmlClass{role-call}{\texttt{ApprovalReserved}} & \htmlClass{role-grant}{a} \\ \texttt{BudgetAuthorized} & \lnot \htmlClass{role-grant}{a} \land \htmlClass{role-grant}{b} \\ \texttt{Prepared} & \lnot \htmlClass{role-grant}{a} \land \lnot \htmlClass{role-grant}{b} \end{cases} \\ &\quad \text{and } \htmlClass{role-call}{\texttt{BudgetAuthorized}} \text{ when } \htmlClass{role-grant}{a} \land \htmlClass{role-grant}{b} \\[8pt] &\texttt{DispatchCommitted} \leftarrow \\ &\quad \begin{cases} \htmlClass{role-call}{\texttt{CapturePending}} & \htmlClass{role-grant}{b} \\ \texttt{ReadyToDispatch} & \lnot \htmlClass{role-grant}{b} \end{cases} \\[8pt] &x \to \texttt{Compensated…} \iff \\ &\quad \mathrm{pre}(x) \land \mathrm{enabled}(x) \end{aligned}
In Rust · state.rs:55029 lines
if to == AdmissionOperationState::CompensatedBeforeDispatch {
    return from.is_pre_dispatch() && predispatch_state_enabled(requirements, from);
}
let ready_source = if requirements.approval {
    AdmissionOperationState::ApprovalReserved
} else if requirements.budget_capture {
    AdmissionOperationState::BudgetAuthorized
} else {
    AdmissionOperationState::Prepared
};
match to {
    // …
    AdmissionOperationState::ReadyToDispatch => {
        from == ready_source
            || (kind == AdmissionOperationKind::ToolDispatch
                && requirements.budget_capture
                && requirements.approval
                && from == AdmissionOperationState::BudgetAuthorized)
    }
    // …
    AdmissionOperationState::DispatchCommitted => {
        from == if requirements.budget_capture {
            AdmissionOperationState::CapturePending
        } else {
            AdmissionOperationState::ReadyToDispatch
        }
    }
    // …
}
The dispatch states of a ToolDispatch operation. Every solid edge is a pair is_legal_transition accepts for the selected requirements; dashed edges are legal under other requirements. The rule says which moves are allowed, not which path a call takes. GovernedActiveResponse lacks the BudgetAuthorized shortcut, and GovernedEconomicMutation has its own four-edge machine.

The states before the dispatch line are the ones recovery may compensate: each enabled pre-dispatch state has a legal move to CompensatedBeforeDispatch. The states after it describe what the kernel knows about an effect that may have happened. Which states exist depends on the call’s requirements, so the transitions are a rule the kernel evaluates; a single fixed chain would misrepresent them.

Inspect the admission state and transition rules

The generated enum contains 18 states across tool dispatch and governed economic mutation. Dispatch legality depends on the required broker, budget, and approval participants.

crates/kernel/chio-kernel/src/admission_operation.rs167-189rust
pub enum AdmissionOperationState {
    Prepared,
    BrokerAttemptRegistered,
    ApprovalRequired,
    BudgetAuthorized,
    ApprovalReserved,
    ReadyToDispatch,
    CapturePending,
    DispatchCommitted,
    Finalizing,
    Completed,
    CompensatedBeforeDispatch,
    NotAcceptedAfterDispatchCommit,
    OutcomeUnknownAfterDispatch,
    /// The delivered output did not match a grant's committed output
    /// digest. A signed Deny is persisted; the open hold is released and
    /// zero is captured.
    DeniedAfterDelivery,
    MutationReady,
    MutationSubmitted,
    EconomicMutationApplied,
    EconomicMutationNotApplied,
}
crates/kernel/chio-kernel/src/admission_operation/state.rs521-623rust
pub(super) fn is_legal_transition(
    kind: AdmissionOperationKind,
    requirements: AdmissionParticipantRequirements,
    from: AdmissionOperationState,
    to: AdmissionOperationState,
) -> bool {
    if kind == AdmissionOperationKind::GovernedEconomicMutation {
        return matches!(
            (from, to),
            (
                AdmissionOperationState::Prepared,
                AdmissionOperationState::MutationReady
            ) | (
                AdmissionOperationState::MutationReady,
                AdmissionOperationState::MutationSubmitted
            ) | (
                AdmissionOperationState::Prepared
                    | AdmissionOperationState::MutationReady
                    | AdmissionOperationState::MutationSubmitted,
                AdmissionOperationState::EconomicMutationNotApplied
            ) | (
                AdmissionOperationState::MutationSubmitted,
                AdmissionOperationState::EconomicMutationApplied
            )
        );
    }
    if !kind.uses_dispatch() {
        return false;
    }
    if to == AdmissionOperationState::CompensatedBeforeDispatch {
        return from.is_pre_dispatch() && predispatch_state_enabled(requirements, from);
    }
    let ready_source = if requirements.approval {
        AdmissionOperationState::ApprovalReserved
    } else if requirements.budget_capture {
        AdmissionOperationState::BudgetAuthorized
    } else {
        AdmissionOperationState::Prepared
    };
    match to {
        AdmissionOperationState::BrokerAttemptRegistered => {
            requirements.broker_attempt && from == AdmissionOperationState::Prepared
        }
        AdmissionOperationState::BudgetAuthorized => {
            requirements.budget_capture
                && ((requirements.approval && from == AdmissionOperationState::ApprovalRequired)
                    || from
                        == if requirements.broker_attempt {
                            AdmissionOperationState::BrokerAttemptRegistered
                        } else {
                            AdmissionOperationState::Prepared
                        })
        }
        AdmissionOperationState::ApprovalRequired => {
            requirements.budget_capture
                && requirements.approval
                && from
                    == if requirements.broker_attempt {
                        AdmissionOperationState::BrokerAttemptRegistered
                    } else {
                        AdmissionOperationState::Prepared
                    }
        }
        AdmissionOperationState::ApprovalReserved => {
            requirements.approval
                && from
                    == if requirements.budget_capture {
                        AdmissionOperationState::BudgetAuthorized
                    } else {
                        AdmissionOperationState::Prepared
                    }
        }
        AdmissionOperationState::ReadyToDispatch => {
            from == ready_source
                || (kind == AdmissionOperationKind::ToolDispatch
                    && requirements.budget_capture
                    && requirements.approval
                    && from == AdmissionOperationState::BudgetAuthorized)
        }
        AdmissionOperationState::CapturePending => {
            requirements.budget_capture && from == AdmissionOperationState::ReadyToDispatch
        }
        AdmissionOperationState::DispatchCommitted => {
            from == if requirements.budget_capture {
                AdmissionOperationState::CapturePending
            } else {
                AdmissionOperationState::ReadyToDispatch
            }
        }
        AdmissionOperationState::Finalizing
        | AdmissionOperationState::NotAcceptedAfterDispatchCommit
        | AdmissionOperationState::OutcomeUnknownAfterDispatch => {
            from == AdmissionOperationState::DispatchCommitted
                || (to == AdmissionOperationState::OutcomeUnknownAfterDispatch
                    && from == AdmissionOperationState::Finalizing)
        }
        AdmissionOperationState::Completed => from == AdmissionOperationState::Finalizing,
        // A delivery-digest mismatch is decided during finalization, so
        // the only legal predecessor is Finalizing.
        AdmissionOperationState::DeniedAfterDelivery => from == AdmissionOperationState::Finalizing,
        _ => false,
    }
}
crates/kernel/chio-kernel/src/admission_operation/state.rs424-440rust
pub(super) fn predispatch_state_enabled(
    requirements: AdmissionParticipantRequirements,
    state: AdmissionOperationState,
) -> bool {
    match state {
        AdmissionOperationState::Prepared | AdmissionOperationState::ReadyToDispatch => true,
        AdmissionOperationState::BrokerAttemptRegistered => requirements.broker_attempt,
        AdmissionOperationState::ApprovalRequired => {
            requirements.budget_capture && requirements.approval
        }
        AdmissionOperationState::BudgetAuthorized | AdmissionOperationState::CapturePending => {
            requirements.budget_capture
        }
        AdmissionOperationState::ApprovalReserved => requirements.approval,
        _ => false,
    }
}
Look up the economic-mutation transitions

Governed economic mutation shares the enum but has a separate closed transition table. These 6 moves come directly from that table.

FromTo
prepared
mutation_ready
mutation_ready
mutation_submitted
prepared
economic_mutation_not_applied
mutation_ready
economic_mutation_not_applied
mutation_submitted
economic_mutation_not_applied
mutation_submitted
economic_mutation_applied

SQLite state spans three files linked by identifiers

The operation record is one table among several. With the SQLite stores, a governed call leaves rows in up to three files. The receipt database keeps the signed receipts, the claim log that checkpoints commit to, the checkpoints, and capability lineage. The admission database keeps each operation with its hash-chained commits and tool outcome, the grant budgets and their holds, and revocations. The approval store keeps pending requests and spent decisions.

State storesChoose a join key.

Foreign keys stop at the file; ids carry across

Each store is a SQLite file with one writer that serializes every insert and a pool of eight reader connections. The receipt database is the file named by --receipt-db; the admission database sits beside --session-db; under chio api protect the approval tables live in the receipt database file. Foreign keys hold only inside a file. The kernel joins records across files by their shared ids. Triggers on both receipt tables write the claim-log entry in the same transaction as the receipt. What survives a crash.

In Rust · open.rs:110715 lines
CREATE TRIGGER chio_tool_receipts_project_claim_log_entry
AFTER INSERT ON chio_tool_receipts
BEGIN
    INSERT INTO claim_receipt_log_entries (
        receipt_id,
        receipt_kind,
        source_seq,
        // …
    ) VALUES (
        NEW.receipt_id,
        'tool_receipt',
        NEW.seq,
        // …
    );
END;
Ten of the tables that carry admission and evidence, each with its primary key; chio_child_receipts, chio_hitl_resolved, tool_outcomes, and budget_authorization_holds are described with the keys they hold. The store crate defines more than 200 tables, including budget replication, the finding market, fiscal schedules, liability, and credit. With durable admission off, budgets and revocations sit in their own --budget-db and --revocation-db files.

Foreign keys are declared only inside a file: a capability’s lineage row points to its parent, and an operation’s commits and outcome point to the operation. capability_id links one call’s authority across the files. The receipt database records it per receipt and per lineage row; the admission database keys budgets, holds, and revocations by it, and the kernel matches the rows on that id in code.

No foreign key joins the files, so nothing in the schema keeps them consistent with each other. Treat them as one set. Back them up together, and rehearse the restore procedure in Backup and restore before relying on a copy.

Dispatch decides what recovery may do

Two kinds of interruption reach the same records by different routes. When a future is dropped or a panic unwinds, Rust cleanup runs in the live process: the kernel’s drop guard acts at once. A hard process stop, an abort, or a power loss runs no cleanup at all; recovery then depends on what was persisted and on startup reconciliation.

Before dispatch, compensate each held resource

The live drop guard attempts each applicable cleanup independently. A failure in one step does not skip the others.

What was heldWhat cleanup releases
Monetary holds and payment authorizationThe hold and its authorization, through the configured payment path.
Invocation countersThe invocation this call had counted against its grant.
Runtime-admission reservationsThe reservations admission made for this call.
The delegated child-budget leaseOnly the holder this evaluation acquired. An overlapping evaluation may hold the same share, and freeing its allocation would let a sibling overspend.

When every step succeeds, the durable operation can be terminalized as compensated_before_dispatch without writing a cancellation receipt. When a step fails, the guard attempts a signed fault receipt carrying pre_dispatch_cleanup_failed, the failing step, and the affected hold or reservation identifiers.

After dispatch, keep what was held and record the uncertainty

Once dispatch begins, releasing a single-use reservation could authorize the same effect a second time. The guard instead flushes buffered child receipts that were already signed, retains the runtime-admission reservations, and attempts to record the parent cancellation with metadata describing the ambiguous dispatch.

When the operation has a dispatch commitment but no durable tool outcome, terminalization records outcome_unknown_after_dispatch. It refuses to overwrite a durable outcome that already exists. The state gives an operator a concrete recovery target without inventing success or compensation; Unknown outcomes treats the same retained uncertainty as a security boundary.

Read the cancellation reasons the guard records
crates/kernel/chio-kernel/src/kernel/kernel_drop_guard.rs14-20rust
const POST_ADMISSION_DROP_REASON: &str = "tool evaluation future dropped after admission";
const POST_DISPATCH_CREDENTIAL_COMMIT_FAILURE_REASON: &str =
    "dispatch credential commit failed after tool execution";
const POST_DISPATCH_URL_ELICITATION_REASON: &str =
    "tool server returned URL elicitation after dispatch; outcome is unknown";
const PRE_DISPATCH_CLEANUP_FAULT_REASON: &str =
    "tool evaluation future dropped before dispatch with cleanup fault";

After a hard stop, reconcile what was persisted

Startup reconciliation reads recoverable operations from the configured durable store. It attempts compensation for applicable pre-dispatch states, resolves dispatch commitments without a durable outcome as unknown, and completes persisted finalization work through its recovery path.

Handled compensation and terminalization failures are deferred, so the sweep can attempt the other operations before it returns a failure. Store access errors and malformed recovery data can stop the sweep earlier. A failed operation stays unresolved; it never becomes a successful recovery. Failure and recovery covers the runtime mechanisms.

One interrupted dispatch survives a restart

The restart case is part of the self-test that ships with every release. A paid tool runs once, then an injected failure prevents its outcome from being recorded. The suite closes the authority, reopens the same database with a fresh kernel, and runs startup reconciliation against the installed binaries, using real persisted records and restart logic. Run it on the host that will hold your durable store:

Terminalbash
chio security selftest restart --evidence ./evidence
Output
suite            result
restart          pass   every required case passed once; inventory complete
1 suite passed; evidence in ./evidence

A missing, renamed, or failed case fails the suite. The case this section follows is sqlite_restart_terminalizes_an_unrecorded_dispatch_without_moving_funds, and its log in ./evidence shows the same transition the SQLite integration suite checks.

Only the operation state changes across the restart. It records the unresolved outcome:

ObservationBefore reopeningAfter recovery
Operation stateDispatchCommittedOutcomeUnknownAfterDispatch
Payment journalAuthorizedStill authorized
Tool invocations1Still 1
Captures, releases, refunds0, 0, 0Still 0, 0, 0
Budget exposure10 minor unitsUnchanged

Reconciliation handles one recoverable operation, and a second sweep handles zero. The unknown-outcome record stays addressable by its operation identity and terminal replay key. Because the caller’s failure did not establish whether the tool ran, recovery preserves the uncertainty and makes no further payment transition.

Install the stores before admitting work

Durable kernel wiring · excerpt from the integration testrust
kernel.set_durable_admission_store(
    operations.clone(), outcomes, fence.clone(),
)?;
kernel.set_budget_store_handle(budget.clone());
kernel.set_payment_adapter(Box::new(payment_adapter));
// Register the same application tool and restore its required context.
let recovered = kernel.reconcile_durable_admission_startup()?;

Here operations, outcomes, budget, and the mutation fence come from the reopened SQLite authority. The complete test also provisions private directories, kernel configuration, the signed grant, and the payment adapter; this excerpt alone does not make a host recovery-capable.

Investigate by operation, not by request

Inspect the retained operationrust
let operation = operations.load_by_operation_id(&operation_id)?;
let journal = operations.load_payment_journal(
    operation_id.as_str(), &fence,
)?;

Resolve the operation, its tool outcome, its payment journal, and its retained evidence together. If the outcome is unknown, query the tool or payment system through the integration’s supported reconciliation path; a client timeout is not a reason to mint a fresh authorization. If cleanup failed, investigate the named hold or reservation. If finalization is pending, let the configured coordinator complete it.

When the outcome is already durable

The neighboring tests sqlite_restart_completes_a_committed_capture_without_request_replay and sqlite_restart_completes_a_committed_release_without_request_replay exercise persisted finalization after an interrupted completion path; run the whole file to cover them. A durable outcome is never overwritten by an invented cancellation or a second tool invocation.

Retained state is not serving readiness

A separate test killed the pricing service process after a successful write. Opening its SQLite database in a separate process recovered the committed rows, and the retained HTTP receipts still passed signature verification. The restarted HTTP listener did not answer within the readiness check. The captured observations establish retained data and evidence, not a recovered serving path.

After a hard stop, check both layers: inspect the persisted operation and records, then exercise a governed request before admitting new traffic. A bound port is not enough. Keep an unready instance out of service while its recovery and authority state are investigated. The in-flight outcome and payment results above come from the separate SQLite suite; they are not inferred from this completed write.

A consumed nonce must outlive a restart

Durable operations stop the kernel from repeating its own work. Execution nonces stop a caller from presenting the same dispatch authorization twice. A nonce binds one dispatch to the subject, capability, server, tool, request ID, and parameter hash. Its signature establishes that it is authentic; consuming it through the nonce store establishes whether it has already been used.

An execution nonce's consumed marker across a process restart, in memory and in a persistent store061218243036t (s)in memoryLRU cache10marker lost at restartaccepted again
cmem(t)=1[6≤t<12]\htmlClass{role-seal}{c_{\mathrm{mem}}}(t) = \mathbf{1}[6 \le t < \htmlClass{role-grant}{12}]
cmem(20)=0⇒accepted\htmlClass{role-seal}{c_{\mathrm{mem}}}(\htmlClass{role-call}{20}) = 0 \Rightarrow \htmlClass{role-call}{\text{accepted}}
persistentSQLite10Err(Replayed)
cdisk(t)=1[t≥6]\htmlClass{role-seal}{c_{\mathrm{disk}}}(t) = \mathbf{1}[t \ge 6]
cdisk(20)=1⇒Replayed\htmlClass{role-seal}{c_{\mathrm{disk}}}(\htmlClass{role-call}{20}) = 1 \Rightarrow \htmlClass{role-deny}{\texttt{Replayed}}
tp=20\htmlClass{role-call}{t_p = 20}
issued_atexpires_atsigned window, 30 sexpiredfirst use Ok(true)
restart tr=12\htmlClass{role-grant}{\text{restart } t_r = 12}
accept(tp)  ⟺  tp<exp  ∧  c(tp)=0\mathrm{accept}(\htmlClass{role-call}{t_p}) \iff \htmlClass{role-call}{t_p} < \htmlClass{cor-parent}{\mathrm{exp}} \;\wedge\; \htmlClass{role-seal}{c}(\htmlClass{role-call}{t_p}) = 0
An execution nonce's consumed marker across a process restart, in memory and in a persistent store061218243036marker lost at restartaccepted againErr(Replayed)
tp=20\htmlClass{role-call}{t_p = 20}
issued_atexpires_atexpiredin memoryLRU cachepersistentSQLitefirst use Ok(true)
restart tr=12\htmlClass{role-grant}{\text{restart } t_r = 12}

The process restarts at 12 seconds. A replay at 20 seconds: in memory, the replay is accepted, a second dispatch; in a persistent store, the replay is rejected as Replayed.

accept(tp)  ⟺  tp<exp∧c(tp)=0cmem(t)=1[6≤t<12]cdisk(t)=1[t≥6]\begin{aligned} \mathrm{accept}(\htmlClass{role-call}{t_p}) &\iff \htmlClass{role-call}{t_p} < \htmlClass{cor-parent}{\mathrm{exp}} \wedge \htmlClass{role-seal}{c}(\htmlClass{role-call}{t_p}) = 0 \\[6pt] \htmlClass{role-seal}{c_{\mathrm{mem}}}(t) &= \mathbf{1}[6 \le t < \htmlClass{role-grant}{12}] \\ \htmlClass{role-seal}{c_{\mathrm{disk}}}(t) &= \mathbf{1}[t \ge 6] \end{aligned}
In Rust · execution_nonce.rs:11119 lines
    /// Reserve a nonce while telling the store when the nonce stops
    /// being cryptographically valid. Durable implementations (SQLite,
    /// remote KV stores) MUST retain the consumed marker until at least
    /// `nonce_expires_at` so replay protection covers the nonce's full
    /// validity window.
    // …
    fn reserve_until(&self, nonce_id: &str, _nonce_expires_at: i64) -> Result<bool, KernelError> {
// …
pub fn consume_execution_nonce(
    nonce_store: &dyn ExecutionNonceStore,
    nonce_id: &str,
    nonce_expires_at: i64,
) -> Result<(), ExecutionNonceError> {
    match nonce_store.reserve_until(nonce_id, nonce_expires_at) {
        Ok(true) => Ok(()),
        Ok(false) => Err(ExecutionNonceError::Replayed),
        Err(error) => Err(ExecutionNonceError::Store(error.to_string())),
    }
}
Illustrative seconds with the default 30-second nonce lifetime, for a call that presents a nonce; with require_nonce at its default of false, callers may omit one. A durable store keeps the consumed marker through the signed expiry. Single-use authorization still does not establish exactly-once effects inside an external tool.

The second half of that check is only as durable as the store behind it. A store that keeps the consumed marker in memory forgets it on restart, and a replay inside the nonce’s signed window is accepted again. A persistent store keeps the marker through the signed expiry, so the same replay is refused.

The feature requires an installed ExecutionNonceConfig. Setting require_nonce makes execution-bound dispatch require a fresh nonce; with the default of false, a presented nonce is checked but callers may omit one. One-time authorization does not establish exactly-once effects inside an external tool: the executor and its own persistence still decide that.

What the recovery record establishes

ObservationConclusion
Compensation recorded before dispatchThe configured recovery path committed a pre-dispatch compensation outcome.
Outcome unknown after dispatchDispatch may have produced an effect. Resolve the operation before authorizing a retry.
A cleanup fault in a signed receiptA named reversal failed. Its metadata identifies the resources to investigate.
No cancellation receiptInsufficient evidence by itself: clean cleanup, a hard stop, or a failed append can each explain the absence.

Destructor cleanup is best effort. A failed receipt append is logged as an audit_fault and may leave no cancellation receipt. Admission storage, outcome storage, receipt storage, and recovery must all be configured for the failures a deployment needs to survive.

Follow the operation through recovery

An ongoing application needs to retain its progress as well as the state of individual operations. The kernel’s admission records preserve operation state; a configured process host retains the actor’s identity, checkpoints, and named calls. To resume a worker’s planning state, tool keys, and messages after an operating-system restart, use Agent Processes and its runnable handoff.

Data restrictions survive the same restart. With information flow configured, the flow store retains the labels each read left behind for the authenticated principal, its delegation lineage, and the session. A reopened host evaluates the next call against them. The persistent taint example reopens a host after a restricted read and shows the restriction still in force.