Chio/Docs
LOGIN · JOIN

Dispatch Commitments

See how one durable commit joins a call's budget, approval, nonce, and attempt, and what recovery may do on either side of it.

Concept chio@5f63e6e

On this page

A call can spend several kinds of authority at once and still be recovered safely after a crash. Before the connector receives the request, the kernel commits the call's budget hold, approval use, execution nonce, and provider attempt in one durable transaction.

That commit decides what recovery may do. A reservation that has not captured can be reversed. A captured operation may have caused its effect, so it keeps that possibility even when the process dies before recording a result.

One transaction captures every participant, or none of them

A partial capture would let accounting and execution disagree: a grant could look available after its tool ran, or a nonce could be consumed while its hold stayed reversible. The combined transaction advances the original operation and every participating authority together.

Capture cutpointsChoose where the process stopped.
Bbudget holdτapprovalnnoncedattemptholdcapturehandoffeffect?releasereceiptdispatch againreconcile the original attemptBbudget holdτapprovalnnoncedattemptholdcapturehandoffeffect?releasereceiptafter capture: reconcile the original attempt

A committed capture keeps its outcome unknown

Capture is durable and the connector recorded no effect. Recovery keeps the captured allowance and records OutcomeUnknownAfterDispatch. An empty effect log in a test is still no provider's proof of non-acceptance, so the original attempt is reconciled, never dispatched again. Unknown outcomes.

baseline_capture_before_connector

One native operation from its hold to its receipt, with the participants the capture commit joins and the five cutpoints of Chio's native restart inventory. Spacing carries no duration; information-flow joins, caller-owned execution, and the serving fence are left out.

Before the physical commit, an interrupted transaction rolls its participants back together. After the database commit, the captured state survives whether or not the connector returns. If synchronization of the external anchor is interrupted, the replacement process verifies that extension before it continues.

The property belongs to the configured composition. Several SQLite-backed stores establish it only when they use the supported combined transaction and ownership path, so an atomicity claim rests on that integration; the shared word SQLite and isolated store tests establish nothing about it.

Reservations hold each authority while the call prepares

A request can need a remaining invocation, an approved disclosure, a provider-use allowance, and a valid one-use credential at the same time. Reserving them keeps concurrent work from spending the same capacity while preparation runs. Capture turns each reservation into a use of the original operation:

  • Invocation or monetary budget. The original reversible hold becomes the original captured allowance.
  • Approval authority. The bound continuation and reservation become the original use episode.
  • Proof or execution nonce. The validated operation binding becomes a consumed or committed original identity.
  • Broker attempt. The prepared, authenticated attempt joins the committed execution unchanged.
  • Information-flow state. Classified input and its required generation become retained joins and release custody.

An operation can also depend on a single-use declassification grant. A complete composition installs its participants together, and the host's constructor and readiness path reject a missing one: a verifier installed without the physical handoff or the durable store leaves the execution contract incomplete.

A capture that disagrees with current state is refused before dispatch

The capture request names the original operation and the state its current owner expects to change. Each group of fields answers one question at capture:

  • operation_id and expected_operation_version: is this still the original operation, at the transition the caller observed?
  • coordinator_lease_epoch and store_fence: does the mutating host still own this serving epoch?
  • hold_id, capability_id, and grant_index: does the captured allowance belong to this authority and grant?
  • Revocations, authorization digests, and expiry: does current authority still permit the capture?
  • Expected quota usage and the previous global sequence and digest: do accounting and the authority's global commit history match what the transaction expects?

A refusal here can mean a stale operation version, withdrawn authority, an expired lease, or an inconsistent predecessor, and an exhausted budget is only one cause among them. A successful commit retains which authority and guarantee applied, the prior and new global commitments, and the serving fence.

