> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arclasp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Chain-level governance

> Why governing whole agent workflows matters more than governing individual tool calls

# Chain-level governance

This is the design decision that makes Arclasp different from other agent governance tools. Understanding it explains why the SDK is structured the way it is and what kind of failures it catches that others don't.

## The problem with per-call governance

Most AI safety tools evaluate one tool call at a time. The agent calls `search_web` — the tool is allowed, no PII, looks fine. The agent calls `calculate_offer` for $3,000 — well below the configured single-transaction threshold, allowed. The agent calls `send_email` — domain is on the allowlist, content looks normal, allowed. The agent calls `record_commitment` for $3,000 — again under the threshold, allowed. Another $3,000 commitment a step later — allowed. A $4,000 commitment to close out the workflow — still under the single-transaction threshold, allowed.

Six allow decisions. The workflow completes. Your company is now committed to paying a vendor \$13,000.

Each individual step passed its own review. The chain of steps did not.

## What chain-level tracking measures

When you open a Arclasp `Chain`, the SDK creates a session that lives for the duration of that workflow. Every action the agent takes in that session contributes to running totals:

* **Cumulative financial exposure** — sum of all `amount` or `value` fields seen across the workflow
* **External communications** — count of emails sent, messages posted, API calls made to external domains
* **Records modified** — count of write/update/delete actions against your data
* **Privileged actions** — count of permission changes, IAM operations, role assignments
* **External domains contacted** — deduplicated list of every domain the agents touched
* **Tokens used and estimated cost** — running LLM spend across all calls in the workflow

Every policy evaluation sees these totals. A $4,000 charge looks fine in isolation; the same charge after three other $3,000 charges is the one that should require approval. The cumulative threshold catches this; the per-transaction threshold doesn't.

## The example, in code

```python theme={null}
import asyncio
import arclasp

arclasp.init(
    api_key="prail_...",
    financial_approval_threshold_usd=5000,        # single-transaction cap
    cumulative_financial_threshold_usd=10000,     # chain cumulative cap
)

async def vendor_workflow():
    async with arclasp.Chain("vendor-purchase") as chain:

        # Research agent — no financial impact
        await chain.record_agent_action(
            agent_name="research-agent",
            action_type="tool_call",
            action_name="search_web",
            payload={"query": "office chair vendors"},
        )
        # Cumulative exposure: $0

        # Negotiation agent — proposes a price
        await chain.record_agent_action(
            agent_name="negotiation-agent",
            action_type="tool_call",
            action_name="record_commitment",
            payload={"amount_usd": 3000},
        )
        # $3,000 < $5,000 single-transaction threshold → allowed
        # Cumulative exposure: $3,000

        # Email agent — sends the offer
        await chain.record_agent_action(
            agent_name="email-agent",
            action_type="tool_call",
            action_name="send_email",
            payload={"to": "sales@vendor.com", "subject": "Purchase order"},
        )

        # Another $3,000 commitment
        await chain.record_agent_action(
            agent_name="commitment-agent",
            action_type="tool_call",
            action_name="record_commitment",
            payload={"amount_usd": 3000},
        )
        # $3,000 < $5,000 → still allowed by single-transaction rule
        # Cumulative exposure: $6,000 (still under the $10,000 chain cap)

        # Another $3,000
        await chain.record_agent_action(
            agent_name="commitment-agent",
            action_type="tool_call",
            action_name="record_commitment",
            payload={"amount_usd": 3000},
        )
        # $3,000 < $5,000 → still allowed by single-transaction rule
        # Cumulative exposure: $9,000 (still under chain cap)

        # Final $4,000 commitment
        await chain.record_agent_action(
            agent_name="commitment-agent",
            action_type="tool_call",
            action_name="record_commitment",
            payload={"amount_usd": 4000},
        )
        # $4,000 < $5,000 → would be allowed by single-transaction rule
        # BUT cumulative would reach $13,000 — exceeds $10,000 chain cap
        # → require_approval triggered, execution blocks for human review

asyncio.run(vendor_workflow())
```

A per-call governance tool would let every action through — none of the individual amounts exceeds the single-transaction threshold. Arclasp blocks the final action and waits for a human to approve or deny it. The approver sees the full chain context (all the prior commitments, what each agent did, the running totals) and makes an informed decision.

## How chain context builds up

Every action in the example above — including the early `search_web` call — is sent to the backend and added to the chain's cumulative metrics. There is no local decision path that skips this: the backend sees the full chain state, in order, before it evaluates each later action. That's what lets a later financial action be judged against everything that came before it in the same chain, not just in isolation.

(An earlier local-evaluation code path still exists in the SDK for compatibility; it does not participate in current governed decisions — see [Limitations](/limitations#backend-round-trip-required-for-every-governed-action).)

## Chain lifecycle

A chain is a context manager. It starts when you enter the `with` block and completes when you exit, normally or via exception.

```python theme={null}
async with arclasp.Chain("workflow-name", metadata={...}) as chain:
    # All actions recorded here belong to one chain
    await chain.record_agent_action(...)
    await chain.record_agent_action(...)
# Chain completes here. Receipt is generated. Audit trail is sealed.
```

When the chain exits, the SDK calls the chain-complete endpoint on the backend, which seals the chain and triggers receipt generation. See [Audit receipts](/concepts/audit-receipts) for what's in a receipt and how to verify them.

## Where to go next

<CardGroup cols={2}>
  <Card title="Policies" icon="shield" href="/concepts/policies">
    How Arclasp decides what to allow, flag, or block.
  </Card>

  <Card title="Audit receipts" icon="receipt" href="/concepts/audit-receipts">
    HMAC-signed records verifiable via a public endpoint.
  </Card>

  <Card title="Configuration" icon="gear" href="/reference/configuration">
    Tune chain behavior — thresholds, fail modes, timeouts.
  </Card>

  <Card title="Framework adapters" icon="puzzle-piece" href="/frameworks/langgraph">
    Wrap LangGraph, LangChain, CrewAI, or MCP with one line.
  </Card>
</CardGroup>
