Chio/Docs
LOGIN · JOIN

Workflow

Bind a multi-step skill to one grant, check each step against it, and verify the signed receipt that records the trace.

Reference chio@5f63e6e

On this page

A skill is an ordered sequence of tool calls that runs under one grant and ends in one signed receipt.

The skill developer declares the steps, and the data each one hands to the next, in a SkillManifest. A SkillGrant bounds which steps may run, in what order, how many times, for how much, and for how long. The host asks the WorkflowAuthority to check each step before it runs the tool through the kernel, reports the outcome and cost afterward, and receives a WorkflowReceipt that signs the whole trace.

Synopsis

A skill grant names one skill version, lists the "server_id:tool_name" steps it authorizes in declared order, and sets the envelope every execution runs inside. This grant, from section 7 of the specification, authorizes the two steps of a search-and-summarize skill for at most ten executions under a 1,000-unit USD envelope:

spec/WORKFLOW.md367-378yaml
# Skill Grant
schema: chio.skill-grant.v1
skill_id: search-and-summarize
skill_version: "1.0.0"
authorized_steps:
  - search-srv:search
  - llm-srv:summarize
budget_envelope:
  units: 1000
  currency: USD
max_executions: 10
strict_ordering: true

Skill model

Four types carry a skill from declaration to evidence. Each tool call still passes the kernel and leaves its own receipt; the workflow receipt binds those calls into one signed trace.

Skill executionChoose an outcome.
invokereceipt id, costsignsσauthorityKkernelr0search-srv:searchr1llm-srv:summarizebeginvalidate_steprecord_step$0.50validate_steprecord_step$1.00finalizeρWorkflowReceiptstatus: completedinvokereceipt id, costsignsσauthorityKkernelr0search-srv:searchr1llm-srv:summarizebeginvalidate_steprecord_step$0.50validate_steprecord_step$1.00finalizeρWorkflowReceiptstatus: completed

finalize signs the trace the host recorded

begin checks the grant against the manifest. Before each step, validate_step checks scope, ordering, and elapsed time; the host runs the tool through the kernel and passes record_step the outcome, cost, and tool receipt id. finalize signs one WorkflowReceipt whose outcome is completed. WorkflowAuthority lifecycle.

One execution of the search-and-summarize skill from the specification’s example: six authority calls, two governed tool calls, and the signed workflow receipt, with the example’s step costs. Durations, the budget envelope, and vendor co-signatures are left out.
  • SkillGrant. Extends the capability model to ordered tool sequences, with a budget envelope and execution limits.
  • SkillManifest. Declares the tool dependencies, the I/O contracts between steps, and the budget requirements.
  • WorkflowReceipt. Records the execution trace in one signed receipt.
  • WorkflowAuthority. Checks scope, ordering, and elapsed time before a step, then accounts for the step's reported cost and outcome afterward.

Workflow objects

The schema id and top-level shape of each object. The Registry column reads spec/schemas/registry.json.

ObjectSchema idTop-level shapeRegistry
SkillGrant
chio.skill-grant.v1
schema, skill_id, skill_version, authorized_steps[], max_executions?, budget_envelope?, max_duration_secs?, strict_ordering
not listed
SkillManifest
chio.skill-manifest.v1
schema, skill_id, version, name, description?, steps: SkillStep[], budget_envelope?, max_duration_secs?, author?
not listed
WorkflowReceipt
chio.workflow-receipt.v1
the WorkflowReceiptBody fields, then signature and vendor_signatures[]
not listed
WorkflowAuthority
none
begin, validate_step, record_step, finalize
none

SkillGrant

A SkillGrant authorizes an agent to execute a named skill. Where a tool grant authorizes one call, a skill grant binds the whole tool sequence under one authorization and one shared budget envelope.

Schema

FieldTypeRequiredDefaultDescription
schema
string
yes
none
MUST be "chio.skill-grant.v1"
skill_id
string
yes
none
Unique skill identifier
skill_version
string
yes
none
Version of the skill manifest this grant authorizes
authorized_steps
string[]
yes
none
Tool steps in declared order; format "server_id:tool_name"
max_executions
u32
no
null (unlimited)
Maximum number of complete skill executions
budget_envelope
MonetaryAmount
no
null
Budget for the entire execution
max_duration_secs
u64
no
null
Maximum wall-clock seconds per execution
strict_ordering
bool
no
true
Whether steps MUST execute in declared order

