Skip to main content

Contract YAML Reference

Every field in per-tool contracts, session.yaml, and workflow.yaml.


Per-tool contract

Each tool gets a YAML file named {tool_name}.yaml in your contracts directory.

Required fields

FieldTypeDescription
toolstringTool name (must match function name in tool definitions)
side_effect"read" | "write" | "destructive" | "admin" | "financial"Risk classification
evidence_class"local_transaction" | "ack_only" | "unverifiable"Evidence pattern (restrictions)
commit_requirement"acknowledged" | "none"When authoritative state advances
timeouts{ total_ms: number }Call timeout
retries{ max_attempts: number, retry_on: string[] }Retry policy
rate_limits{ on_429: { respect_retry_after: boolean, max_sleep_seconds: number } }Rate limit handling
assertions{ input_invariants: Invariant[], output_invariants: Invariant[] }Structural assertions
golden_casesGoldenCase[]Reference test cases
allowed_errorsAllowedError[]Expected error patterns

Optional fields

FieldTypeDescription
gate"allow" | "block"Override risk_defaults for this tool
transitions{ valid_in_phases: string[], advances_to: string }Phase restrictions
preconditionsPrecondition[]Cross-step ordering requirements
forbids_afterstring[]Tools blocked after this one executes
argument_value_invariantsValueInvariant[]Runtime argument value checks
response_format_invariantsResponseFormatInvariantsResponse envelope checks
execution_constraints{ arguments: Invariant[] }Pre-execution argument validation
policyPolicyBlockPer-tool authorization rules
bindsBindEntry[]Captures argument/output values into named session-scoped slots
schema_derivedbooleanEnable/disable auto-derived invariants from tool schema (default: true)
schema_derived_excludestring[]JSONPaths excluded from schema-derived invariant synthesis
checkpointCheckpointDefinitionHuman approval gate for high-stakes calls

evidence_class restrictions

ack_only cannot be used with high-risk side effects. The compiler rejects the combination with error code ACK_ONLY_ON_HIGH_RISK.

side_effectlocal_transactionack_onlyunverifiable
readOKOKOK
writeOKOKOK
destructiveOKBlockedOK
adminOKBlockedOK
financialOKBlockedOK

Use local_transaction (observable results) or unverifiable (fire-and-forget) for high-risk tools instead.


transitions

transitions:
valid_in_phases: [phase_a, phase_b] # Phases where this tool is visible
advances_to: phase_c # Phase after successful execution

preconditions

preconditions:
# Basic ordering
- requires_prior_tool: tool_name

# With output check
- requires_prior_tool: tool_name
with_output:
- path: "$.field"
equals: "value"

# Same-entity enforcement
- requires_prior_tool: tool_name
resource:
bind_from: arguments
path: "$.entity_id"
with_output:
- path: "$.approved"
equals: true

# Minimum step count
- requires_step_count:
gte: 3

argument_value_invariants

Runtime argument value checks support a subset of operators:

argument_value_invariants:
- path: "$.field_name"
exact_match: "string_value" # String exact match
- path: "$.field_name"
type: string # Type check
- path: "$.field_name"
regex: "^pattern$" # Regex match
- path: "$.field_name"
one_of: ["a", "b", "c"] # Enumeration
- path: "$.field_name"
gte: 0 # Greater than or equal
- path: "$.field_name"
lte: 10000 # Less than or equal

response_format_invariants

response_format_invariants:
finish_reason: "tool_calls" # Expected finish reason
content_when_tool_calls: "empty" # "empty" | "allowed"
tool_calls_present: true # Must have tool calls

execution_constraints

execution_constraints:
arguments:
- path: "$.file_path"
regex: "^/tmp/scratch/"
- path: "$.recursive"
equals: false

policy

policy:
allow:
- principal:
path: "$.department"
equals: "finance"
deny:
- principal:
path: "$.type"
equals: "readonly-auditor"

binds

binds:
- name: approved_shares
source: arguments # "arguments" (default) or "output"
path: $.shares
- name: approval_id
source: output
path: $.approval_id

Captures values into named session-scoped slots. Consumer tools reference them with ref in argument_value_invariants.

FieldTypeRequiredDescription
binds[].namestringyesUnique slot name
binds[].source"arguments" | "output"no (default: "arguments")Where to extract the value
binds[].pathstringyesJSONPath into the source object

ref operator (in argument_value_invariants)

argument_value_invariants:
- path: $.shares
ref: approved_shares # Must equal bound value
- path: $.notional_value
ref: approved_notional
tolerance: 0.01 # Relative tolerance for floats
FieldTypeRequiredDescription
refstringnoName of a previously bound slot. Asserts equality.
tolerancenumbernoRelative tolerance for numeric comparisons. Only valid with ref.

schema_derived / schema_derived_exclude

# Disable all schema-derived invariants for this tool
schema_derived: false

# Or exclude specific fields
schema_derived_exclude:
- $.shares
- $.metadata.custom_field

