// behavioral specs for AI agents

The graph your agent actually follows.

You wrote the prompt. Maybe you even wrote an orchestration graph. But what your agent really does — which tools, in what order, gated on what conditions — lives in a pile of traces nobody reads. tracegraph turns those traces into a spec: reviewable in a PR, checked in CI, diffed across model swaps, and enforced live in front of your MCP tools.

Get started on GitHub npm i -g tracegraph · Apache-2.0 · zero API keys to try
tracegraph synthesize ./traces -o refund.spec.yaml

refund — induced from 101 traces
action: issue_refund · 45 took it, 56 did not · training agreement: 100.0%

 get_order(order_id) as order
 check_refund_policy(order_id) as refund_policy
 get_customer(customer_id) as customer  (not load-bearing)
◇ gate: (refund_policy.max_amount > 0.25)
     issue_refund(order_id, amount)

spec written to refund.spec.yaml — commit it next to your code
the problem

Agents decide their own control flow. Can you answer these four questions?

unanswerable today

What does it actually do?

The real behavior exists only across thousands of traces nobody can read.

unanswerable today

Does it stay inside the lines?

"Never refund without a valid policy check" — an LLM judge's opinion is not a guarantee.

unanswerable today

What changes if I swap models?

A benchmark score moving 87→86 tells you that something changed. Never what.

unanswerable today

What would it have done if…?

Re-running an agent re-rolls the dice. You can't hold everything fixed and vary one thing.

All four become answerable the moment behavior is a document instead of a log pile. That document is what tracegraph induces — decision-tree guards over the state your agent actually acted on, with data flow lifted into variables. No chain-of-thought access required: on our benchmark corpus the spec was induced from observable actions alone.

why scripted checks aren't enough

A true story: the refund that every ordering check passed.

We watched a frontier model, confused by poorly-named tools, pass a customer id where an order id belonged. The policy check errored. The model issued the refund anyway — then checked the policy again afterwards. A hand-written trajectory assertion — "was the policy checked before the refund?"passed this run: a check did precede the refund. Its answer was just never valid. The induced spec caught it, because the spec knows what state the decision requires, not merely what order calls happen in. That trace ships in the repo as a permanent test. And the gate blocks it live:

node examples/refund/bad-agent.mjs   # reenacts the captured failure — no LLM needed

 get_order({"order_id":"ORD-1002"})
 check_refund_policy({"order_id":"CUST-1004"})   ← the real captured mistake
   {"error":"order CUST-1004 not found"}
✗ issue_refund — blocked: gate guard does not hold: (refund_policy.max_amount > 0.25)
the refund never executed — backend state: {"refunds":[]}
one spec, four verbs

Everything hangs off a document that lives in git.

synthesize

Traces in, spec out. Reads Claude Code sessions, ATIF, and OpenTelemetry GenAI exports; auto-detects the consequential action; separates mixed trace populations instead of averaging them.

tracegraph synthesize ./traces \
  -o agent.spec.yaml

check

CI regression for behavior. Every gated action is judged on the state visible at that moment, plus your hand-written invariants. Non-zero exit on deviation.

tracegraph check ./traces \
  --spec agent.spec.yaml \
  --rules invariants.yaml  # exit 1 on deviation

diff

What a model or prompt swap really changed: structure, thresholds (collapsed into readable moves), and decision agreement replayed over real samples.

tracegraph diff old.yaml new.yaml \
  --traces ./traces
~ threshold moved: total > 0.25 -> 5
decisions: 29/29 agree — equivalent

gate

An MCP proxy in front of your real tools. Conformant calls flow through in microseconds; deviations are blocked (or shadow-logged) before the tool ever runs.

tracegraph gate --spec agent.spec.yaml \
  --mode block \
  --target-url http://localhost:8321/mcp
against the alternatives

"Don't we already have tools for this?"

Eval & observability platformsLangSmith, Braintrust, Langfuse…
They give you recordings and scores — what happened, and a judge model's opinion of it. tracegraph gives you a specification — a formal object you can prove things about, enforce at runtime, and diff structurally. Keep your eval stack: tracegraph reads the very traces it already collects.
Orchestration graphsLangGraph, ADK, AutoGen GraphFlow…
Those are graphs you author — what you intend. tracegraph induces the graph you discover — what your agent actually does. The diff between the two is precisely the information you don't have today. They compose: keep authoring; tracegraph tells you what really runs.
Hand-written trajectory checksassertions, pytest, rewardkit criteria…
Ordering assertions verify sequence — and sequence held in our captured bad refund; the decision was still wrong. Induced guards verify decision logic: the state a decision was made on. And you don't have to enumerate the checks — they fall out of the traces you already have.
Static guardrail rulesallowlists, regex filters, policy DSLs…
Somebody has to know and write the rules — and the rules nobody thought to write are the ones that fire at 2am. Induction surfaces the rules your agent already follows (including ones nobody designed: our test agent silently refuses $0 refunds), then lets you enforce them.
validated, not vibes

The method held up before the tool shipped.

95%+

held-out agreement between induced guards and real agent behavior (leave-one-input-out)

279

real traces in the integration corpus — 3 tool-name dialects, 2 models, 1 known-bad run

1 / 279

deviations flagged across the corpus: exactly the known-bad trace, nothing else

µs

gate decision time — pure in-memory boolean logic; session state accumulates by proxying

HONEST SCOPE

tracegraph covers the behavioral layer: tools, ordering, structured conditions. It does not judge whether your model's reasoning was sound, and semantic conditions ("the customer sounds angry") are future work. For consequential actions — money, deletion, compliance — the behavioral layer is the one you need guarantees on.

start now

90 seconds, no API key.

# the example ships real traces + a tiny MCP backend — no keys, no cloud
git clone https://github.com/wayfarer-ai/tracegraph && cd tracegraph && npm i && npm run build
npx tracegraph synthesize examples/refund/traces -o refund.spec.yaml # induce the spec
npx tracegraph check examples/refund/traces --spec refund.spec.yaml  # 16/16 conformant
node examples/refund/server.mjs &
node examples/refund/bad-agent.mjs                                   # watch the gate block it