agent-behavior-trace/v0
JSON schema for trace-diff cases on raisingagents.is.
Read by: <trace-diff> web
component (system/assets/trace-diff.js). Written by
(Round 06+): the workbench evaluator on each contract run.
Written by (Round 05): human authors, by hand, for
canonical demos.
Stable contract. Versioned at the top level
("schema": "agent-behavior-trace/v0"). Breaking changes
bump to /v1; the component refuses unknown versions.
1 — Top-level shape
{
"schema": "agent-behavior-trace/v0", // required · identity
"case": "OPBR · refund commit", // required · human label
"contract": "refund.commit_after_checks", // required · contract ref
"contract_clause": "P2", // optional · specific clause
"rails": [Rail, Rail], // required · exactly 2
"verdict": Verdict, // required
"permalink": "/lab/opbr", // optional · canonical URL
"updated": "2026-05-24" // optional · ISO date
}
A trace is always two rails. Single-rail cases are out of scope for this format — they belong to a different surface (a workbench run viewer, not a trace-diff).
2 — Rail
{
"id": "run-a", // required · slug, used as DOM id
"label": "Run A · stable", // required · display label, mono kicker
"regression": false, // required · marks the rail that fails contract
"steps": [Step, …] // required · 1 or more
}
The two rails share step indices (1, 2, 3, …) but are otherwise independent. Step counts may differ between rails. The reader compares row-by-row; the component does not enforce alignment.
Exactly one rail should have "regression": true. That
rail is rendered with the oxide accent column and receives the violation
marker treatment.
3 — Step
{
"n": 2, // required · 1-based index, used for sort
"tool": "refund_commit", // required · snake_case tool name
"result": "success", // required · short return summary
"kind": "side_effect", // required · semantic role (see below)
"consequential": true, // optional · is this the apex of a violation?
"in_violation": false, // optional · is this step inside a violation span?
// Round 06+ fields, optional, ignored by Round 05 renderer:
"args": { "customer_id": "c_91", "amount_eur": 1200 },
"return": { "ok": true, "ref": "rf_2024_…" },
"timestamp_ms": 1832,
"tokens": 412
}
kind vocabulary
(locked)
| value | meaning | marker (default) |
|---|---|---|
"read" |
non-mutating read of state | open circle |
"precondition" |
check that gates a side effect | open circle |
"side_effect" |
mutates the world (refund, write, edit) | open circle when ok, filled square + oxide when
consequential: true |
"respond" |
terminal model output | open circle |
The marker rendering is a function of kind +
consequential + in_violation. The author
asserts the semantics; the component renders.
consequential and
in_violation
These are assertions about the contract evaluation,
not about the step itself. The same refund_commit step is:
- consequential: false in Run A (preconditions resolved
first → no violation) - consequential: true in Run B
(preconditions still pending → apex of violation)
in_violation: true marks the rows that sit inside the
violation span — typically the preconditions that should have fired
before the consequential side effect. The renderer draws the hand-drawn
bracket from the consequential row through all subsequent
in_violation rows.
4 — Verdict
{
"output": "PASS", // required · "PASS" | "FAIL"
"behavior": "FAIL", // required · "PASS" | "FAIL"
"behavior_reason": "P2 violation — side effect precedes precondition check (policy_version_check)",
"operational": "within budget" // optional
}
behavior_reason is required when
behavior === "FAIL". The Round 05 renderer shows it in the
compact verdict bar. Round 06’s verdict-flip computes this string live
as the reader drags refund_commit.
5 — Component contract
The <trace-diff> element accepts data in three
ways:
- External JSON —
<trace-diff data-src="/system/data/opbr-canonical.json"></trace-diff> - Inline JSON —
<trace-diff><script type="application/json">{…}</script></trace-diff> - Programmatic —
el.data = {…}thenel.render()
Mode attributes:
data-mode="compact"(default) — ~200px tall, embeddable in prose.data-mode="expanded"— ~420px tall, fully unfolded. Used on the canonical demo page.data-inspect="/lab/opbr#after-step-3"— adds an “inspect →” link to a deeper view.
Schema mismatch (wrong version, missing required field) renders a
single line:
[trace-diff: unsupported schema X · expected agent-behavior-trace/v0].
No silent failure.
6 — Versioning policy
- v0: this document. Round 05 ships against v0.
- v1: breaking change to
kindvocabulary, or toverdictshape. Workbench evaluator output bumps when adding new clause types.
The component validates schema on every render. Unknown
schema strings render the error line, never the data.