When enabled (default), the compiler extracts JSON Schema keywords (minimum, maximum, pattern, enum, minLength, maxLength, const) from tool definitions and synthesizes corresponding argument_value_invariants automatically. Manual invariants take precedence on the same path.

checkpoint

checkpoint:
when:
- path: $.notional_value
gte: 1000000
- path: $.order_type
equals: "market"
match: any # "any" (default) or "all"
timeout_seconds: 300
on_timeout: deny # "deny" (default) or "allow"
context:
- path: $.shares
label: "Shares"
- path: $.notional_value
label: "Notional Value"

Pauses the session and emits an approval request when conditions are met. See Govern Mode for server-side enforcement.

FieldTypeRequiredDescription
whencondition[] | "always"yesConditions that trigger the checkpoint
when[].pathstringconditionalJSONPath into tool arguments
when[].gte / lte / equals / one_ofvariesconditionalCondition operator
when_side_effectstringnoTrigger based on tool's side_effect classification
match"any" | "all"no (default: "any")Whether any or all conditions must be met
timeout_secondsnumberyesHow long to wait for approval (minimum 30)
on_timeout"deny" | "allow"no (default: "deny")What happens if no decision within timeout
context[]arraynoFields to include in the approval request

session.yaml

One per contracts directory. Defines session-level configuration.

schema_version: "1.0"
agent: my-agent-name

# Phase machine
phases:
- name: start
initial: true
- name: processing
- name: done
terminal: true

transitions:
start: [processing]
processing: [done]

# Session limits
session_limits:
max_steps: 20
max_tool_calls: 50
max_tool_calls_mode: block # "block" (default) | "narrow"
max_cost_per_session: 10.00
max_calls_per_tool:
dangerous_tool: 1
loop_detection:
window: 5
threshold: 3
circuit_breaker:
consecutive_blocks: 5
consecutive_errors: 3

# Risk classification defaults
risk_defaults:
read: allow
write: block
destructive: block
admin: block
financial: block

# Session-level policy
policy:
default_deny: false
rules:
- deny:
principal: { path: "$.type", equals: "readonly" }
- allow:
principal: { path: "$.tier", one_of: ["L2", "L3"] }
tools: ["specific_tool"]

# Provider constraints
provider_constraints:
anthropic:
block_incompatible:
- "tool_choice.type=any + thinking"
openai:
warn_incompatible:
- "parallel_tool_calls=false + gpt-4o-mini"
warn_floating_alias: true

# Resource definitions (for workflow governance)
resources:
order:
type: order
extract_from:
path: "$.order_id"

# Session aggregates (Layer 3)
aggregates:
- name: total_shares
metric: sum
tool: submit_live_order
path: $.shares
lte: 100000
reason: "Total shares must not exceed risk limit"

# Value envelopes (Layer 3)
envelopes:
- name: risk_envelope
stages:
- tool: approve_risk_check
path: $.shares
role: ceiling
- tool: submit_live_order
path: $.shares
role: constrained
constraint: lte_ceiling
reason: "Submitted shares must not exceed approved shares"

# Session checkpoints (Layer 4)
checkpoints:
- name: high_value_trade
tool: submit_live_order
when:
- path: $.notional_value
gte: 1000000
timeout_seconds: 300
on_timeout: deny

# Label gates
policy:
label_gates:
- when_label: mnpi
deny_tools: [submit_live_order, place_trade]
reason: "Trading blocked when MNPI in context"

# Schema-derived invariants (default: true)
schema_derived: true

# Contract graph analysis suppression
graph_analysis:
suppress:
- check: dead_tool
tool: sentinel_tool
reason: "Intentional sentinel"

Phase rules

  • Exactly one phase must have initial: true
  • At least one phase must have terminal: true
  • All phases in transitions must be declared in phases
  • Every non-terminal phase must be reachable from the initial phase
  • Per-tool valid_in_phases and advances_to must reference declared phases

aggregates

Session-wide computed constraints. Checked speculatively before each tool call is committed.

FieldTypeRequiredDescription
namestringyesUnique aggregate identifier
metric"sum" | "count" | "max" | "min" | "count_distinct"yesAggregation function
toolstring | string[] | "*"yesTool(s) to aggregate over
pathstringconditionalJSONPath into arguments. Required for all metrics except count.
gte / ltenumberat least oneBounds on the aggregate value
reasonstringyesHuman-readable reason (appears in block decisions)
whencondition[]noConditional bound overrides (see below)

Conditional bounds: when

Aggregate bounds can adapt to runtime conditions via session bindings. Each when entry references a binding and overrides the base gte/lte when the condition matches. First match wins.

aggregates:
- name: hedge_calls
metric: count
tool: hedge_position
lte: 3
reason: "Hedge call limit"
when:
- binding: latest_var
gte: 0.10
then_lte: 10

Default: max 3 hedge calls. When the latest_var binding is >= 0.10: max 10.