The capture request and the combined commit
crates/kernel/chio-kernel/src/admission_operation/capture.rs44–60rust
pub struct AdmissionCaptureRequestV1 {
    pub operation_id: AdmissionOperationId,
    pub expected_operation_version: u64,
    pub coordinator_lease_epoch: u64,
    pub capability_id: AdmissionIdentifier,
    pub grant_index: u32,
    pub hold_id: AdmissionIdentifier,
    pub event_id: AdmissionIdentifier,
    pub revocation_set: CanonicalRevocationSet,
    pub authorization_artifact_digests: Vec<AdmissionDigest>,
    pub authorization_expires_at_unix_ms: u64,
    pub expected_invocation_quota_usages: Vec<BudgetInvocationQuotaUsage>,
    pub authority: BudgetEventAuthority,
    pub previous_global_commit_sequence: u64,
    pub previous_global_commit_digest: AdmissionDigest,
    pub store_fence: StoreMutationFence,
}
crates/kernel/chio-kernel/src/admission_operation/capture.rs119–127rust
pub struct AdmissionCombinedCaptureCommit {
    pub authority: BudgetEventAuthority,
    pub guarantee_level: BudgetGuaranteeLevel,
    pub previous_global_commit_sequence: u64,
    pub previous_global_commit_digest: AdmissionDigest,
    pub global_commit_sequence: u64,
    pub global_commit_digest: AdmissionDigest,
    pub store_fence: StoreMutationFence,
}

These are store contracts. The kernel captures with live participants, and a request built from an old receipt carries none of them: commitment fields in a receipt describe what was captured, and only the current serving owner performs the next mutation.

One operation identity runs from admission to release

The request commitment covers the normalized operation and its relevant context. Its durable identity connects admission, reservations, capture, execution, and release, so a retry locates the original state instead of drawing fresh authority for a same-looking payload. Admission and execution nonces derives that identity from the request.

A retained record describes what happened; the current serving fence decides who may mutate the operation now. One worker at a time owns an operation's live mutation sequence. A duplicate attempt that fails to acquire it leaves the owner's hold, capture, and dispatch untouched, and a malformed continuation leaves the legitimate pending operation intact.

After a restart, the new owner acquires the supported authority to continue recovery. Credentials tied to an old hold remain evidence without permission, and an ownership failure is resolved on the original operation, never by creating another with the same display identifier.

Revocation can still stop the handoff after capture

Authority can change after admission. At the handoff boundary the runtime rechecks revocation and the operation-owned security state. A revocation that wins before capture prevents both the effect and the captured quota; one that arrives after capture stops the handoff and keeps the captured state.

That retained capture keeps “handoff stopped here” distinct from a claim that the effect was impossible. Once a provider may have accepted the request, only the provider can say whether the effect happened: the original attempt stays, and the selected executor's evidence contract, or authenticated provider status where the provider offers it, reconciles it. Withdrawal can also stop a later output release, and the committed capture stays.

The return has its own checks. A provider response passes output evaluation, classification, durable restriction joins, and release authorization. The release checkpoint names the exact guarded response and receipt that may be replayed, which is a different fact from delivery to the client.

Read more in Unknown outcomes.

The repository read committed at version 6 and completed at version 8

Run the Execution application with its host running, from its prepared example commands. The client retains the original receipt before it displays the guarded value; verify that receipt under the host's retained key, then select its admission projection.

Worker terminalbash
review read-pricing-v1 src/pricing.py read-pricing.receipt.json  < .chio/connection.json
chio receipt verify --input read-pricing.receipt.json  --trusted-kernel-pubkey .chio/kernel.pub
jq '.metadata.admission_operation | {
  projected_state, projected_dispatch_state, projected_operation_version,
  committed_version: .retained_dispatch_commit.committed_version,
  transport: .retained_dispatch_commit.provider_attempt.transport_id
}' read-pricing.receipt.json
Selected fields of the original receipt
{
  "projected_state": "completed",
  "projected_dispatch_state": "terminal",
  "projected_operation_version": 8,
  "committed_version": 6,
  "transport": "kernel-tool-server:workspace"
}

