> ## Documentation Index
> Fetch the complete documentation index at: https://reagent-ai.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Handoff Contracts

> Validate the data shape and integrity of handoffs between agents.

## Overview

Handoff contracts validate the structured data passed from one agent session to another via `handoff_context`. They catch renamed fields, missing keys, wrong types, and broken parent-child links.

## Setting up a handoff

Link two sessions by passing `parent_trace_id` and `handoff_context`:

```python theme={null}
import reagent_flow

with reagent_flow.session("parent-agent") as parent:
    parent.log_llm_call(tool_calls=[{"name": "plan", "arguments": {}}])
    parent.log_tool_result("plan", result={"task": "research", "query": "Q3"})

# Child receives the parent's output
with reagent_flow.session(
    "child-agent",
    parent_trace_id=parent.trace.trace_id,
    handoff_context={"task": "research", "query": "Q3"},
) as child:
    child.log_llm_call(tool_calls=[{"name": "search", "arguments": {}}])
    child.log_tool_result("search", result={"found": True})
```

## assert\_handoff\_received

Verifies that a child session is linked to its expected parent.

```python theme={null}
child.assert_handoff_received(parent)
```

Fails if the child's `parent_trace_id` doesn't match the parent's `trace_id`, or if `handoff_context` is missing.

## assert\_handoff\_matches

Type-checks the `handoff_context` against a declared schema.

```python theme={null}
child.assert_handoff_matches(schema={
    "task": str,
    "query": str,
})
```

### What it checks

* Every key in the schema must exist in the handoff data
* Every value must match the declared type
* Nested dicts, typed lists, and list-of-dicts are supported (see [Nested Schemas](/advanced/nested-schemas))
* `bool` and `int` are strictly separated — `True` is not a valid `int`

### Failure message

```
handoff field 'data_access.contains_customer_pii': missing from data

AGENT STACK TRACE — security-review
================================
Turn 0: assess_security_risk(vendor_name="ClearVoice AI")
  → {"risk_level": "LOW"}
```

## assert\_handoff\_has\_fields

Checks that specific field names exist in the handoff context (without type checking):

```python theme={null}
child.assert_handoff_has_fields(["task", "query", "priority"])
```

Use this for quick presence checks when you don't need full type validation.

## assert\_no\_extra\_fields

Verifies that the handoff context contains **only** the allowed fields:

```python theme={null}
child.assert_no_extra_fields(allowed=["task", "query"])
```

Catches PII or debug fields accidentally leaking through handoffs. Fails if the handoff contains any key not in the allowed list.

## assert\_context\_preserved

Verifies that specific values survived a handoff unchanged:

```python theme={null}
source = {"user_id": "abc123", "query": "revenue Q4"}
child.assert_context_preserved(source, fields=["user_id", "query"])
```

Compares the `source` dict values against the child's `handoff_context` for the listed fields. Fails if any value was mutated, truncated, or lost.

## Full example

```python theme={null}
def test_handoff_contracts(tmp_path):
    trace_dir = str(tmp_path)

    with reagent_flow.session("intake", trace_dir=trace_dir) as parent:
        parent.log_llm_call(
            tool_calls=[{"name": "extract_vendor_packet", "arguments": {"vendor_name": "ClearVoice AI"}}],
        )
        parent.log_tool_result(
            "extract_vendor_packet",
            result={
                "vendor_name": "ClearVoice AI",
                "data_access": {"contains_customer_pii": True},
            },
        )

    handoff = {
        "vendor_name": "ClearVoice AI",
        "data_access": {"contains_customer_pii": True},
    }
    with reagent_flow.session(
        "security-review",
        trace_dir=trace_dir,
        parent_trace_id=parent.trace.trace_id,
        handoff_context=handoff,
    ) as child:
        child.log_llm_call(
            tool_calls=[{"name": "assess_security_risk", "arguments": {"vendor_name": "ClearVoice AI"}}],
        )
        child.log_tool_result("assess_security_risk", result={"risk_level": "HIGH"})

    # All handoff contracts
    child.assert_handoff_received(parent)
    child.assert_handoff_has_fields(["vendor_name", "data_access"])
    child.assert_handoff_matches(schema={
        "vendor_name": str,
        "data_access": {"contains_customer_pii": bool},
    })
    child.assert_no_extra_fields(allowed=["vendor_name", "data_access"])
    child.assert_context_preserved({"vendor_name": "ClearVoice AI"}, fields=["vendor_name"])
```