FieldTypeRequiredDescription
bindingstringyesSession binding name to check
gte / lte / equalsnumber or valueat least oneCondition on the binding value
then_gte / then_ltenumberat least oneOverridden bounds when condition matches

The binding must be captured via a contract's binds entry before the aggregate is evaluated. Missing bindings fall through to base bounds.

envelopes

Directional flow constraints across tool calls.

FieldTypeRequiredDescription
namestringyesUnique envelope identifier
stages[]arrayyesOrdered value capture/check points (min 2)
stages[].toolstringyesTool name
stages[].pathstringyesJSONPath to the value
stages[].role"ceiling" | "floor" | "anchor" | "initial" | "constrained"yesRole in the envelope
constraint"lte_ceiling" | "gte_floor" | "within_band" | "monotonic_decrease" | "monotonic_increase" | "bounded"yesConstraint type
bandnumberconditionalBand width for within_band (fraction, e.g., 0.05 = 5%)
reasonstringyesHuman-readable reason

checkpoints (session-level)

Session-level checkpoint definitions (alternative to per-tool checkpoint blocks).

FieldTypeRequiredDescription
namestringyesCheckpoint name
toolstring | "*"yesTool to gate
whencondition[] | "always"yesTrigger conditions
when_side_effectstringnoTrigger based on side_effect classification
timeout_secondsnumberyesApproval timeout (minimum 30)
on_timeout"deny" | "allow"no (default: "deny")Timeout behavior

label_gates (in policy)

Context-aware tool gating based on session labels.

FieldTypeRequiredDescription
when_labelstringyesLabel that activates this gate
deny_toolsstring[]yesTools denied when label is active
reasonstringyesHuman-readable reason

Labels follow taint semantics: they can be added but never removed within a session.

schema_derived (session-level)

schema_derived: false    # Disables schema-derived invariants globally

Default: true. When enabled, JSON Schema keywords from tool definitions are automatically synthesized into argument_value_invariants.

graph_analysis

graph_analysis:
suppress:
- check: dead_tool
tool: sentinel_tool
reason: "Intentional sentinel"

Suppress specific contract graph analysis diagnostics. Suppressions require a reason field.

Provider constraints

Block known API incompatibilities or warn about soft issues per provider.

FieldTypeDescription
provider_constraintsobjectTop-level key, keyed by provider name (openai, anthropic)
block_incompatiblestring[]Patterns that throw before the LLM call
warn_incompatiblestring[]Patterns that fire a diagnostic callback and proceed
warn_floating_aliasbooleanRecommend pinned model version instead of alias
provider_constraints:
anthropic:
block_incompatible:
- "tool_choice.type=any + thinking" # Hard API error
openai:
warn_incompatible:
- "parallel_tool_calls=false + gpt-4o-mini" # Silently ignored
warn_floating_alias: true # Recommend pinned model version

block_incompatible entries throw before the LLM call. warn_incompatible entries fire a diagnostic callback and proceed.


workflow.yaml

Defines multi-session coordination above individual sessions.

schema_version: "1.0"
workflow: my-workflow-name

roles:
- name: orchestrator
session_contract: packs/orchestrator/session.yaml
- name: worker
session_contract: packs/worker/session.yaml

handoffs:
- from: orchestrator
to: worker
requires_artifacts: ["task_summary"]
parent_close_policy: request_cancel

workflow_limits:
max_sessions: 8
max_active_sessions: 4
max_total_steps: 100
max_total_cost: 25.00
max_open_handoffs: 6

shared_resources:
- alias: resource_name
mode: single_writer # single_writer | exclusive_pending | serial_only

cancellation:
subtree_kill: true
workflow_kill: operator_only

parent_close_policy:
default: request_cancel # request_cancel | terminate | abandon

Shared resource modes

ModeBehavior
exclusive_pendingWhile one session has a pending step on the resource, no other session can start one
single_writerAt most one session can be the mutating owner
serial_onlyMultiple sessions may act, but only one commits at a time

Kill scopes

ScopeEffect
sessionKills one session
subtreeKills session + all descendants
workflowKills all sessions, rejects future claims

Invariant operators

Full operator set (assertions, execution_constraints, preconditions.with_output)

OperatorTypeDescription
equalsanyExact value match
existsbooleanField exists (not null/undefined)
typestringValue type check (string, number, integer, boolean, object, array)
containsstringString/array contains
one_ofarrayValue in enumeration
regexstringRegex pattern match
gtenumberGreater than or equal
ltenumberLess than or equal
length_gtenumberMinimum length
length_ltenumberMaximum length
equals_envstringEquals environment variable value

Argument value invariants (subset)

argument_value_invariants support a narrower set of operators:

OperatorTypeDescription
exact_matchstringString exact match
typestringValue type check
regexstringRegex pattern match
one_ofarrayValue in enumeration
gtenumberGreater than or equal
ltenumberLess than or equal
refstringAssert equality with a previously bound session slot
tolerancenumberRelative tolerance for numeric ref comparisons

Next steps