Workflow
The skill and workflow authority system extends the Chio capability model with multi-step tool compositions, I/O contracts between steps, budget envelopes, and signed workflow receipts. Implementations MUST follow the grant, manifest, receipt, and authority lifecycle described here.
Source
This page normatively reflects spec/WORKFLOW.md in the chio repository. Status: Normative. Version 1.0.
Purpose
A skill is an ordered sequence of tool invocations that composes multiple tools into a single authorized unit of work. The workflow system provides four primitives:
- SkillGrant. Extends the capability model for ordered tool sequences with budget envelopes and execution limits.
- SkillManifest. Declares tool dependencies, I/O contracts between steps, and budget requirements.
- WorkflowReceipt. Captures the complete execution trace as a single signed artifact.
- WorkflowAuthority. Validates each step against declared scope, ordering, budget, and time constraints.
SkillGrant
A SkillGrant authorizes an agent to execute a named skill. Unlike individual tool grants, a skill grant binds an entire tool sequence under a single authorization with a shared budget envelope.
Schema
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
schema | string | yes | -- | MUST be "chio.skill-grant.v1" |
skill_id | string | yes | -- | Unique skill identifier |
skill_version | string | yes | -- | Version of the skill manifest this grant authorizes |
authorized_steps | string[] | yes | -- | 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 |
Step Authorization
A step is authorized if "server_id:tool_name" appears in the authorized_steps list. Invocations of tools not in the list MUST be rejected.
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.
SkillManifest
A SkillManifest is authored by the skill developer and declares the full execution plan for a skill.
Schema
| Field | Type | Required | Description |
|---|---|---|---|
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
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
index | usize | yes | -- | Step index (0-based) |
server_id | string | yes | -- | Tool server hosting this step's tool |
tool_name | string | yes | -- | 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 (only relevant when retryable is true) |
IoContract
The IoContract type describes data flow between steps.
| Field | Type | Description |
|---|---|---|
required_fields | string[] | Field names required by the step (inputs) or guaranteed (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:
- For each step after the first, every field in
input_contract.required_fieldsMUST appear in theoutput_contract.produced_fieldsof some preceding step. - The first step's input requirements come from the caller, not from preceding steps, and are therefore not validated against the manifest.
- Violations MUST be reported with the step index, tool name, and missing field name.
Tool Dependencies
The manifest's tool dependencies are the list of "server_id:tool_name" strings derived from each step. The workflow authority uses this list to verify the grant covers all required tools.
WorkflowReceipt
A WorkflowReceipt captures the complete execution of a skill as a single signed artifact.
WorkflowReceiptBody
| Field | Type | Description |
|---|---|---|
id | string | Unique receipt ID |
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 (nullable) |
capability_id | string | Capability that authorized the workflow |
outcome | WorkflowOutcome | Overall outcome |
steps | StepRecord[] | Per-step execution records |
total_cost | MonetaryAmount | Total cost (nullable) |
duration_ms | u64 | Wall-clock duration in milliseconds |
kernel_key | PublicKey | Kernel public key |
WorkflowReceipt (Signed)
The signed WorkflowReceipt inlines all fields from WorkflowReceiptBody and adds a signature field (Ed25519 signature over canonical JSON of the body). The signature MUST be computed over canonical_json_bytes(body) using RFC 8785 canonical JSON.
WorkflowOutcome
| Variant | Fields | Description |
|---|---|---|
Completed | -- | All steps completed successfully |
Denied | reason | Workflow denied before execution started |
StepFailed | step_index, reason | A step failed, halting the workflow |
BudgetExceeded | limit_units, spent_units, currency | Budget envelope exceeded |
TimedOut | limit_secs, elapsed_secs | Time limit exceeded |
Cancelled | reason | Cancelled by agent or operator |
StepRecord
| Field | Type | Description |
|---|---|---|
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 |
tool_receipt_id | string | Receipt ID for the underlying tool 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) |
StepOutcome
| Value | Description |
|---|---|
success | Step completed successfully |
denied | Step denied by policy |
failed | Step failed during execution |
skipped | Step skipped (workflow aborted before reaching it) |
Signature Verification
Verification 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 WorkflowAuthority manages the lifecycle of skill executions. It holds the kernel signing key and tracks execution counts for limit enforcement.
begin
begin(manifest, grant, agent_id, capability_id, session_id)
-> Result<WorkflowExecution, WorkflowError>Preconditions, all of which MUST be checked:
grant.skill_id == manifest.skill_idandgrant.skill_version == manifest.version. Fail:UnauthorizedSkill.- If
grant.max_executionsis set,execution_count < limit. Fail:ExecutionLimitReached. - Every step in the manifest MUST be authorized by the grant. For each step,
grant.authorized_stepsMUST contain"step.server_id:step.tool_name". Fail:UnauthorizedStep.
On success, returns a WorkflowExecution with:
- Budget limit from
grant.budget_envelopeormanifest.budget_envelope(grant takes precedence). - Time limit from
grant.max_duration_secsormanifest.max_duration_secs(grant takes precedence). activeset totrue.- Empty
step_recordsand zerobudget_spent.
validate_step
validate_step(execution, step, grant) -> Result<(), WorkflowError>Preconditions, checked in order:
execution.activeMUST betrue. Fail:InvalidState.- The step MUST be authorized by the grant. Fail:
UnauthorizedStep. - If strict ordering is enabled,
step.index == execution.completed_steps(). Fail:StepOutOfOrder. - If a time limit is set, elapsed time MUST be less than the limit. Fail:
TimeLimitExceeded.
record_step
record_step(execution, step, outcome, duration_ms, cost, tool_receipt_id, output_hash)
-> Result<(), WorkflowError>Behavior:
- If
execution.activeisfalse, returnInvalidState. - Add
cost.units(if present) toexecution.budget_spentusing saturating addition. - Append a
StepRecordtoexecution.step_records. The record is always appended, even if the budget is about to be exceeded, so the audit trail includes the offending step. - After recording, check the budget envelope. If
budget_spent > budget_limit.units, setactivetofalseand returnBudgetExceeded. - If
outcomeisFailedorDenied, setactivetofalse.
Audit ordering
finalize
finalize(execution) -> Result<WorkflowReceipt, WorkflowError>Behavior:
- Set
execution.activetofalse. - Determine the
WorkflowOutcomeby inspecting step records and budget. If any step hasoutcome == Failedoroutcome == Denied, the outcome isStepFailed. Ifbudget_spent > budget_limit.units, the outcome isBudgetExceeded. Otherwise the outcome isCompleted. - Construct
WorkflowReceiptBodywith all execution data. - Sign the body:
keypair.sign_canonical(body). - Increment the authority's
execution_count. - Return the signed
WorkflowReceipt.
WorkflowError
| Error | Fields | Description |
|---|---|---|
UnauthorizedSkill | skill_id, version | Grant does not authorize the requested skill |
UnauthorizedStep | step_index, server, tool | Step not in the grant's authorized list |
StepOutOfOrder | step_index, expected | Step submitted out of sequence |
BudgetExceeded | limit_units, spent_units, currency | Budget envelope exceeded |
TimeLimitExceeded | elapsed_secs, limit_secs | Time limit exceeded |
ExecutionLimitReached | limit | Maximum executions reached |
InvalidState | message | Workflow is not in the correct state |
SigningFailed | message | Receipt signing error |
Example
A two-step search-and-summarize skill:
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: USDschema: 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: trueExecution flow:
authority.begin(manifest, grant, agent, capability, session). Validates grant matches manifest, creates execution.authority.validate_step(execution, step_0, grant). Checks authorization, ordering, time.- Invoke
search-srv:search, collect result. authority.record_step(execution, step_0, Success, 100ms, $0.50). Records cost, checks budget.authority.validate_step(execution, step_1, grant).- Invoke
llm-srv:summarize, collect result. authority.record_step(execution, step_1, Success, 200ms, $1.00).authority.finalize(execution). Signs receipt, increments count.