Chio/Docs
LOGIN · JOIN

Authorize an ordered tool workflow

Read an order, quote it, and publish the approved price in a fixed order, with a receipt for every step.

Guide chio@5f63e6e

On this page

A three-step price change in which every step passes two checks: the workflow grant's order, then the kernel's check of that tool's own capability. The last step also waits for Mara Okafor.

This chapter extends the approval desk from Require human approval with a host dispatch loop: read order-77, quote its SKU at a discount, and publish the new price. A skill manifest declares the steps, a workflow grant authorizes their sequence, and publish_price keeps Mara's gate. The host signs one workflow receipt that links every tool receipt.

Start
Workflow outcomesChoose an outcome.

Three admitted steps and one workflow receipt

read_order, quote, and publish_price each pass the workflow authority and the kernel. Each step record links its tool receipt and output hash into one signed workflow receipt, and the host prints Workflow complete: true. Workflow reference.

One execution of the reviewed-price-change workflow, drawn at its last step, publish_price. read_order and quote run first on every path but the out-of-order attempt; their receipts and output hashes are summarized in the panel.

Two different refusals are drawn. The workflow authority stops a skipped step before any tool runs, so it leaves no receipt; a rejected review reaches the kernel at the last step and leaves a deny receipt beside the two steps that already ran.

1. Run the price-change workflow

Starts from chio new pricing-desk --example pricing-desk, or the same project as a download. The desk is Larkfield's pricing functions behind small hosts that embed the kernel, so it needs no separate Chio process. Cargo selects Rust 1.94.1 from the project's toolchain file and fetches the Chio crates its manifest pins; the first build takes a few minutes.

Terminal · pricing-deskbash
chio new pricing-desk --example pricing-desk && cd pricing-desk
cargo run --locked --bin quote-workflow -- order-77 15
Output
read_order: "Allow" receipt="a72918e334be333aa80ca859f4aef505732713cb4a17630139e0281c9f2a0d67"
quote: "Allow" receipt="e44c32d2a2ff4292591373fd929fa333b5f315b08f8d04bd04715bad541c97e9"
Review the exact price change:
{
  "change_id": "2272eec2-8d6b-431d-a2b8-1e16f9656d40",
  "customer": "order-77",
  "percent": 15.0,
  "price": "49.30",
  "sku": "sku-204"
}
Type approve to publish it, or anything else to reject: approve
publish_price: "Allow" receipt="d4ab34b1a8fcfe1828413df98cf3b3f23a5fb4480635a766863fc230f28105fd"
Workflow complete: true. Signed receipt: state/approvals/wf-1791787902-0-2bf083f9b8db1823a7d842e0a6edf934d9e046a7021f2127102f5b72f1313285.json

The host keeps its state in state/approvals and signs receipts into state/approvals/receipts.db.

Before you begin

Use a Unix-compatible system with durable file locking, Rust through rustup, and curl and jq for the client commands. Keep the selected port free. The project README lists every endpoint, credential file, and supported persistence setting.

Stop the browser desk first: the workflow uses its admission authority, agent capability, approval store, receipts, and price-change directory. read_order finds order-77 (one sku-204, paid USD 58.00), quote prices that SKU at 15 percent off, and the console prints the exact change and asks you, as Mara, to type approve. Any other answer rejects it.

You should see three Allow lines with distinct receipt IDs, then Workflow complete: true and the path of the signed workflow receipt. The host reports completion only when the successful steps equal the manifest’s declared step count.

2. Read the manifest and grant

Print the files the host wrote into state/approvals.

Terminal · pricing-deskbash
jq '{skill_id, skill_version, authorized_steps, strict_ordering, max_executions, max_duration_secs}' \
  state/approvals/workflow-grant.json
jq -c '.steps[] | {tool_name, needs: .input_contract.required_fields, makes: .output_contract.produced_fields}' \
  state/approvals/workflow-manifest.json
Output
{
  "skill_id": "reviewed-price-change",
  "skill_version": "1.0.0",
  "authorized_steps": [
    "pricing:read_order",
    "pricing:quote",
    "pricing:publish_price"
  ],
  "strict_ordering": true,
  "max_executions": 1,
  "max_duration_secs": 900
}
{"tool_name":"read_order","needs":null,"makes":["sku"]}
{"tool_name":"quote","needs":["sku"],"makes":["price"]}
{"tool_name":"publish_price","needs":["sku","price"],"makes":[]}

You should see three authorized steps, strict ordering, one execution, and a 900-second limit. The manifest's contracts say what each step needs from the steps before it: quote needs the SKU read_order found, and publish_price needs the price quote computed. The trusted host supplies this grant, so a caller cannot edit it to gain tool authority, and every dispatch still needs the agent's signed capability.

3. Try to publish a price before reading the order

