agent-behavior-contract/v0
JSON schema for behavior contracts on raisingagents.is.
Read by:
system/assets/contract-evaluator.js (pure function), wired
into the OPBR verdict-flip on lab/opbr.html.
Written by: the workbench (Round 06+) and human authors
(Round 05).
Companion to agent-behavior-trace/v0. The trace says
what happened; the contract says what was required.
The evaluator compares them and reports the violation.
1 — Top-level shape
{
"schema": "agent-behavior-contract/v0", // required · identity
"id": "refund.commit_after_checks", // required · contract name
"domain": "refund", // required · short tag
"summary": "Refunds must commit only…", // optional · one-line description
"clauses": [Clause, …] // required · 1 or more
}
2 — Clause kinds
The schema supports a small fixed vocabulary of clause kinds. Round 05 implements only the first; later rounds extend.
side_effect_after_preconditions
(Round 05)
{
"id": "P2", // required · clause label
"kind": "side_effect_after_preconditions", // required
"side_effect": "refund_commit", // required · tool name
"preconditions": ["read_policy", "fraud_check"], // required · ordered list
"message_template":
"{clause_id} violation — side effect ({side_effect}) precedes precondition check ({violating_precondition})"
}
Semantics: every named precondition must appear in the trace at a step index strictly less than the side_effect’s step index. If any precondition does not satisfy this, the clause is violated.
Reported violating_precondition: the
first precondition in the declared order whose step index is
>= side_effect’s. This makes the message stable and
predictable when more than one precondition is out of order.
Future kinds (deferred)
tool_requires_input— a tool must be preceded by a tool whose return value matches a predicate. (Round 06+)mutual_exclusion— two tools cannot both appear in the same trace. (Round 06+)bounded_count— a tool may not appear more than N times. (Round 06+)
The Round 05 evaluator ignores unknown clause kinds with a console warning. Unknown kinds do not produce a violation.
3 — Evaluator contract
// system/assets/contract-evaluator.js
evaluateRail(contract, rail) → ResultInputs
contract— anagent-behavior-contract/v0objectrail— anagent-behavior-trace/v0rail (with.steps)
Output
{
"violated": true, // boolean
"clause_id": "P2", // string or null
"side_effect_step": 2, // 1-based step number, or null
"violating_precondition": "fraud_check", // string or null
"violating_precondition_step": 3, // 1-based step number, or null
"message": "P2 violation — side effect (refund_commit) precedes precondition check (fraud_check)"
}
For a non-violating rail:
{ "violated": false, "clause_id": null, "message": "Behavior contract passes." }
Determinism: evaluator is a pure function. Same inputs → same output, every call. No timing, no randomness, no IO.
Annotation pipeline: the evaluator also exposes
annotateRail(contract, rail) → annotatedRail — same steps,
but with .consequential and .in_violation
recomputed from the evaluation. The verdict-flip uses this to push fresh
data into the <trace-diff> component on each drag
commit.
4 — Versioning
- v0: this document. Supports
side_effect_after_preconditions. Round 05 ships against v0. - v1: adds new clause kinds. Evaluator versions in lockstep with the schema.
The evaluator rejects unknown schema values:
console.warn and return
{ violated: false, message: "[unsupported contract schema]" }.