Beat 1
It wanted to charge. The gate said no. It got paid anyway.
The whole thesis in one frame: a calibrated prior said retry, a named RBI clause said not yet, and the rebooked debit was paid. Zero model calls, by design -- this frame survives every cut on the cut list.
$ RECOVERY_LLM=replay python -m recovery replay evt_10427 --explain evt_10427 cust_3391 sub_88213 "Vaayu Fitness" Rs 4,999 method=upi_autopay payment.failed error_source=customer error_step=payment_debit_response error_reason=insufficient_funds platform_retries=0 notification object present on the order -> Razorpay does not retry; the sequencer is ours tier0 INSUFFICIENT_FUNDS deterministic, 0 tokens prior p(recover | retry today) = 0.71 calibration.yaml:34 [source: url] propose RETRY_DEBIT Rs 4,999 today 20:40 IST proposer=tier0.v3 policy 17 rules evaluated, 2 fired: UPI_AUTOPAY_EXECUTION_WINDOW MODIFY sev 25 20:40 in blocked 17:00-21:30 NPCI UPI OC-215, 21 May 2025 (as reported) PREDEBIT_NOTICE_NOT_MATURE DENY sev 88 notice 4h old, 24h required RBI/DPSS/2026-27/396 cl.6(a), 21 Apr 2026 binds=issuer -> reaches us via cl.10(c) VERDICT DENY rule=PREDEBIT_NOTICE_NOT_MATURE (strictest of 2) EXECUTOR refused. 0 debits. 0 messages. audit=rec_00918 REPLAN SEND_PRE_DEBIT_NOTICE 20:41 -> matures 2026-09-01 06:12 RETRY_DEBIT Rs 4,999 2026-09-01 13:40 IST (permitted 13:00-17:00) -> rec_01204 2026-09-01 13:40 subscription.charged Rs 4,999 RECOVERED frame: 23/30 rows, max col 79/80 fits
Beat 3 · the counterfactual
What it recovered, against Razorpay's own documented behaviour
Read the rails, not the pool. Arm B is a different incumbent on every rail, and pooling averages over four of them. Pooled, this system is slightly behind — that row stays on the page.
| rail | lift | 95% CI on the delta (Rs/seed) | seeds favouring C | sign test | contacts |
|---|---|---|---|---|---|
card_subscription | -46.0% | [-268,674, -235,741] | 0/30 | p=0.0000 | 0.27x |
emandate | +17.8% | [+25,201, +45,499] | 26/30 | p=0.0001 | 0.41x |
payment_link | +18.5% | [+16,781, +28,907] | 27/30 | p=0.0000 | 0.22x |
upi_autopay | +37.9% | [+141,116, +174,466] | 30/30 | p=0.0000 | 0.70x |
| pooled | -2.8% | [-65,685, -6,950] | 10/30 | p=0.0987 | 0.40x |
| rule breached | per seed | ||||
| --- | ---: | ||||
COOLDOWN_ACTIVE | 2,880 | ||||
OUTREACH_BUDGET | 2,655 | ||||
CONSENT_REVOKED_90D | 258 | ||||
TCCCPR_BLOCKED_PREFERENCE | 241 | ||||
PROMISE_ACTIVE | 220 | ||||
DISPUTE_HOLD | 60 | ||||
LADDER_MONOTONE | 46 | ||||
ISSUER_DOWN | 12 |
Read from docs/RESULTS.md, which src/eval/headline.py generates from a real 30-seed run and tests/unit/test_results_doc.py regenerates and diffs. No number on this page was typed by hand.
| arm | events | recovered n | recovered | contacts/rec | channel spend | breaches |
|---|---|---|---|---|---|---|
| A no intervention (control) | 1,200 | 189 | Rs 4,13,029.36 | 0.00 | Rs 0 | 0 |
| B Razorpay default (per rail) | 1,200 | 600 | Rs 12,85,520.33 | 3.22 | Rs 333.55 | 0 |
| B' volume-matched to C | 1,200 | 460 | Rs 9,81,532.63 | 1.50 | Rs 123.02 | 0 |
| C Control Arm | 1,200 | 586 | Rs 12,49,644.73 | 1.31 | Rs 310.22 | 0 |
| D gate removed | 1,200 | 783 | Rs 16,64,478.16 | 4.28 | Rs 43,363.43 | 6,371 |
| rail | lift | 95% CI on the delta (Rs/seed) | seeds favouring C | sign test | contacts | |
| --- | ---: | --- | ---: | ---: | ---: | |
card_subscription | -46.0% | [-268,674, -235,741] | 0/30 | p=0.0000 | 0.27x | |
emandate | +17.8% | [+25,201, +45,499] | 26/30 | p=0.0001 | 0.41x | |
payment_link | +18.5% | [+16,781, +28,907] | 27/30 | p=0.0000 | 0.22x | |
upi_autopay | +37.9% | [+141,116, +174,466] | 30/30 | p=0.0000 | 0.70x | |
| pooled | -2.8% | [-65,685, -6,950] | 10/30 | p=0.0987 | 0.40x | |
| rule breached | per seed | |||||
| --- | ---: | |||||
COOLDOWN_ACTIVE | 2,880 | |||||
OUTREACH_BUDGET | 2,655 | |||||
CONSENT_REVOKED_90D | 258 | |||||
TCCCPR_BLOCKED_PREFERENCE | 241 | |||||
PROMISE_ACTIVE | 220 | |||||
DISPUTE_HOLD | 60 | |||||
LADDER_MONOTONE | 46 | |||||
ISSUER_DOWN | 12 |
Arm D is this same agent with the gate removed. Its last column counts breaches of encoded policy rules — my predicates on my population, not violations of law. That is the price of the gate, measured rather than asserted.
Beat 2
The rule ladder, with citations
Seventeen rules evaluated against one deliberately aggressive proposal. Every rule that fires prints its basis and its citation, and the verdict is the strictest one -- DENY over MODIFY over ALLOW.
$ recovery explain --proposal tests/fixtures/aggressive_retry.json evt_10427 cust_3391 "Vaayu Fitness" Rs 4,999 method=upi_autopay propose RETRY_DEBIT Rs 4,999 2026-09-01 20:40 IST proposer=tier0.v3 policy 17 rules evaluated, 2 fired: UPI_AUTOPAY_EXECUTION_WINDOW MODIFY sev 25 20:40 in blocked 17:00-21:30 NPCI UPI circular OC-215 (as reported) PREDEBIT_NOTICE_NOT_MATURE DENY sev 88 notice 4h old, 24h required RBI/DPSS/2026-27/396 cl.6(a), 21 Apr 2026 binds=issuer -> reaches us via cl.10(c) VERDICT DENY rule=PREDEBIT_NOTICE_NOT_MATURE (strictest of 2) EXECUTOR refused. 0 debits. 0 messages.
One webhook in, the whole chain out
The case pack
Seven real Razorpay payment.failed envelopes, parsed by the same ingest the harness uses. A webhook carries no consent, no preference and none of our history, so every frame prints what it assumed before it prints a verdict.
insufficient funds upi
Tier 0 proposes the NOTICE, not the retry. The cl.6(a) pre-debit notice needs 24 hours to mature, so on this event a retry is not yet an action the proposer is allowed to want.
notice matured debit
The gate does not refuse the debit -- it moves it. NPCI's permitted window puts the retry at 13:00, and MODIFY returns a complete replacement proposal rather than patching a field.
opt out absorbing
Opt-out is absorbing: no expiry, no cooling-off, no exception, on any channel, for the rest of the run.
issuer down
The bank is down, not the customer. The contact is suppressed and the mandate attempt is NOT consumed -- spending one here would cost a retry the customer never had a chance to pass.
ambiguous handoff
Escalation here means intrusiveness. HUMAN_HANDOFF is a rung, and the ladder is monotone: it cannot be reached by skipping one.
inferred consent lapsed
Same bytes, opposite verdict. FULLY_BLOCKED alone stays lawful for service under inferred consent (TRAI memorandum para 39); it is the DISCHARGED contract that lapses it under DPDP s.7(a).
out of enum source
Razorpay's own netbanking sample carries error_source=bank, which is not in their documented list. The parser tolerates it instead of crashing on the vendor's own payload.
$ recovery try --event tests/fixtures/cases/01_insufficient_funds_upi.json INGEST payment.failed pay_QxK1insuff01 Rs 3,081 signature not checked -- pass --signature to verify rail upi_autopay surface=MANDATE_DEBIT_FAILURE error BAD_REQUEST_ERROR / customer / payment_debit_response / insufficient_funds ASSUMED a webhook carries none of this; --context replaces it consent=INFERRED preference=UNREGISTERED (default) contract=SUBSISTING lender=false gates the 08:00-19:00 window history 0 contacts, 0 debits, pre-debit notice: none now 2026-08-31 12:10 IST (the webhook created_at) CLASSIFY tier0.v3 INSUFFICIENT_FUNDS keyed on the 4-tuple, never the reason alone: upi_autopay / customer / payment_debit_response / insufficient_funds PROPOSE 1 proposal pay_QxK1insuff01 cust_3391 "Vaayu Fitness" Rs 3,081 method=upi_autopay propose SEND_PRE_DEBIT_NOTICE Rs 3,081 2026-08-31 12:10 IST proposer=tier0.v3 policy 17 rules evaluated, 0 fired: VERDICT ALLOW rule=- (strictest of 0) EXECUTOR SEND_PRE_DEBIT_NOTICE channel=sms rung=SMS Namaste, Vaayu Fitness ka payment Rs 3,081 complete nahi ho paya kyunki account mein balance kam tha. Kripya apne account se dobara try karein. Reply STOP to opt out. I7 ok I8 SERVICE_INFERRED SUMMARY 1 executed, 0 refused, 0 debit(s), 1 message(s) 2 audit records, in memory only dry-run port: nothing left this machine. This frame shows what the system decides, never whether the customer pays -- that number needs a counterfactual, which is `make eval`. fixtures/llm_cache is empty; drafting is the template path.
$ recovery try --event tests/fixtures/cases/02_notice_matured_debit.json INGEST payment.failed pay_QxK2matured02 Rs 3,081 signature not checked -- pass --signature to verify rail upi_autopay surface=MANDATE_DEBIT_FAILURE error BAD_REQUEST_ERROR / customer / payment_debit_response / insufficient_funds ASSUMED a webhook carries none of this; --context replaces it consent=INFERRED preference=UNREGISTERED (default) contract=SUBSISTING lender=false gates the 08:00-19:00 window history 0 contacts, 0 debits, pre-debit notice: 30 Aug 10:10 now 2026-08-31 12:10 IST (the webhook created_at) CLASSIFY tier0.v3 INSUFFICIENT_FUNDS keyed on the 4-tuple, never the reason alone: upi_autopay / customer / payment_debit_response / insufficient_funds PROPOSE 1 proposal pay_QxK2matured02 cust_3391 "Vaayu Fitness" Rs 3,081 method=upi_autopay propose RETRY_DEBIT Rs 3,081 2026-09-01 12:10 IST proposer=tier0.v3 policy 17 rules evaluated, 1 fired: UPI_AUTOPAY_EXECUTION_WINDOW MODIFY sev 25 12:10 in blocked 10:00-13:00 NPCI UPI circular OC-215 (as reported) VERDICT MODIFY rule=UPI_AUTOPAY_EXECUTION_WINDOW (strictest of 1) REPLACEMENT RETRY_DEBIT 2026-09-01 13:00 IST rung=SILENT_RETRY purpose=TRANSACTIONAL a complete proposal, never a patch EXECUTOR RETRY_DEBIT channel=none rung=SILENT_RETRY SUMMARY 1 executed, 0 refused, 1 debit(s), 0 message(s) 2 audit records, in memory only dry-run port: nothing left this machine. This frame shows what the system decides, never whether the customer pays -- that number needs a counterfactual, which is `make eval`. fixtures/llm_cache is empty; drafting is the template path.
$ recovery try --event tests/fixtures/cases/03_opt_out_absorbing.json INGEST payment.failed pay_QxK3optout03 Rs 872 signature not checked -- pass --signature to verify rail card_subscription surface=MANDATE_DEBIT_FAILURE error BAD_REQUEST_ERROR / issuer_bank / payment_authorization / debit_instrument_blocked ASSUMED a webhook carries none of this; --context replaces it consent=INFERRED preference=UNREGISTERED (default) contract=SUBSISTING lender=false gates the 08:00-19:00 window history 0 contacts, 0 debits, pre-debit notice: none now 2026-08-31 12:10 IST (the webhook created_at) CLASSIFY tier0.v3 INSTRUMENT_BLOCKED keyed on the 4-tuple, never the reason alone: card_subscription / issuer_bank / payment_authorization / debit_instrument_blocked PROPOSE 1 proposal pay_QxK3optout03 cust_1102 "Setu Learning" Rs 872 method=card_subscription propose SEND_REAUTH_LINK Rs 872 2026-08-31 12:10 IST proposer=tier0.v3 policy 17 rules evaluated, 1 fired: CONSENT_REVOKED_90D DENY sev 100 consent revoked 40d ago; opt-out is absorbing TCCCPR 2018, 1 Feb 2025 VERDICT DENY rule=CONSENT_REVOKED_90D (strictest of 1) EXECUTOR refused. 0 debits. 0 messages. SUMMARY 0 executed, 1 refused, 0 debit(s), 0 message(s) 1 audit records, in memory only dry-run port: nothing left this machine. This frame shows what the system decides, never whether the customer pays -- that number needs a counterfactual, which is `make eval`. fixtures/llm_cache is empty; drafting is the template path.
$ recovery try --event tests/fixtures/cases/04_issuer_down.json INGEST payment.failed pay_QxK4issuer04 Rs 2,741 signature not checked -- pass --signature to verify rail upi_autopay surface=MANDATE_DEBIT_FAILURE error GATEWAY_ERROR / issuer_bank / payment_authorization / bank_technical_error ASSUMED a webhook carries none of this; --context replaces it consent=INFERRED preference=UNREGISTERED (default) contract=SUBSISTING lender=false gates the 08:00-19:00 window history 0 contacts, 0 debits, pre-debit notice: none now 2026-08-31 12:10 IST (the webhook created_at) CLASSIFY tier0.v3 ISSUER_DOWN keyed on the 4-tuple, never the reason alone: upi_autopay / issuer_bank / payment_authorization / bank_technical_error PROPOSE 1 proposal pay_QxK4issuer04 cust_1000 "Neelam Grocers" Rs 2,741 method=upi_autopay propose SEND_MESSAGE Rs 2,741 2026-08-31 12:10 IST proposer=tier0.v3 policy 17 rules evaluated, 1 fired: ISSUER_DOWN DENY sev 70 down_HDFC_04 severity=high covers bank=HDFC,vpa=neelam@okhdfcbank; the bank is down, not the customer https://razorpay.com/docs/webhooks/payloads/ VERDICT DENY rule=ISSUER_DOWN (strictest of 1) EXECUTOR refused. 0 debits. 0 messages. SUMMARY 0 executed, 1 refused, 0 debit(s), 0 message(s) 1 audit records, in memory only dry-run port: nothing left this machine. This frame shows what the system decides, never whether the customer pays -- that number needs a counterfactual, which is `make eval`. fixtures/llm_cache is empty; drafting is the template path.
$ recovery try --event tests/fixtures/cases/05_ambiguous_handoff.json INGEST payment.failed pay_QxK5ambig05 Rs 946 signature not checked -- pass --signature to verify rail card_subscription surface=MANDATE_DEBIT_FAILURE error BAD_REQUEST_ERROR / customer / payment_authentication / payment_failed ASSUMED a webhook carries none of this; --context replaces it consent=INFERRED preference=UNREGISTERED (default) contract=SUBSISTING lender=false gates the 08:00-19:00 window history 0 contacts, 0 debits, pre-debit notice: none now 2026-08-31 12:10 IST (the webhook created_at) CLASSIFY tier0.v3 AMBIGUOUS keyed on the 4-tuple, never the reason alone: card_subscription / customer / payment_authentication / payment_failed Razorpay documents this reason as one it may not know PROPOSE 1 proposal pay_QxK5ambig05 cust_2210 "Kadak Coffee Club" Rs 946 method=card_subscription propose HUMAN_HANDOFF Rs 946 2026-08-31 12:10 IST proposer=tier0.v3 policy 17 rules evaluated, 1 fired: LADDER_MONOTONE MODIFY sev 55 first contact at HUMAN_HANDOFF; ladder starts at SMS BUSINESS default -- no citation, by schema VERDICT MODIFY rule=LADDER_MONOTONE (strictest of 1) REPLACEMENT HUMAN_HANDOFF 2026-08-31 12:10 IST rung=SMS purpose=SERVICE_INFERRED a complete proposal, never a patch EXECUTOR HUMAN_HANDOFF channel=sms rung=SMS SUMMARY 1 executed, 0 refused, 0 debit(s), 1 message(s) 2 audit records, in memory only dry-run port: nothing left this machine. This frame shows what the system decides, never whether the customer pays -- that number needs a counterfactual, which is `make eval`. fixtures/llm_cache is empty; drafting is the template path.
$ recovery try --event tests/fixtures/cases/06_inferred_consent_lapsed.json INGEST payment.failed pay_QxK6blocked06 Rs 3,844 signature not checked -- pass --signature to verify rail upi_autopay surface=MANDATE_DEBIT_FAILURE error BAD_REQUEST_ERROR / business / payment_initiation / mandate_expired ASSUMED a webhook carries none of this; --context replaces it consent=INFERRED preference=FULLY_BLOCKED contract=DISCHARGED lender=false gates the 08:00-19:00 window history 0 contacts, 0 debits, pre-debit notice: none now 2026-08-31 12:10 IST (the webhook created_at) CLASSIFY tier0.v3 MANDATE_DEAD keyed on the 4-tuple, never the reason alone: upi_autopay / business / payment_initiation / mandate_expired PROPOSE 1 proposal pay_QxK6blocked06 cust_1384 "Tarang Music" Rs 3,844 method=upi_autopay propose SEND_REAUTH_LINK Rs 3,844 2026-08-31 12:10 IST proposer=tier0.v3 policy 17 rules evaluated, 1 fired: TCCCPR_BLOCKED_PREFERENCE DENY sev 86 inferred consent lapsed: contract discharged TCCCPR 2018 cl.(z) as substituted 2025, 1 Fe VERDICT DENY rule=TCCCPR_BLOCKED_PREFERENCE (strictest of 1) EXECUTOR refused. 0 debits. 0 messages. SUMMARY 0 executed, 1 refused, 0 debit(s), 0 message(s) 1 audit records, in memory only dry-run port: nothing left this machine. This frame shows what the system decides, never whether the customer pays -- that number needs a counterfactual, which is `make eval`. fixtures/llm_cache is empty; drafting is the template path.
$ recovery try --event tests/fixtures/cases/07_out_of_enum_source.json INGEST payment.failed pay_QxK7outenum07 Rs 1,250 signature not checked -- pass --signature to verify rail payment_link surface=PAYMENT_FAILURE error BAD_REQUEST_ERROR / beneficiary_bank / payment_authorization / payment_failed ASSUMED a webhook carries none of this; --context replaces it consent=INFERRED preference=UNREGISTERED (default) contract=SUBSISTING lender=false gates the 08:00-19:00 window history 0 contacts, 0 debits, pre-debit notice: none now 2026-08-31 12:10 IST (the webhook created_at) CLASSIFY tier0.v3 AMBIGUOUS keyed on the 4-tuple, never the reason alone: payment_link / beneficiary_bank / payment_authorization / payment_failed Razorpay documents this reason as one it may not know error_source 'beneficiary_bank' is out-of-enum and was tolerated PROPOSE 1 proposal pay_QxK7outenum07 cust_4407 "Chhaya Interiors" Rs 1,250 method=payment_link propose HUMAN_HANDOFF Rs 1,250 2026-08-31 12:10 IST proposer=tier0.v3 policy 17 rules evaluated, 1 fired: LADDER_MONOTONE MODIFY sev 55 first contact at HUMAN_HANDOFF; ladder starts at SMS BUSINESS default -- no citation, by schema VERDICT MODIFY rule=LADDER_MONOTONE (strictest of 1) REPLACEMENT HUMAN_HANDOFF 2026-08-31 12:10 IST rung=SMS purpose=SERVICE_INFERRED a complete proposal, never a patch EXECUTOR HUMAN_HANDOFF channel=sms rung=SMS SUMMARY 1 executed, 0 refused, 0 debit(s), 1 message(s) 2 audit records, in memory only dry-run port: nothing left this machine. This frame shows what the system decides, never whether the customer pays -- that number needs a counterfactual, which is `make eval`. fixtures/llm_cache is empty; drafting is the template path.
Beat 6
Five messages that cannot invent a number
Slot-constrained generation: every numeral in every body traces to a validated slot value. I7 rejects any numeral -- ASCII or Devanagari -- that is not in the slots.
$ make outreach RECOVERY_LLM=replay · drafter=template.v1 · 0 tokens · 0 network calls RCV_FAIL_01 ISSUER_DOWN ok Namaste, Neelam Grocers ka payment Rs 2,741 complete nahi ho paya kyunki bank ki taraf se technical dikkat thi. Kripya apne account se dobara try karein. Reply STOP to opt out. slots: merchant=Neelam Grocers, amount=Rs 2,741, reason=bank ki taraf se technical dikkat thi RCV_FAIL_01 INSUFFICIENT_FUNDS ok Namaste, Vaayu Fitness ka payment Rs 3,081 complete nahi ho paya kyunki account mein balance kam tha. Kripya apne account se dobara try karein. Reply STOP to opt out. slots: merchant=Vaayu Fitness, amount=Rs 3,081, reason=account mein balance kam tha RCV_FAIL_01 PLATFORM_ERROR ok Namaste, Kadak Coffee Club ka payment Rs 946 complete nahi ho paya kyunki ek technical error aa gaya tha. Kripya apne account se dobara try karein. Reply STOP to opt out. slots: merchant=Kadak Coffee Club, amount=Rs 946, reason=ek technical error aa gaya tha RCV_MANDATE_02 MANDATE_DEAD ok Namaste, Tarang Music ke liye aapka auto-pay Rs 3,844 nahi ho paya kyunki aapka mandate ab active nahi hai. Mandate resume karke dobara try karein. Reply STOP to opt out. slots: merchant=Tarang Music, amount=Rs 3,844, reason=aapka mandate ab active nahi hai RCV_FAIL_01 INSTRUMENT_BLOCKED ok Namaste, Setu Learning ka payment Rs 872 complete nahi ho paya kyunki aapka card ya account block hai. Kripya apne account se dobara try karein. Reply STOP to opt out. slots: merchant=Setu Learning, amount=Rs 872, reason=aapka card ya account block hai 5 messages · every numeral traced to a slot · 0 validation failures
Beat 7
The word 'off' flips the verdict
A promotional token reclassifies a Service message as Promotional, which costs it its inferred-consent basis. The model is overruled and made to regenerate.
$ make outreach --adversarial draft "Namaste, Neelam Grocers ka payment Rs 2,741 complete… …. Aur aaj hi pay karein to 10% off bhi milega!" drafter=injected.fixture.v1 model=None I7 GROUNDED_LANGUAGE FAIL ['10'] not in slot values I8 CONTENT_CLASS Service -> PROMOTIONAL (TCCCPR cl.(au) second proviso) consent_basis=inferred · recipient=fully_blocked VERDICT DENY rule=TCCCPR_PROMO_CONTAMINATION 0 messages sent regenerate -> clean draft -> ALLOW -> sent 11:04 IST Namaste, Neelam Grocers ka payment Rs 2,741 complete…Reply STOP to opt out. the word 'off' flipped the verdict. Not a silent strip: a DENY, with the clause.
Beat 8
Every rung, and the silence after a promise
The ladder is monotone and VOICE is disabled by config, so it never executes. Then she names a date -- and the system does nothing at all for three days. That is the feature.
$ recovery demo-ladder evt_10004 cust_1000 "Neelam Grocers" Rs 2,741 rung verdict rule | SILENT_RETRY ALLOW || SMS ALLOW ||| WHATSAPP ALLOW |||| VOICE DENY LADDER_MONOTONE VOICE is disabled by config channel disabled by config; never executes ||||| HUMAN_HANDOFF DENY OUTREACH_BUDGET 105p spent + 5000p exceeds the 1500p rolling |||||| WRITE_OFF ALLOW she replies "parso pay kar dunga" day +0 SEND_MESSAGE DENY PROMISE_ACTIVE day +2 SEND_MESSAGE DENY PROMISE_ACTIVE day +4 SEND_MESSAGE ALLOW when she names a date, the system does nothing at all for three days. That is the feature.
Chaos
The same webhook three times, one charge
Razorpay retries a webhook for 24 hours, so duplicates are the normal case rather than the fault case. Each duplicate is logged explicitly, never silently dropped.
$ make chaos-duplicate -- Razorpay retries for 24h; duplicates are the normal case rec_00001 EXECUTION PENDING rec_00002 OUTCOME FAILED rec_00003 OUTCOME NO_OP_DUPLICATE rec_00004 OUTCOME NO_OP_DUPLICATE NO_OP idempotency_key=d025a36be553d6ef0583aa6b... charges=1 audit_records=4 2 duplicate deliveries logged explicitly, never silently dropped
Beat 11
Nine guarantees, each one a test
Every row runs its own test file and prints the generated case count. The property rows are 500 Hypothesis examples each, derandomised -- a repo whose headline claim is determinism cannot have a random suite.
GUARANTEES -- each one is a test, not a claim id statement proved by cases ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ I1 AUTHORITY no execution without a invariants/test_i1_authority. - PASS matching ALLOW verdict py I2 CONSENT opt-out is absorbing, forever, invariants/test_i2_consent_ab 500 PASS every channel sorbing.py I3 TIME_WINDOW every contact inside its invariants/test_i3_time_windo 500 PASS permitted time window w.py I4 DEBIT_ELIG never retry a debit that invariants/test_i4_debit_elig 500 PASS cannot succeed ibility.py I5 BOUNDED contacts capped, cooled, invariants/test_i5_bounded_es 500 PASS budgeted, ladder never skips calation.py I6 EXACTLY_ONCE duplicates and crashes change invariants/test_i6_exactly_on 500 PASS no canonical projection ce.py I7 GROUNDED no number in output absent invariants/test_i7_grounded_l 500 PASS from input anguage.py I8 CONTENT_CLASS promotional contamination invariants/test_i8_content_cl - PASS downgrades consent and denies ass.py I9 SIM_FIREWALL the agent cannot import the arch/test_i9_sim_firewall.py - PASS world import firewall src.agent -> src.sim edges PASS calibration 44/44 parameters carry a source URL PASS policy provenance 17/17 rules carry valid provenance PASS lender rule RECOVERY_AGENT_HOURS does not fire for a payment aggregator PASS 9/9 invariants proved | rules implemented: 17/17 | RECOVERY_LLM=replay
Reproduce
Every frame on this page, from a clean clone
No API key is needed. Model responses are a committed hash-keyed cache and RECOVERY_LLM=replay is the default, which hard-fails on a cache miss rather than quietly calling out.
make prove # the nine guarantees, each one a test make cases # the seven-case pack, back to back make eval SEEDS=30 # the arm table and the per-rail lift make report # out/report.html, the measurement report make walkthrough # this page
This page is regenerated by src/ui/walkthrough.py and pinned by tests/unit/test_walkthrough.py, which re-renders every exhibit and diffs it against the committed copy. A stale page is a failing test, not a file that still opens.