Interlock

Scenario billing_credit: goodwill credit vs a billing run

Generated 2026-09-13 22:40 UTC by experiments/scenario_billing_credit.py. Stripe Billing test mode with test clocks, model claude-haiku-4-5-20251001, every crash a real SIGKILL of the worker process.

crash / outage no_check hand_check interlock
before_send / renewal CREDITED; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof no; 18.1s crash to settled (0.8s after restart) CREDITED; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof yes; 23.7s crash to settled (1.5s after restart) COMMITTED_BY_RETRY via retry-idempotent; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof yes; 41.8s crash to settled (14.0s after restart)
before_send / billing_credit CREDITED; 1 case credit(s), $10 (want 0, $0); VIOLATED; answer matches Stripe; proof no; 19.1s crash to settled (0.4s after restart) REFUSED:stale_premise; 0 case credit(s), $0 (want 0, $0); held; answer matches Stripe; proof yes; 21.4s crash to settled (1.2s after restart) REFUSED:stale_premise_at_recovery; 0 case credit(s), $0 (want 0, $0); held; answer matches Stripe; proof yes; 41.5s crash to settled (27.5s after restart)
before_send / proration CREDITED; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof no; 16.6s crash to settled (0.8s after restart) CREDITED; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof yes; 17.0s crash to settled (1.4s after restart) COMMITTED_BY_RETRY via retry-idempotent; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof yes; 41.0s crash to settled (24.1s after restart)
after_send / renewal REPLAYED_BY_STRIPE; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof no; 15.5s crash to settled (0.4s after restart) FOUND_BY_LOOKUP; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof yes; 15.3s crash to settled (0.3s after restart) COMMITTED_BY_RETRY via retry-idempotent; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof yes; 41.1s crash to settled (20.3s after restart)
after_send / proration REPLAYED_BY_STRIPE; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof no; 13.7s crash to settled (0.3s after restart) FOUND_BY_LOOKUP; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof yes; 14.1s crash to settled (0.8s after restart) COMMITTED_BY_RETRY via retry-idempotent; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof yes; 41.2s crash to settled (27.4s after restart)
before_send / unrelated_credit CREDITED; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof no; 25.7s crash to settled (0.7s after restart) CREDITED; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof yes; 16.5s crash to settled (1.2s after restart) COMMITTED_BY_RETRY via retry-idempotent; 1 case credit(s), $10 (want 1, $10); held; answer matches Stripe; proof yes; 41.1s crash to settled (21.0s after restart)

Rows where hand_check and interlock differ on the invariant: none.

Verdict

Premise candidates, from this run

Each cell read all three candidates from Stripe at decision time, right after the SIGKILL, and right before the restart. Values are cents (customer_balance is negative when the customer is owed). “Refuses” means the value differs from decision time in that many of the row’s cells. The right answer at restart is to refuse exactly when the row wants no credit from this case; a refusal after after_send would contradict a credit that already landed.

crash / outage want customer_balance decision -> crash -> restart compensation_by_others decision -> crash -> restart incident_compensation decision -> crash -> restart
before_send / renewal 1 0 -> 0 -> 0; refuses at restart 0/3; right 0 -> 0 -> 0; refuses at restart 0/3; right 0 -> 0 -> 0; refuses at restart 0/3; right
before_send / billing_credit 0 0 -> 0 -> -1000; refuses at restart 3/3; right 0 -> 0 -> 1000; refuses at restart 3/3; right 0 -> 0 -> 1000; refuses at restart 3/3; right
before_send / proration 1 0 -> 0 -> 0; refuses at restart 0/3; right 0 -> 0 -> 0; refuses at restart 0/3; right 0 -> 0 -> 0; refuses at restart 0/3; right
after_send / renewal 1 0 -> -1000 -> 0; refuses at restart 0/3, would refuse at crash 3/3; right 0 -> 0 -> 0; refuses at restart 0/3; right 0 -> 0 -> 0; refuses at restart 0/3; right
after_send / proration 1 0 -> -1000 -> 0; refuses at restart 0/3, would refuse at crash 3/3; right 0 -> 0 -> 0; refuses at restart 0/3; right 0 -> 0 -> 0; refuses at restart 0/3; right
before_send / unrelated_credit 1 0 -> 0 -> -1000; refuses at restart 3/3; WRONG 0 -> 0 -> 1000; refuses at restart 3/3; WRONG 0 -> 0 -> 0; refuses at restart 0/3; right

Setup

Rows

LLM decisions

Ids, for checking in the Stripe test dashboard

Re-run

ANTHROPIC_API_KEY=... python3 experiments/scenario_billing_credit.py