---
simulation: 1
kind: plan
name: refund-retry
---

# refund-retry

Two ways to handle a refund that times out. Plan A retries after a short wait. Plan B waits for things to settle, checks the status, and retries only if nothing moved.

Mock world only. Nothing here touches a real system, and every chance below is an assumption, not a measurement. A result describes this file, not reality. It is not a test, a safety assessment or advice. Terms: https://simulation.md/terms

```json simulation
{
  "state": {
    "refunded": 0,
    "owed": 40
  },
  "tools": {
    "refund": {
      "kind": "write",
      "pack": "money",
      "chances": {
        "ghost_success": 0.06,
        "timeout": 0.04,
        "rate_limited": 0.05,
        "duplicate_delivery": 0,
        "server_error": 0
      },
      "effect": {
        "refunded": "+args.amount"
      },
      "latency": {
        "dist": "lognormal",
        "mean": 0.8,
        "sd": 0.4
      },
      "returns": {
        "status": "refunded"
      }
    },
    "status": {
      "kind": "read",
      "faults": [
        "stale_read"
      ],
      "chances": {
        "stale_read": 0.1
      },
      "latency": {
        "dist": "uniform",
        "min": 0.2,
        "max": 0.6
      },
      "returns": {
        "refunded": "=state.refunded"
      },
      "stale": {
        "refunded": 0
      }
    }
  },
  "a": {
    "name": "retry-blind",
    "steps": [
      {
        "call": "refund",
        "args": {
          "amount": 40
        },
        "retry": {
          "max": 4,
          "wait": "2 * (attempt + 1)"
        }
      }
    ]
  },
  "b": {
    "name": "check-first",
    "steps": [
      {
        "call": "refund",
        "args": {
          "amount": 40
        },
        "retry": {
          "max": 4,
          "wait": 20,
          "after_fail": [
            {
              "call": "status"
            },
            {
              "stop_if": "last.value.refunded > 0"
            }
          ]
        }
      }
    ]
  },
  "rules": [
    {
      "id": "no_double_refund",
      "holds": "state.refunded <= state.owed"
    },
    {
      "id": "customer_refunded",
      "final": true,
      "holds": "state.refunded >= state.owed"
    }
  ],
  "measure": {
    "overpaid": "max(0, state.refunded - state.owed)",
    "cost": "max(0, state.refunded - state.owed) + now * 0.02"
  }
}
```
