30 seeds × 400 customers × 45 days · 1,200 revenue-at-risk events per seed · git 9418359
| arm | events | recovered n | recovered | contacts/rec | breaches |
|---|---|---|---|---|---|
| A no intervention | 1,200 | 189 | Rs 4,13,029.36 | 0.00 | 0 |
| B Razorpay default | 1,200 | 600 | Rs 12,85,520.33 | 3.22 | 0 |
| B' volume-matched to C | 1,200 | 460 | Rs 9,81,532.63 | 1.50 | 0 |
| C Control Arm | 1,200 | 586 | Rs 12,49,644.73 | 1.31 | 0 |
| D gate removed | 1,200 | 783 | Rs 16,64,478.16 | 4.28 | 6,371 |
| rail | lift | 95% CI on the delta (Rs/seed) | seeds favouring C | sign test |
|---|---|---|---|---|
card_subscription | -46.0% | [-268,674, -235,741] | 0/30 | p=0.0000 |
emandate | +17.8% | [+25,201, +45,499] | 26/30 | p=0.0001 |
payment_link | +18.5% | [+16,781, +28,907] | 27/30 | p=0.0000 |
upi_autopay | +37.9% | [+141,116, +174,466] | 30/30 | p=0.0000 |
| pooled | -2.8% | [-65,685, -6,950] | 10/30 | p=0.0987 |
The card rail loses because every compliant retry needs its own 24-hour cl.6(a) pre-transaction notice, while Razorpay's documented ladder retries at T+1/T+2/T+3. That is what the framework costs on that rail. This report does not conclude that Razorpay is non-compliant: Arm B encodes documented retry behaviour, and whether it satisfies cl.6(a) is a question about an issuer's notification practice we cannot see.
Arm D is Arm C with one substituted collaborator: the policy gate replaced by a null gate that always allows. It recovers +33.2% more and breaches encoded policy rules 6,371 times per seed. The count is of MY predicates on a synthetic population, and the extra money is granted by MY response model. It is not a legal finding.
pooled: there is no separation to lose — C and B are statistically indistinguishable up to an organic rate of 0.22, and C loses above 0.30. A crossover chart with nothing to cross is an artefact of pooling, and saying so is cheaper than pretending otherwise.
upi_autopay: the advantage decays from +60% to +8% across the swept range and does not cross zero at any swept point up to an organic recovery rate of 0.60, which is far above any plausible value.
Most sensitive to, in order: organic_recovery.base_rate_30d (swept above), response_propensity.insufficient_funds_near_payday and action_response.channel_effectiveness. All three are ASSUMPTION in config/calibration.yaml and say so.
| scenario | what the agent now believes falsely | lift | seeds | invariant violations |
|---|---|---|---|---|
| payday shifted 9 days | the scheduler aligns retries to a payday that is not when money arrives | -5.3% | 4/15 | 0 |
| failure-reason mix inverted | the rarest failure cause is now the most common on every rail | -3.8% | 6/15 | 0 |
| organic recovery doubled | customers self-heal twice as often, so far more 'recoveries' were never wins | -6.6% | 1/15 | 0 |
| downtime signal removed | the payment.downtime side-channel is gone; ISSUER_DOWN cannot fire | -3.8% | 6/15 | 0 |
15 seeds per scenario. Lift is a property of my failure model and degrades when that model is attacked. The violation column reads zero four times. It is computed by src/eval/verify.py, which re-derives the invariants from the audit log rather than asking the policy engine — a checker that consulted the same predicates would report zero under every attack and prove nothing. Only Arm C is shown: Arm B is not gated by our policy, so counting our predicates against Razorpay's cadence would read as a compliance claim we have no standing to make.
136 events per seed escalated to a human, carrying Rs 2,92,844.03 of at-risk revenue. That is the AMBIGUOUS bucket: the four reasons Razorpay itself documents as ones where it “may not have access to specific details regarding the failure reason”. The share was pre-registered at 0.18 before the classifier was written.
11 contacts per seed suppressed because the bank was down, not the customer. Small on purpose: the harness takes one decision instant per logical day, so ISSUER_DOWN can only suppress an outage still open at that instant.
Promises: 14.0 per seed, 66% kept.
Cost per recovered rupee: Rs 0.00. Zero tokens were spent. Tier 0 is deterministic, message drafting runs the template path, and this machine has no Anthropic credentials, so fixtures/llm_cache/ is empty. The accounting reads response.usage and would be non-zero the moment a call is recorded; it is never estimated.