The Rust struct is SkillGrant in grant.rs. The optional fields are omitted from the JSON when absent, and a missing strict_ordering deserializes as true.

Step authorization

A step is authorized when "server_id:tool_name" appears in the authorized_steps list. Invocations of tools not in the list MUST be rejected. SkillGrant::authorizes_step makes this check.

Ordering modes

When strict_ordering is true (the default), each step MUST execute at index equal to the number of previously completed steps. A step submitted out of order MUST be rejected with StepOutOfOrder.

When strict_ordering is false (relaxed mode), steps may execute in any order. All steps MUST still be in the authorized_steps list. SkillGrant::is_step_in_order returns true for every index in relaxed mode.

SkillManifest

A SkillManifest is authored by the skill developer and declares the full execution plan for a skill.

Schema

FieldTypeRequiredDescription
schema
string
yes
MUST be "chio.skill-manifest.v1"
skill_id
string
yes
Unique skill identifier
version
string
yes
Semantic version
name
string
yes
Human-readable name
description
string
no
Human-readable description
steps
SkillStep[]
yes
Ordered steps in the skill
budget_envelope
MonetaryAmount
no
Budget for a single execution
max_duration_secs
u64
no
Maximum wall-clock seconds
author
string
no
Author identifier

SkillStep

FieldTypeRequiredDefaultDescription
index
usize
yes
none
Step index (0-based)
server_id
string
yes
none
Tool server hosting this step's tool
tool_name
string
yes
none
Tool to invoke
label
string
no
null
Human-readable step label
input_contract
IoContract
no
null
Input data contract
output_contract
IoContract
no
null
Output data contract
budget_limit
MonetaryAmount
no
null
Per-step budget limit
retryable
bool
no
false
Whether this step can be retried
max_retries
u32
no
null
Maximum retries, read when retryable is true

IoContract

An IoContract describes the data that flows between steps. Each list field deserializes as empty when absent.

FieldTypeDescription
required_fields
string[]
Field names the step requires (inputs) or guarantees (outputs)
produced_fields
string[]
Field names this step produces
optional_fields
string[]
Optional field names
json_schema
JSON
Optional JSON Schema for the data structure

I/O contract validation

Implementations MUST validate that I/O contracts form a consistent data flow. SkillManifest::validate_io_contracts walks the steps in order and applies these rules:

  • For each step after the first, every field in input_contract.required_fields MUST appear in the output_contract.produced_fields of some preceding step.
  • The first step's input requirements come from the caller; validation leaves them unchecked.
  • A violation reports the step index, the tool name, and the missing field name.

Tool dependencies

The manifest's tool dependencies are the "server_id:tool_name" strings derived from its steps, returned by SkillManifest::tool_dependencies. begin checks every manifest step against the grant before an execution starts.

WorkflowReceipt

A WorkflowReceipt records one skill execution in one signed receipt.

WorkflowReceiptBody

FieldTypeDescription
id
string
Unique receipt ID; the execution id begin minted
schema
string
MUST be "chio.workflow-receipt.v1"
started_at
u64
Unix timestamp when execution started
completed_at
u64
Unix timestamp when execution completed
skill_id
string
Skill ID from the manifest
skill_version
string
Skill version from the manifest
agent_id
string
Agent that executed the workflow
session_id
string
Session binding; omitted when null
capability_id
string
Capability that authorized the workflow
outcome
WorkflowOutcome
Overall outcome
steps
StepRecord[]
Per-step execution records
total_cost
MonetaryAmount
Total cost; omitted when null
duration_ms
u64
Wall-clock duration in milliseconds
kernel_key
PublicKey
Kernel public key

chio.workflow-receipt.v1 is the only workflow receipt schema a verifier accepts. Verifiers MUST reject unknown schema values fail-closed: WorkflowReceipt::verify returns false for any other schema value before it checks the signature.

Signed WorkflowReceipt

The signed WorkflowReceipt inlines every field of WorkflowReceiptBody and adds a signature field, an Ed25519 signature over the canonical JSON of the body. The signature MUST be computed over canonical_json_bytes(body) using RFC 8785 canonical JSON; the crate signs through Keypair::sign_canonical.