Terminal · pricing-deskbash
cargo run --locked --bin quote-workflow -- order-77 15 --try-publish-first
Output
REFUSED before dispatch: step 2 is out of order (expected step 0)

You should see the workflow authority refuse step 2 while step 0 is expected. The command stops before any tool runs and before any price moves. This is a workflow admission error, so it has no tool receipt and no finalized workflow receipt.

4. Reject the review and keep the steps that ran

Run the step 1 command again and answer anything but approve at the prompt.

Terminal · pricing-deskbash
cargo run --locked --bin quote-workflow -- order-77 15
Output
read_order: "Allow" receipt="e2a39ff14796936683954ea0fb83bb2a168fce2d19e279a8351bc4b472530429"
quote: "Allow" receipt="2d2a3c7eb4ab6a4c78f9f745da8f40e3a60d55d7de271806e5341906884e6533"
Review the exact price change:
{
  "change_id": "1b4e9faa-f55a-4aa3-8a79-77bcf6370c0c",
  "customer": "order-77",
  "percent": 15.0,
  "price": "49.30",
  "sku": "sku-204"
}
Type approve to publish it, or anything else to reject: no
publish_price: "Deny" receipt="8272c88551a824b4309b845569bb4f5b00d88727f1422b707869ea0d9d89894d"
Workflow complete: false. Signed receipt: state/approvals/wf-1791787904-0-2bf083f9b8db1823a7d842e0a6edf934d9e046a7021f2127102f5b72f1313285.json

You should see read_order and quote admitted, publish_price denied, and Workflow complete: false. The workflow receipt records step_failed at step 2, and no price changes. Rejecting the last step erases nothing: an incomplete workflow can hold completed steps, and their receipts stay useful evidence.

5. Match every step to its tool receipt

Set WORKFLOW_RECEIPT to the path step 1 printed, then list the receipt's steps.

Terminal · pricing-deskbash
WORKFLOW_RECEIPT=state/approvals/wf-1791787902-0-2bf083f9b8db1823a7d842e0a6edf934d9e046a7021f2127102f5b72f1313285.json
jq -r '.schema + " " + .outcome.status,
  (.steps[] | "\(.step_index) \(.tool_name) \(.outcome) \(.tool_receipt_id[0:16]) \(.output_hash[0:16])")' \
  "$WORKFLOW_RECEIPT"
Output
chio.workflow-receipt.v1 completed
0 read_order success a72918e334be333a e0ace1cc1e2d6a3a
1 quote success e44c32d2a2ff4292 852fa409dbeba022
2 publish_price success d4ab34b1a8fcfe18 5bdd9d534c4d47b3

You should see each step’s tool receipt ID beside the hash of its actual output. Find every tool receipt ID in state/approvals/receipts.db and compare the file under price-changes/ with the change shown at review. An output hash binds a step to particular bytes; whether 15 percent off was the right price is Mara's judgment.

The host checks the workflow signature when it finalizes. An independent verifier also needs the expected workflow key from workflow-public-key.txt, chosen separately from any key the receipt presents. The workflow key and the kernel key carry different responsibilities even when one application operates both. The approved, rejected, and out-of-order captures hold each run’s records.

The host’s dispatch loop
src/workflow.rs · inside the dispatch looprust
authority.validate_step(&execution, step, &grant)?;
// read_order and quote go straight to the kernel; publish_price also waits for Mara.
let request = request(&capability, "pricing", &step.tool_name, arguments);
let result = response(desk.kernel.evaluate_tool_call(&request).await?)?;
let allowed = result["verdict"] == "Allow" && !result["output"].is_null();
authority.record_step(&mut execution, step, StepExecutionRecordInput {
    outcome: if allowed { StepOutcome::Success } else { StepOutcome::Denied },
    tool_receipt_id: result["receipt_id"].as_str().map(str::to_owned),
    duration_ms: started.elapsed().as_millis() as u64,
    cost: None,
    output_hash: Some(chio_core::sha256_hex(&chio_core::canonical_json_bytes(
        &result["output"],
    )?)),
})?;
if !allowed {
    break;
}

Each step is validated against the grant, evaluated by the kernel, and recorded with its receipt ID and output hash. A refused step stops the loop. The complete source adds Mara's review branch for publish_price.

Complete source: src/workflow.rs, src/approvals.rs, src/lib.rs, all in the project download.

A stopped host keeps every receipt it signed

The workflow’s step cursor and its one-execution limit live in the host process. A host that stops between steps keeps the tool receipts and application records already written; inspect the completed effects, then start a new execution under the grant. A stop during publish_price leaves the desk's proposal, decision, dispatch marker, and any price change, and the reconciliation procedure applies before new work. Steps that must resume under their original identity after a restart run as named calls on the process host.

Next: the workflow contract

Authorize an ordered tool workflow · Chio Docs