Skip to content

Contracts Guide

Handover contracts validate step output before dependent steps begin. They catch malformed artifacts early, preventing wasted work downstream.

What is a Contract?

A contract validates that a step produced correct output. Contracts can check:

  • Structure - JSON schema compliance
  • Types - TypeScript compilation
  • Behavior - Test suite results

Configuration

Contracts are defined in a step's handover section:

yaml
steps:
  - id: navigate
    handover:
      contract:
        type: json_schema
        schema_path: .agents/contracts/navigation.schema.json
        source: .agents/output/analysis.json
        on_failure: retry
        max_retries: 2
FieldDefaultDescription
type-json_schema, typescript_interface, test_suite, markdown_spec, format, non_empty_file, llm_judge, source_diff, agent_review, event_contains, or spec_derived_test
schema_path-Schema file path (for json_schema)
source-File to validate
command-Test command (for test_suite)
dirworkspaceWorking directory for test_suite: project_root, absolute, or relative
must_passtrueWhether failure blocks progression
on_failureretryAction: retry or halt
max_retries2Maximum retry attempts

Contract Types

JSON Schema

Validates output structure:

yaml
handover:
  contract:
    type: json_schema
    schema_path: .agents/contracts/navigation.schema.json
    source: .agents/output/analysis.json

Example schema:

json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["files", "summary"],
  "properties": {
    "files": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["path", "purpose"],
        "properties": {
          "path": { "type": "string" },
          "purpose": { "type": "string" }
        }
      }
    },
    "summary": { "type": "string" }
  }
}

TypeScript Interface

Validates generated TypeScript compiles:

yaml
handover:
  contract:
    type: typescript_interface
    source: .agents/output/types.ts
    validate: true

If tsc is unavailable, degrades to syntax-only checking.

Test Suite

Validates by running tests:

yaml
handover:
  contract:
    type: test_suite
    command: "go test ./..."
    dir: project_root
    must_pass: true
    max_retries: 3

Use dir: project_root when the command needs project files (like go.mod). Without it, the command runs in the ephemeral workspace directory.

Non-Empty File

Validates that the artifact file exists and is not empty. Useful as a lightweight check that a step produced output.

yaml
handover:
  contract:
    type: non_empty_file
    source: .agents/output/result.md

No additional configuration fields are required beyond type and source.

LLM Judge

Uses an LLM to evaluate the artifact against criteria specified in a prompt. The LLM reads the artifact content and returns a pass/fail judgment.

yaml
handover:
  contract:
    type: llm_judge
    source: .agents/output/plan.md
    prompt: "Does this plan address all acceptance criteria from the issue?"
    model: balanced
FieldDefaultDescription
prompt-Evaluation criteria for the LLM judge
modelcheapestModel tier (cheapest, balanced, strongest) or a specific model identifier

Source Diff

Validates that the step produced meaningful source code changes by checking the git diff. Catches the failure mode where a step claims success but made no actual changes.

yaml
handover:
  contract:
    type: source_diff
    glob: "*.go"
    min_files: 1
FieldDefaultDescription
glob(all files)Glob pattern to filter which changed files count
min_files1Minimum number of qualifying changed files

Agent Review

Delegates validation to another agent session using an adapter runner. Unlike other contract types, agent_review does not use NewValidator — the executor calls ValidateWithRunner() instead.

yaml
handover:
  contract:
    type: agent_review
    source: .agents/output/implementation.md
    prompt: "Review this implementation for security issues"

Event Contains

Validates that specific pipeline events occurred during step execution. Matches events by state and optional message substrings.

yaml
handover:
  contract:
    type: event_contains
    events:
      - state: step_failed
        contains: "timeout"
FieldDefaultDescription
events-List of event patterns to match
events[].state-Required event state to match (e.g., step_failed)
events[].contains(any)Optional substring the event message must include

Spec-Derived Test

Generates and runs tests derived from a specification document. Like agent_review, this type requires an adapter runner — NewValidator returns nil and the executor calls ValidateSpecDerived() instead.

yaml
handover:
  contract:
    type: spec_derived_test
    source: .agents/output/spec.md
    test_dir: .agents/output/tests/

Failure Handling

Retry Behavior

When on_failure: retry:

  1. Step transitions to retrying
  2. Re-executes with fresh context
  3. Validates again
  4. After max_retries failures, transitions to failed

Halt Behavior

When on_failure: halt:

  1. Step immediately fails
  2. Pipeline stops
  3. Error includes validation details

Optional Contracts

Use must_pass: false for advisory checks:

yaml
handover:
  contract:
    type: test_suite
    command: "npm run lint"
    must_pass: false    # Log but don't block

Common Patterns

yaml
- id: navigate
  output_artifacts:
    - name: analysis
      path: .agents/output/analysis.json
  handover:
    contract:
      type: json_schema
      schema_path: .agents/contracts/navigation.schema.json
      source: .agents/output/analysis.json

Implementation Contract

yaml
- id: implement
  handover:
    contract:
      type: test_suite
      command: "go build ./... && go test ./..."
      dir: project_root
      max_retries: 3

Chained Validation

Use a script for multiple checks:

yaml
handover:
  contract:
    type: test_suite
    command: ".agents/scripts/validate.sh"
bash
#!/bin/bash
set -e
npx ajv validate -s schema.json -d output.json
npm test

Schema Organization

.agents/
├── contracts/
│   ├── navigation.schema.json
│   ├── specification.schema.json
│   └── implementation.schema.json
└── pipelines/
    └── feature-flow.yaml

Debugging Failures

Check audit logs:

bash
cat .agents/traces/<pipeline-id>.jsonl | jq 'select(.type == "contract_failure")'

See Also

Released under the MIT License.