The receipt MAY also carry vendor_signatures, a list of WorkflowVendorSignature entries with the camel-case fields vendorId, publicKey, and signature; the list is omitted when empty. Each vendor co-signature signs the same canonical workflow body as the kernel signature. WorkflowReceipt::verify_vendor_signatures takes the required vendor signers and, per signer and in this order, rejects a missing vendor signature, a public key that differs from the required one, and a signature that fails verification.

WorkflowOutcome

The enum serializes with a status tag in snake case; the variant fields sit beside it.

VariantWire statusFieldsDescription
Completed
completed
none
All steps completed successfully
Denied
denied
reason
Workflow denied before execution started
StepFailed
step_failed
step_index, reason
A step failed, halting the workflow
BudgetExceeded
budget_exceeded
limit_units, spent_units, currency
Budget envelope exceeded
TimedOut
timed_out
limit_secs, elapsed_secs
Time limit exceeded
Cancelled
cancelled
reason
Canceled by agent or operator

StepRecord

FieldTypeDescription
step_index
usize
Step index in the manifest
server_id
string
Tool server
tool_name
string
Tool name
allowed
bool
Whether the step was authorized to execute; record_step sets it for a success or failed outcome
tool_receipt_id
string
Receipt ID for the underlying governed call (nullable)
outcome
StepOutcome
Step-level outcome
duration_ms
u64
Step duration in milliseconds
cost
MonetaryAmount
Cost attributed to this step (nullable)
output_hash
string
SHA-256 hash of step output (nullable)
bilateral_dsse_sha256
string
SHA-256 hash of the strict bilateral DSSE envelope for this step (optional)
governance_receipt_id
string
Governance receipt id authorizing this step (optional)
parent_receipt_sha256
string
SHA-256 hash of the previous step receipt in the workflow chain (optional)
consistency_anchor
string
Per-step consistency anchor value (optional)
destructive
bool
Destructive-action marker (optional)

Every optional field is omitted from the JSON when absent. record_step fills tool_receipt_id, cost, and output_hash from its input and leaves the remaining optional fields unset. The specification requires a destructive step to be backed by a live capability lease and a matching governance receipt when the verifier's action-class policy marks the step as receipt-backed.

StepOutcome

StepOutcome serializes as one of four snake-case values:

  • success: the step completed successfully.
  • denied: policy denied the step.
  • failed: the step failed during execution.
  • skipped: the workflow aborted before reaching the step.

Signature verification

WorkflowReceipt::verify reconstructs the WorkflowReceiptBody from the receipt fields and verifies the Ed25519 signature over its canonical JSON serialization using the embedded kernel_key. A tampered receipt (any field modified after signing) MUST fail verification.

WorkflowAuthority lifecycle

The host drives the lifecycle: begin once, validate before each tool invocation, record each outcome, and finalize the trace. The authority checks and records. Dispatching tools, authenticating the enclosing capability token, and enforcing a step's I/O schema belong to the host's integration and the tool's kernel execution path.

The WorkflowAuthority holds the kernel signing key, counts the executions it has started, and keeps a per-capability reservation table for grants that carry max_executions. These counters are local to the authority instance and are lost when it is recreated. The signatures below are quoted from the specification; the numbered behavior is the crate's.

begin

Signaturerust
begin(manifest, grant, agent_id, capability_id, session_id)
  -> Result<WorkflowExecution, WorkflowError>

Preconditions, checked in this order:

  1. grant.skill_id == manifest.skill_id and grant.skill_version == manifest.version. Fail: UnauthorizedSkill.
  2. Every step in the manifest MUST be authorized by the grant: grant.authorized_steps contains "step.server_id:step.tool_name" for each step. Fail: UnauthorizedStep.
  3. If grant.max_executions is set, the authority reserves one execution for the capability_id; completed plus in-flight executions for that capability MUST be below the limit. Fail: ExecutionLimitReached.

On success the authority increments its execution count and returns a WorkflowExecution with:

  • Budget limit from grant.budget_envelope or manifest.budget_envelope (grant takes precedence).
  • Time limit from grant.max_duration_secs or manifest.max_duration_secs (grant takes precedence).
  • An id of the form wf-{started_at}-{sequence}-{agent_id}, where the sequence is a per-authority counter so two executions begun in the same second get distinct ids.
  • active set to true.
  • Empty step_records and zero budget_spent.

