Provenance mode. Every figure below now shows the exact key it was read from — in out/development/evaluation.json, the manifest make sweeps writes, unless it names another artifact. Nothing on this page is typed by hand; a value an artifact does not carry renders as a dash rather than a plausible number. That rule is not decorative — a hardcoded constant was found masquerading as a measurement in this very file and is recorded in EVALUATION.md §10.
WARNING: evaluation.json parse failed or is incomplete. Figures it does not carry render as a dash, and receipts it does not carry cannot be verified.
Agent report card/—/—

—

recovered in the development cohort · —

—

—

— arm-seed runs · —

§2b · simulator-dependent — the outcome model decides these

EXHIBIT A — The Money, and What It Cost

The money above is what the agent recovered. This is what it cost to recover it compliantly: the realistic incumbent recovers more, and that was registered as falsification criterion F1 before the first run. The interval below excludes zero in the baseline's favour — a worse result than F1 firing, and the artifact says so in its own detail field. Every figure here is read from out/development/evaluation.json; nothing is hardcoded, and a value the manifest does not carry renders as a dash.

Paired difference vs Vasool, recovery rate

Every arm runs the same seeded universe — same customers, same arrivals, same outcome draws — so the comparison is the per-seed difference, bootstrapped over the development cohort's seeds. Bars are 95% percentile intervals. — A marker clear of the dashed zero line is a real difference; which side it falls on is the story.

Vasool recovers more Vasool recovers less zero — no detectable difference
Table view
ArmDifference95% intervalExcludes zero
§2a · simulator-independent — the simulator cannot fake these

EXHIBIT B — The Safety Ledger

These are properties of what the agent did, scanned from the hash-chained ledger. They hold or fail regardless of what outcome model runs underneath, which is why they are the claims this project actually rests on — and why they are banded differently from every recovery number on this page. Fifteen pure-function guards gate the execution plane; all fifteen are evaluated on every proposal and resolved by severity, never short-circuited.

The chain, in the order it runs

