---
title: "Example: timeout after a write"
description: "A historical payment example of a general workflow fault class."
canonical: "https://agenttwin.si/docs/wedge/"
---

# Example: timeout after a write

A historical payment example of a general workflow fault class.


<Info>This historical refund example illustrates a fault class, not AgentTwin’s scope or a shipped Stripe adapter. Hosted custom-collection workflows can test committed-write delays and retries. Stripe fidelity and provider-native conformance remain unimplemented.</Info>

One useful fault class is: **a service completes a write, but the caller times out before receiving the response**. The caller retries, and the action happens twice.

## The scenario

A worker asks a payment provider to create a refund. The provider commits the write, but the response times out. The worker cannot tell whether the refund happened and retries. A weak implementation creates a second refund. A resilient implementation uses an idempotency key and verifies final state.

```mermaid
sequenceDiagram
    participant W as Worker
    participant P as Payment provider
    W->>P: Create refund (idempotency key)
    P->>P: Commit refund
    P--xW: Response lost (timeout)
    W->>P: Retry create refund
    Note over W,P: Weak: a second refund. Resilient: the key returns the original.
```

## What the scenario declares

| | |
| --- | --- |
| **Starting state** | One eligible payment, zero refunds. |
| **Injected condition** | Commit refund; discard response; allow retry. |
| **Expected outcome** | Exactly one refund record, one customer notification, a recoverable trace. |
| **Regression artifact** | Versioned starting state, fault point, input, and assertions. |

## Why this wedge

- **Real** — this failure class appears in production integrations, not only in theory.
- **Costly** — a duplicate refund moves money and damages trust.
- **Hard to reproduce** — it needs a write that commits and a response that never arrives.
- **Testable without risk** — a developer should be able to reproduce it without credentials, network calls, or a real account.

<Warning>
  The CLI runtime supports all four fault classes — timeout-after-write, blackhole, server-error and duplicate-write — across the supported HTTP twins (custom JSON collections, Slack, Stripe, and the thirteen vendored twins). Seeded fault scheduling applies to every class except the declared `timeout-after-write` delay, which stays the user's declared spec. Hosted sandbox runs accept faults too, but a run seed and seed matrices are currently local-runtime only. Prepare and submit your actual workflow code to inspect the write count and fault activation. A custom collection test does not verify Stripe behavior.
</Warning>

## What a reproduced failure buys you

The fault is seeded: from one seed string, trials deterministically derive
which collection faults and how long the response is withheld (plus an
unfaulted control trial). Seeds cover fault selection and delays, not OS
scheduling. A saved trial becomes a named regression you can require in CI,
so the fix is proven against unchanged checks and the failing trial carries a
seed coordinate in its receipt. In buyer's terms: "first failure at seed
000000008f2c41d9, reproduced exactly" replaces "the refunds double sometimes;
we could never reproduce it."

## The seeded fault classes

Four fault classes ship today, each named by the loss it costs, never the
mechanism. The run seed decides *which* matched requests get faulted, so the
same seed plus the same fingerprint recreates the same schedule:

- **Timeout after write** — the write commits, the answer vanishes mid-charge;
  a weak retry turns one refund into two. The declared `delayMs` is
  application-visible spec, never seeded.
- **Blackhole** — the service goes dark mid-charge; no response at all, ever;
  the caller never learns whether the write committed.
- **Server error** — the dependency starts returning 500s (seeded rejections
  and an optional seeded delay window), so error paths get exercised.
- **Duplicate write** — two refunds hit at the same time; duplication itself,
  not a lost response inviting a retry. An idempotency key suppresses it, which
  is exactly the property worth asserting.

Further fault types are deliberately rejected by today's schemas rather than
silently ignored, and an untriggered requested fault makes a run incomplete.