validate_step

Signaturerust
validate_step(execution, step, grant) -> Result<(), WorkflowError>

Preconditions, checked in order:

  1. execution.active MUST be true. Fail: InvalidState.
  2. The step MUST be authorized by the grant. Fail: UnauthorizedStep.
  3. If strict ordering is enabled, step.index == execution.completed_steps(), where completed_steps counts the records whose outcome is success. Fail: StepOutOfOrder.
  4. If a time limit is set, elapsed time MUST be less than the limit. Fail: TimeLimitExceeded.

This check does not reserve money for the upcoming call. Budget checks run when the host records the step’s cost. For a spending ceiling that must prevent dispatch, also use the kernel’s budget admission.

record_step

Signaturerust
record_step(execution, step, StepExecutionRecordInput {
  outcome, duration_ms, cost, tool_receipt_id, output_hash
})
  -> Result<(), WorkflowError>

The host supplies a StepExecutionRecordInput. The authority records the reported tool receipt id and output hash without checking them against the tool receipt. Behavior:

  1. If execution.active is false, return InvalidState.
  2. Evaluate, without returning yet: whether the time limit has elapsed; whether cost.currency differs from the currency of step.budget_limit or of the execution budget; and whether cost.units exceeds step.budget_limit.units.
  3. Unless the currency mismatched, add cost.units (if present) to execution.budget_spent using saturating addition.
  4. Append a StepRecord to execution.step_records. The record is always appended, even when a check below fails, so the finalized receipt carries the offending step.
  5. Return the first failure that applies, each of which sets active to false: a currency mismatch returns BudgetCurrencyMismatch and records a Denied outcome; a per-step limit breach returns BudgetExceeded; budget_spent > budget_limit.units returns BudgetExceeded; an elapsed time limit returns TimeLimitExceeded and records a TimedOut outcome.
  6. If outcome is failed or denied, set active to false.

finalize

Signaturerust
finalize(execution) -> Result<WorkflowReceipt, WorkflowError>

Finalization signs the records supplied to this authority. A Completed outcome means the recorded trace has no failure selected by the rules below. The authority keeps no copy of the manifest's step count, so to judge completeness, compare the recorded steps with the manifest and verify each linked tool receipt.

Behavior, in the order the specification numbers:

  1. Set execution.active to false.
  2. Determine the WorkflowOutcome, in this precedence:
    • If any step record has outcome failed or denied, the outcome is StepFailed with that step's index.
    • Otherwise a terminal outcome that record_step recorded (Denied, BudgetExceeded, or TimedOut) stands; a time limit that elapsed by finalize time also yields TimedOut.
    • Otherwise, if budget_spent > budget_limit.units, the outcome is BudgetExceeded.
    • Otherwise the outcome is Completed.
  3. Construct WorkflowReceiptBody with all execution data: completed_at and duration_ms from the clock, total_cost as budget_spent in the envelope's currency when spend is positive and an envelope exists, and kernel_key as the authority's public key.
  4. Sign the body: keypair.sign_canonical(body). Fail: SigningFailed.
  5. For a grant with max_executions, move the capability's reservation from in-flight to completed. begin already incremented the authority's execution count.
  6. Return the signed WorkflowReceipt.

WorkflowError

ErrorFieldsDescription
UnauthorizedSkill
skill_id, version
The grant names a different skill or version
UnauthorizedStep
step_index, server, tool
Step missing from the grant's authorized list
StepOutOfOrder
step_index, expected
Step submitted out of sequence
BudgetExceeded
limit_units, spent_units, currency
Budget envelope or per-step limit exceeded
BudgetCurrencyMismatch
expected_currency, actual_currency
A step reported cost in a currency other than the declared budget's
TimeLimitExceeded
elapsed_secs, limit_secs
Time limit exceeded
ExecutionLimitReached
limit
Maximum executions reached
InvalidState
message
Workflow is in the wrong state for the call
SigningFailed
message
Receipt signing error

Example