Drawn from vasool/policy/registry.py at build time, not copied: each link is a guard's own name and statute, in evaluation order, with COMPLIANCE.md's number beside it. Where the code sets no statute — a platform constraint or a house rule — the page says None rather than lending it one.

    World-keyed counters

    Counted against the class the world registered for each episode, not the label the arm assigned itself — so an arm that declines to classify cannot satisfy these by mislabelling. These are world numbers, not ledger scans, and they are not part of EVALUATION.md §2a. Every row is the sum over the development cohort.

    Arm Retries onINSTRUMENT_DEAD Actions onRISK_BLOCK Retries onCUSTOMER_ACTION

    The first column is read across arms, not against zero. Taxonomy §2 permits one probe on an INSTRUMENT_DEAD row, so a non-zero count is expected of an agent that classifies honestly; the other two columns price at zero and are coloured by it. The third closes the limit registered in EVALUATION.md §10 on 2026-08-24, which recorded that CUSTOMER_ACTION — 0.09 of the registered failure mix, and priced at zero retry budget — had no world-keyed counter, so a baseline retrying those episodes earned recovery credit with no guardrail reporting it. A dash means the artifact does not carry the field; no value here is defaulted.

    Where every episode ended

    A recovery rate reports one bucket and leaves the rest as a single undifferentiated failure. Restraint is not a failure, and neither is an episode the horizon cut short. The four terminal states are absorbing, so this is a partition: every episode appears exactly once, and the row sums to the episode count. blocked is the agent declining to act; escalated is a human taking it; awaiting is right-censored — still in flight when the horizon ended, neither won nor lost.

    recovered blocked escalated exhausted awaiting
    Arm Episodes Recovered Blockeddeclined by policy Escalatedto a human Exhaustedbudget burned Awaitingcensored

    §7 · sensitivity

    EXHIBIT C — Does It Survive the Sweep?

    Most of the outcome parameters are guesses. So every registered parameter is swept independently at −50%, −25%, +25% and +50% of its value, 9 arms at a time, and each comparison is re-tested in every configuration. A cell is marked only when the comparison fails to survive; the unremarkable majority stays recessive, because the exceptions are the finding. F6 fires if 5 or more of the 8 comparisons flip in at least one configuration.

    survives fails to survive each column is one configuration

    §9 · registered in advance

    EXHIBIT D — What Would Have Killed This

    Seven criteria, each with a threshold, written into the protocol before any run existed. A criterion invented after seeing the numbers is not a criterion. — Read F1's row carefully, because fired: false is not the same as good news, and the artifact says so in its own detail field.

    §4.5 · where the LLM lost

    EXHIBIT E — Should the LLM Own This?

    The architecture keeps the LLM away from money. That is a claim; this is the measurement behind it. Both classifiers were asked the same questions in shadow — the LLM never touched a ledger, and a test walks the import graph in both directions to prove it could not have. The rules column is 1.000 by construction, not by measurement — ground truth resolves through the same lookup the rules read, and saying so is the only way the other column means anything.

    Failure the webhook reported Truth Rules LLM Accuracy Consistency Episodes

    invariant 1 · proved by parsing, drawn here

    EXHIBIT F — The AI Air-Gap

    Not a drawing of the claim — the claim itself. tests/test_shadow_boundary.py proves invariant 1 by walking the import graph with ast; this is that same graph, parsed at build time and rendered. Arrows run from importer to imported, so both planes point into the shared vocabulary: they agree on the type definitions and share no path.

    SHADOW PLANE SHARED VOCABULARY EXECUTION PLANE 1 module 4 modules · pure data 61 modules diagnosis.taxonomyevents.rail_codesevents.schemas diagnosis.llm imported by 2 tools.shadow · wt.shadow neither is an actor actions.executoractions.razorpay_clientactions.commsledger.receipts+ 57 more modules

    No edge crosses either dashed rule, in either direction — 0 paths from diagnosis.llm to anything that acts, and it is unreachable from all 10 execution roots. 111 modules parsed.

    The two planes share types, not paths. That is why the gap holds without a filter in it: the LLM emits an LLMVerdict, the policy plane consumes a Proposal, and no function in the repository converts one into the other. For the model to move money someone would have to write a conversion that does not exist — and an absence cannot have a bug in it, which a validator can.

    The only two modules importing the classifier are the shadow harnesses that score it, and neither can act. Note what the execution plane’s modules include: fifteen guards, of which eleven rest on a statute. The other four — idempotency, the retry cap, the spend cap, the human handoff — are platform constraints and house rules whose statute attribute is None. Calling all fifteen statutory would be the cheapest way to make this page look stronger than it is.

    §2a · the adversary

    EXHIBIT G — What Still Beats It

    The survival criterion was registered before the first attack was written, and judge() is the only thing that can return a verdict. It scans the ledger the way §2a scans — never “a guard returned BLOCKED”. An attack may add evidence requirements; it cannot lower the bar. Each row below carries the SHA-256 of the ledger that attack produced.

    AttackVerdictWhyReceiptsLedger

    do not trust this page · check it

    EXHIBIT H — The Audit Trail

    Recompute a receipt's SHA-256 digest here, in your browser, with the Web Crypto API — from the exact bytes the ledger sealed, which Exhibit I shows. Then forge one byte and watch the digest stop matching: a receipt cannot be edited after the fact without the edit showing.

    > awaiting input...
    one seed's ledger, from genesis

    EXHIBIT I — Trajectory Explorer

    Real receipts from the head of one seed's ledger, shown as the exact byte string each hash was computed over — guard decisions, obligations, statute citations and all. Nothing here is pretty-printed, because reformatting would change the bytes and break the hash. Every digest and every link back to genesis is recomputed in this browser when the page loads.

    > Select a receipt from the ledger...