Chio/Docs
LOGIN · JOIN

ReferenceSpec

Workflow

Skill grants, skill manifests, and signed workflow receipts for WORKFLOW.md version 1.0, with the WorkflowAuthority lifecycle the chio-workflow crate implements.

Source

This page normatively reflects spec/WORKFLOW.md in the chio repository. Status: Normative. Version 1.0, dated 2026-04-14. 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, the page describes the crate and names the function.


Synopsis

The schema ids and the lifecycle type; the last column reads spec/schemas/registry.json at the pin.

ObjectSchema idTop-level shapeRegistry
SkillGrantchio.skill-grant.v1schema, skill_id, skill_version, authorized_steps[], max_executions?, budget_envelope?, max_duration_secs?, strict_orderingnot listed
SkillManifestchio.skill-manifest.v1schema, skill_id, version, name, description?, steps: SkillStep[], budget_envelope?, max_duration_secs?, author?not listed
WorkflowReceiptchio.workflow-receipt.v1the WorkflowReceiptBody fields, then signature and vendor_signatures[]not listed
WorkflowAuthoritynonebegin, validate_step, record_step, finalizenone

Purpose

A skill is an ordered sequence of tool invocations that composes multiple tools into a single authorized unit of work. The crate defines these types:

  • 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. Records the execution trace in one signed receipt.
  • 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

FieldTypeRequiredDefaultDescription
schemastringyesnoneMUST be "chio.skill-grant.v1"
skill_idstringyesnoneUnique skill identifier
skill_versionstringyesnoneVersion of the skill manifest this grant authorizes
authorized_stepsstring[]yesnoneTool steps in declared order; format "server_id:tool_name"
max_executionsu32nonull (unlimited)Maximum number of complete skill executions
budget_envelopeMonetaryAmountnonullBudget for the entire execution
max_duration_secsu64nonullMaximum wall-clock seconds per execution
strict_orderingboolnotrueWhether 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 if "server_id:tool_name" appears in the authorized_steps list. Invocations of tools not in the list MUST be rejected. The crate makes this check in SkillGrant::authorizes_step.

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
schemastringyesMUST be "chio.skill-manifest.v1"
skill_idstringyesUnique skill identifier
versionstringyesSemantic version
namestringyesHuman-readable name
descriptionstringnoHuman-readable description
stepsSkillStep[]yesOrdered steps in the skill
budget_envelopeMonetaryAmountnoBudget for a single execution
max_duration_secsu64noMaximum wall-clock seconds
authorstringnoAuthor identifier

SkillStep

FieldTypeRequiredDefaultDescription
indexusizeyesnoneStep index (0-based)
server_idstringyesnoneTool server hosting this step's tool
tool_namestringyesnoneTool to invoke
labelstringnonullHuman-readable step label
input_contractIoContractnonullInput data contract
output_contractIoContractnonullOutput data contract
budget_limitMonetaryAmountnonullPer-step budget limit
retryableboolnofalseWhether this step can be retried
max_retriesu32nonullMaximum retries (only relevant when retryable is true)

IoContract

The IoContract type describes data flow between steps. Each list field deserializes as empty when absent.

FieldTypeDescription
required_fieldsstring[]Field names required by the step (inputs) or guaranteed (outputs)
produced_fieldsstring[]Field names this step produces
optional_fieldsstring[]Optional field names
json_schemaJSONOptional 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 and are not validated against the manifest.
  • A violation is reported with the step index, the tool name, and the missing field name.

Tool dependencies

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


WorkflowReceipt

A WorkflowReceipt records a skill execution in one signed receipt.

WorkflowReceiptBody

FieldTypeDescription
idstringUnique receipt ID; the execution id begin minted
schemastringMUST be "chio.workflow-receipt.v1"
started_atu64Unix timestamp when execution started
completed_atu64Unix timestamp when execution completed
skill_idstringSkill ID from the manifest
skill_versionstringSkill version from the manifest
agent_idstringAgent that executed the workflow
session_idstringSession binding; omitted when null
capability_idstringCapability that authorized the workflow
outcomeWorkflowOutcomeOverall outcome
stepsStepRecord[]Per-step execution records
total_costMonetaryAmountTotal cost; omitted when null
duration_msu64Wall-clock duration in milliseconds
kernel_keyPublicKeyKernel 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 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; 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 rejects a missing vendor signature, a public key that differs from the required one, and a signature that fails verification, in that order per signer.