Section 7 of spec/WORKFLOW.md pairs the grant in the synopsis with this manifest for the two-step search-and-summarize skill. The second step requires the results field the first step produces, so the manifest passes I/O contract validation:

spec/WORKFLOW.md341-363yaml
# Skill Manifest
schema: chio.skill-manifest.v1
skill_id: search-and-summarize
version: "1.0.0"
name: Search and Summarize
steps:
  - index: 0
    server_id: search-srv
    tool_name: search
    label: Search
    output_contract:
      produced_fields: [results]
  - index: 1
    server_id: llm-srv
    tool_name: summarize
    label: Summarize
    input_contract:
      required_fields: [results]
    output_contract:
      produced_fields: [summary]
budget_envelope:
  units: 1000
  currency: USD

Execution flow, in the order the specification lists it:

  1. authority.begin(manifest, grant, agent, capability, session): validates that the grant matches the manifest and creates the execution.
  2. authority.validate_step(execution, step_0, grant): checks authorization, ordering, and time.
  3. Invoke search-srv:search and collect the result.
  4. authority.record_step(execution, step_0, Success, 100ms, $0.50): records the cost and checks the budget.
  5. authority.validate_step(execution, step_1, grant): checks authorization, ordering, and time.
  6. Invoke llm-srv:summarize and collect the result.
  7. authority.record_step(execution, step_1, Success, 200ms, $1.00): records the cost and checks the budget.
  8. authority.finalize(execution): signs the receipt and completes the execution reservation.

Schema registration

The crate carries chio.skill-grant.v1, chio.skill-manifest.v1, chio.workflow-receipt.v1 as constants in the module that defines each object, outside spec/schemas/registry.json.

The registry's chio-workflow family holds the preflight plan the CLI reads and the report it prints:

Schema idKindFile
chio.workflow.preflight-plan.v1
workflow_preflight_plan
spec/schemas/chio-workflow/v1/preflight-plan.schema.json
chio.workflow.preflight-report.v1
workflow_preflight_report
spec/schemas/chio-workflow/v1/preflight-report.schema.json

CLI command

The help text for chio workflow reads: Validate read-only workflow planning evidence before dispatch. The command plans and validates: it runs no skill, spends no budget, and signs no workflow receipt.

An accepted plan prints a chio.workflow.preflight-report.v1 report with verdict accepted and evidence class planning, and the command exits 0:

reference · workflow-preflighttranscript
$ chio workflow preflight --plan ./valid-plan.json | jq .
{
  "schema": "chio.workflow.preflight-report.v1",
  "id": "workflow-preflight-report-workflow-preflight-valid-child-scope",
  "issued_at": "2026-06-10T00:00:00Z",
  "plan_id": "workflow-preflight-valid-child-scope",
  "verdict": "accepted",
  "evidence_class": "planning",
  "verified_claims": [
    "claim.workflow.preflight_child_scope_bounded",
    "claim.workflow.preflight_planning_only"
  ],
  "rejected_checks": [],
  "live_authority_claims": []
}
exit 0

A plan whose child task requests an action outside the parent scope is rejected: the command names the rejected check on stderr and exits 1:

reference · workflow-preflight-denytranscript
$ chio workflow preflight --plan ./broader-plan.json
error[CHIO-KERNEL-OUT-OF-SCOPE-TOOL]: workflow preflight rejected the plan: child task task-child-payment requests payment.capture, which its parent's scope does not grant
  help: narrow task-child-payment to actions its parent grants, or widen the parent's scope in the plan
  code: urn:chio:error:capability:scope-exceeded
  docs: https://chio.computer/docs/reference/errors#error-capability-scope-exceeded
exit 1
SubcommandHelp textRequired option
chio workflow preflight
Validate a read-only workflow preflight plan
--plan <PATH>: Path to a chio.workflow.preflight-plan.v1 JSON artifact

The full option list is in the CLI reference.

Source

The normative text is spec/WORKFLOW.md in the Chio repository, version 1.0, dated 2026-04-14, with status Normative. The keywords MUST, SHOULD, and MAY are normative.

Lifecycle behavior is read from crates/platform/chio-workflow/src/authority.rs, and the object shapes from grant.rs, manifest.rs, and receipt.rs in the same crate. Where the specification and the crate differ, each entry follows the crate and names the function.

Next: read the receipt each step links