
How to verify cancellation invariants before production runs, and what a production comparison design pattern actually needs.
The measured part is a rehearsal receipt from October 18: 27 declared checks against Stripe, Zendesk, Square, and Slack twins for the Acme demo cancellation flow. The proposed part is a design pattern for comparing a production run against that receipt. Both are useful, and I want to keep them clearly separate.
What the rehearsal measured
Agent-assisted cancellation workflows have more state than the happy path suggests. The approval gate alone produces at least four conditions worth verifying separately: parking an approval without triggering cancellation, processing an approved continuation, rejecting without changing subscription state, and restarting a parked app before completing the approval. The October 18 Acme demo run measured all four.
| Measured check | Rehearsal result |
|---|---|
| No cancellation while approval parked | Held |
| Approved continuation cancels subscription | Held |
| Rejection leaves subscription unchanged | Held |
| Parked app restart then cancels once when approved | Held |
Application state is app-reported from behind a relay, not certified from an independent data store. Customer email notifications went through a simulated mailbox, not live SMTP. The receipt attests to what the application reported at those checkpoints. The full run: https://fetchsandbox.com/runs/70cabf96ee?flow=vr_750a50af079849668b6b40d4085c41ef.
Stripe mechanics this pattern assumes
Stripe offers two cancellation paths. Immediate cancellation sets status: canceled right away. Setting cancel_at_period_end: true keeps the subscription active until the billing period closes. This pattern uses immediate cancellation throughout. A workflow using period-end scheduling will show a different state at the same checkpoint. That is not a defect, it is a different policy.
Webhook event ordering is not guaranteed. Stripe recommends retrieving the latest object state from the API rather than relying on payload sequence. Any comparison approach that keys on event arrival order will mislead when sequences vary between runs.
Reconciliation as a design pattern
The rehearsal receipt records what the workflow measured on a specific date, against a specific scenario, using specific twins. It is a versioned reference, not ground truth for all future production runs.
A production comparison that means something needs several things in place. The rehearsal and production workflow versions need to be known and comparable. The scenario type needs to apply: a subscription on a different billing cycle or eligibility path may not satisfy the Acme demo invariants. Comparison needs to operate on normalized business invariants, not raw field equality. Stripe IDs and timestamps differ across runs. Status fields, refund counts, and presence of expected records at named checkpoints are the stable signals. Missing observations, stale captures, or partial traces should return inconclusive rather than pass.
When a diff appears, it localizes a boundary where something diverged. It does not tell you why. A planned policy change from immediate to period-end cancellation will show up as a difference. That difference is worth investigating, not automatically categorizing as a bug.
The comparison layer described here is not built. Integrating live observers, correlating runs by business key, and building the diff logic is a separate project.
What the receipt gives you today
If a production incident surfaces, your team has a timestamped reference for what expected behavior looked like when you shipped. Support can look at what the rehearsal declared and what agreed. That is useful before any automated comparison exists.
Connect your agent
Start by preserving a receipt from day one. Connect your coding agent at https://fetchsandbox.com/mcp and ask it to verify state before approval, after approval, and after retry. Keep the receipt. That is the foundation a comparison layer would build on.