WorkflowOutcome

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

VariantWire statusFieldsDescription
CompletedcompletednoneAll steps completed successfully
DenieddeniedreasonWorkflow denied before execution started
StepFailedstep_failedstep_index, reasonA step failed, halting the workflow
BudgetExceededbudget_exceededlimit_units, spent_units, currencyBudget envelope exceeded
TimedOuttimed_outlimit_secs, elapsed_secsTime limit exceeded
CancelledcancelledreasonCanceled by agent or operator

StepRecord

FieldTypeDescription
step_indexusizeStep index in the manifest
server_idstringTool server
tool_namestringTool name
allowedboolWhether the step was authorized to execute; record_step sets it for a success or failed outcome
tool_receipt_idstringReceipt ID for the underlying governed call (nullable)
outcomeStepOutcomeStep-level outcome
duration_msu64Step duration in milliseconds
costMonetaryAmountCost attributed to this step (nullable)
output_hashstringSHA-256 hash of step output (nullable)
bilateral_dsse_sha256stringSHA-256 hash of the strict bilateral DSSE envelope for this step (optional)
governance_receipt_idstringGovernance receipt id authorizing this step (optional)
parent_receipt_sha256stringSHA-256 hash of the previous step receipt in the workflow chain (optional)
consistency_anchorstringPer-step consistency anchor value (optional)
destructiveboolDestructive-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 that a destructive step 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

ValueDescription
successStep completed successfully
deniedStep denied by policy
failedStep failed during execution
skippedStep skipped (workflow aborted before reaching it)

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 WorkflowAuthority manages the lifecycle of skill executions. It holds the kernel signing key, counts the executions it has started, and keeps a per-capability reservation table for grants that carry max_executions. The signatures below are quoted from the specification; the numbered behavior is the crate's.

begin

text
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

text
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.

record_step

text
record_step(execution, step, outcome, duration_ms, cost, tool_receipt_id, output_hash)
  -> Result<(), WorkflowError>

The crate passes the arguments after step as one StepExecutionRecordInput value. 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

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

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. The authority's execution count was already incremented by begin.
  6. Return the signed WorkflowReceipt.

WorkflowError

ErrorFieldsDescription
UnauthorizedSkillskill_id, versionGrant does not authorize the requested skill
UnauthorizedStepstep_index, server, toolStep not in the grant's authorized list
StepOutOfOrderstep_index, expectedStep submitted out of sequence
BudgetExceededlimit_units, spent_units, currencyBudget envelope or per-step limit exceeded
BudgetCurrencyMismatchexpected_currency, actual_currencyA step reported cost in a currency that does not match the declared budget
TimeLimitExceededelapsed_secs, limit_secsTime limit exceeded
ExecutionLimitReachedlimitMaximum executions reached
InvalidStatemessageWorkflow is not in the correct state
SigningFailedmessageReceipt signing error

Example

Section 7 of spec/WORKFLOW.md gives this manifest for a two-step search-and-summarize skill:

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

The same section pairs it with this grant, which authorizes both steps under one 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

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 specification defines chio.skill-grant.v1, chio.skill-manifest.v1, chio.workflow-receipt.v1, and spec/schemas/registry.json does not list them. The crate carries each id as a constant in the module that defines the object.

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

Schema idKindFile
chio.workflow.preflight-plan.v1workflow_preflight_planspec/schemas/chio-workflow/v1/preflight-plan.schema.json
chio.workflow.preflight-report.v1workflow_preflight_reportspec/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. It is a preflight and validation command. It does not run skills, spend budget, or emit signed workflow receipts.

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 [urn:chio:error:cli:other]: workflow preflight rejected: child task task-child-payment requested action payment.capture outside parent scope
context: {"domain":"cli","severity":"error","stability":"deprecated","string_code":"CHIO-CLI-OTHER"}
suggested fix: Preserve the original message and migrate the call site to a specific registry code when touched.
exit 1
SubcommandHelp textRequired option
chio workflow preflightValidate a read-only workflow preflight plan--plan <PATH>: Path to a chio.workflow.preflight-plan.v1 JSON artifact

The full option list is on the CLI reference.