You should see dispatch committed at version 6 and the completed projection at version 8, with the retained attempt naming kernel-tool-server:workspace. The versions are observations from this run, never constants for an adapter. The output is a projection of the original receipt, not a new signed artifact; the full receipt also binds the operation ID, request hash, coordinator lease, and store fence.

This read requires two participants, budget capture and a broker attempt, and both carry the same operation ID. Here the attempt identifies the registered repository transport, with no credential broker involved. Its approval, execution-nonce, payment, and authorization-consumption requirements are false, so the read exercises only the participants it configures. Compare it with the interrupted read:

Original callRetained dispatchProjected outcomeOn original-key replay
Completed repository readCommit version 6; terminalCompleted at version 8Same response and receipt; no second read
Interrupted after readingCommit version 6; terminalUnknown after dispatch at version 7; no tool outcome IDUnknown retained; no second read

Both dispatch projections are terminal and only one outcome is completed, so a dashboard that reads terminal dispatch as successful work misreports the interrupted call. A missing tool-outcome record leaves its capture in place: the independent resource journal observed the read before the interruption.

Observe dispatch from an embedding host
security-host/observe_dispatch.rsrust
use chio_kernel::{ChioKernel, KernelError, SecurityInvocationContext,
    ToolCallRequest, ToolCallResponse};
use chio_kernel::admission_operation::{AdmissionOperationId,
    AdmissionOperationStore, AdmissionOperationStoreError, AdmissionDispatchState};

#[derive(Debug)]
pub struct DispatchObservation {
    pub state: AdmissionDispatchState,
    pub committed_version: Option<u64>,
}

pub fn inspect_dispatch(
    store: &dyn AdmissionOperationStore,
    original_id: &AdmissionOperationId,
) -> Result<Option<DispatchObservation>, AdmissionOperationStoreError> {
    Ok(store.load_by_operation_id(original_id)?.map(|operation| DispatchObservation {
        state: operation.dispatch_state(),
        committed_version: operation.dispatch_commit().map(|commit| commit.committed_version),
    }))
}

/// Inspect both sides of the real call even if evaluation/finalization errors.
/// Both lookups are advisory snapshots; neither acquires mutation ownership.
pub fn invoke_and_observe_dispatch(
    kernel: &ChioKernel, store: &dyn AdmissionOperationStore,
    original_id: &AdmissionOperationId, original: &ToolCallRequest,
    current_context: &SecurityInvocationContext,
) -> (
    Result<Option<DispatchObservation>, AdmissionOperationStoreError>,
    Result<ToolCallResponse, KernelError>,
    Result<Option<DispatchObservation>, AdmissionOperationStoreError>,
) {
    let before = inspect_dispatch(store, original_id);
    let response = kernel.evaluate_tool_call_blocking_with_security_context(original, current_context);
    let after = inspect_dispatch(store, original_id);
    (before, response, after)
}

observe_dispatch.rs takes the host's configured kernel, admission store, original request and context, and original operation ID. It keeps the before lookup, the invocation result, and the after lookup independent: an unavailable lookup leaves the response intact, an invocation error still records the after observation, and neither lookup acquires mutation authority.

Progress comes from the dispatch state, never from a guess about the tool

The admission ledger tracks dispatch independently of the client transport. Build diagnostics and recovery tooling on the typed state.

crates/kernel/chio-kernel/src/admission_operation.rs305–312rust
pub enum AdmissionDispatchState {
    NotCommitted,
    CapturePending,
    Committed,
    Finalizing,
    Terminal,
    NotApplicable,
}

AdmissionDispatchState::Committed is a durable authority transition; the provider's effect may or may not have completed. Finalizing describes processing around a retained operation, before the client has its response. Join these states to the outcome and release records before you display completion.

When a client reconnects after a timeout, query the original operation through the host's authenticated interface and keep NotApplicable, CapturePending, Committed, and Terminal distinct. Collapsing them into one retryable error removes what recovery needs to avoid a duplicate effect.

Read more in Recover an operation, which exercises both sides of capture, including the transaction-rollback cases and the races around handoff.

Next: decide what to do when the reply never arrives