{
  "id": "bug-zoo",
  "version": 1,
  "title": "Bug Zoo",
  "description": "Four tiny retry failures to replay, reuse as regression tests, and reduce into sharper counterexamples.",
  "datePublished": "2026-09-06",
  "scope": "Authored synthetic exhibits for Retry Proof v1. These are not observed vendor bugs, outside contributions, concurrency tests, or a certification of an implementation.",
  "license": "https://boner.pics/bug-zoo/LICENSE.txt",
  "evidence": "authored_synthetic_fixtures",
  "goldenTraceProvenance": "Hand-specified observations, checked against Retry Proof v1 at build time.",
  "contract": {
    "id": "retry-proof",
    "version": 1,
    "ttlTicks": 6,
    "maxTick": 24,
    "maxSteps": 12,
    "bodyBytes": 3072,
    "keys": [
      "A",
      "B"
    ],
    "values": [
      "A",
      "B"
    ],
    "deliveries": [
      "received",
      "lost"
    ],
    "targets": [
      "duplicate-write",
      "body-blind",
      "sliding-expiry",
      "forget-on-loss"
    ],
    "semantics": {
      "clock": "Nondecreasing integer ticks. A key expires at creation tick + 6; a request at the deadline can create a new write.",
      "create": "Commit one write, cache body and receipt ordinal, return 201. Receipt ordinals start at 1 and increase once per committed write.",
      "replay": "Before expiry, the same key and body return 200 with the original receipt and no write. The deadline does not move.",
      "conflict": "Before expiry, the same key with a different body returns 409 with a null receipt and no write.",
      "loss": "Lost delivery happens after the service finishes: caller status and receipt are null, but committed writes and durable key state survive.",
      "observations": "Each step exposes status, receipt and independently measured cumulative committedWrites. Keys are independent.",
      "mutants": {
        "duplicate-write": "A matching unexpired replay commits another write and returns 201 with its new receipt; it retains the original expiry.",
        "body-blind": "An unexpired key returns 200 and its original receipt even when the body differs.",
        "sliding-expiry": "A matching unexpired replay extends the deadline to this request tick + 6.",
        "forget-on-loss": "A successful create or replay followed by lost delivery deletes its cached key record after committing."
      }
    }
  },
  "cases": [
    {
      "slug": "duplicate-write",
      "title": "The double-tap write",
      "subtitle": "One intent. Two commits.",
      "searchTitle": "Duplicate writes on retry: an idempotency regression test",
      "summary": "A retry of the same key and body commits again, even though the first result is still cached.",
      "target": "duplicate-write",
      "color": "pink",
      "glyph": "⧉",
      "artifact": {
        "target": "duplicate-write",
        "steps": [
          {
            "at": 0,
            "key": "B",
            "value": "B",
            "delivery": "received"
          },
          {
            "at": 1,
            "key": "A",
            "value": "A",
            "delivery": "received"
          },
          {
            "at": 2,
            "key": "A",
            "value": "A",
            "delivery": "received"
          },
          {
            "at": 3,
            "key": "B",
            "value": "A",
            "delivery": "received"
          }
        ]
      },
      "expected": {
        "reference": [
          {
            "status": 201,
            "receipt": 1,
            "committedWrites": 1
          },
          {
            "status": 201,
            "receipt": 2,
            "committedWrites": 2
          },
          {
            "status": 200,
            "receipt": 2,
            "committedWrites": 2
          },
          {
            "status": 409,
            "receipt": null,
            "committedWrites": 2
          }
        ],
        "faulty": [
          {
            "status": 201,
            "receipt": 1,
            "committedWrites": 1
          },
          {
            "status": 201,
            "receipt": 2,
            "committedWrites": 2
          },
          {
            "status": 201,
            "receipt": 3,
            "committedWrites": 3
          },
          {
            "status": 409,
            "receipt": null,
            "committedWrites": 3
          }
        ]
      },
      "explanation": [
        "Operation 2 creates A. Operation 3 repeats the same intent before expiry. The reference returns receipt 2 without a write; the duplicate-write model creates receipt 3. The independent B operations give you removable context to investigate.",
        "AWS describes why retryable APIs need to avoid extra side effects and why a caller-provided request identifier makes intent explicit. This exhibit turns that general concern into a deliberately small model; its status codes and six-tick lifetime belong to our contract."
      ],
      "takeaway": "Count durable writes as well as responses: a plausible reply can hide a duplicate side effect.",
      "sources": [
        {
          "title": "AWS Builders’ Library: Making retries safe with idempotent APIs",
          "url": "https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/"
        }
      ]
    },
    {
      "slug": "body-blind",
      "title": "The identity mix-up",
      "subtitle": "Same key. Different request.",
      "searchTitle": "Reusing an idempotency key with a different request body",
      "summary": "A changed request body is mistaken for a replay and receives the old receipt.",
      "target": "body-blind",
      "color": "violet",
      "glyph": "≠",
      "artifact": {
        "target": "body-blind",
        "steps": [
          {
            "at": 0,
            "key": "B",
            "value": "B",
            "delivery": "received"
          },
          {
            "at": 1,
            "key": "A",
            "value": "A",
            "delivery": "received"
          },
          {
            "at": 2,
            "key": "A",
            "value": "B",
            "delivery": "received"
          },
          {
            "at": 3,
            "key": "A",
            "value": "A",
            "delivery": "received"
          }
        ]
      },
      "expected": {
        "reference": [
          {
            "status": 201,
            "receipt": 1,
            "committedWrites": 1
          },
          {
            "status": 201,
            "receipt": 2,
            "committedWrites": 2
          },
          {
            "status": 409,
            "receipt": null,
            "committedWrites": 2
          },
          {
            "status": 200,
            "receipt": 2,
            "committedWrites": 2
          }
        ],
        "faulty": [
          {
            "status": 201,
            "receipt": 1,
            "committedWrites": 1
          },
          {
            "status": 201,
            "receipt": 2,
            "committedWrites": 2
          },
          {
            "status": 200,
            "receipt": 2,
            "committedWrites": 2
          },
          {
            "status": 200,
            "receipt": 2,
            "committedWrites": 2
          }
        ]
      },
      "explanation": [
        "Operation 3 keeps key A but changes its value from A to B. The reference refuses that conflict with 409. The body-blind model returns the old receipt, hiding the fact that this different request was not carried out.",
        "Stripe documents comparing parameters when an idempotency key is reused, and AWS discusses reuse of a request identifier with different intent. Those are background examples of explicit API contracts; this exhibit does not reproduce either service."
      ],
      "takeaway": "An idempotency key identifies an intent. Reusing it with different content needs an explicit policy.",
      "sources": [
        {
          "title": "Stripe API: Idempotent requests",
          "url": "https://docs.stripe.com/api/idempotent_requests"
        },
        {
          "title": "AWS Builders’ Library: Making retries safe with idempotent APIs",
          "url": "https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/"
        }
      ]
    },
    {
      "slug": "sliding-expiry",
      "title": "The moving deadline",
      "subtitle": "A retry quietly resets the clock.",
      "searchTitle": "Idempotency key expiry: fixed deadline versus sliding TTL",
      "summary": "A replay extends a key lifetime and changes what happens exactly at its original expiry boundary.",
      "target": "sliding-expiry",
      "color": "amber",
      "glyph": "◴",
      "artifact": {
        "target": "sliding-expiry",
        "steps": [
          {
            "at": 0,
            "key": "A",
            "value": "A",
            "delivery": "received"
          },
          {
            "at": 1,
            "key": "B",
            "value": "B",
            "delivery": "received"
          },
          {
            "at": 5,
            "key": "A",
            "value": "A",
            "delivery": "received"
          },
          {
            "at": 6,
            "key": "A",
            "value": "A",
            "delivery": "received"
          }
        ]
      },
      "expected": {
        "reference": [
          {
            "status": 201,
            "receipt": 1,
            "committedWrites": 1
          },
          {
            "status": 201,
            "receipt": 2,
            "committedWrites": 2
          },
          {
            "status": 200,
            "receipt": 1,
            "committedWrites": 2
          },
          {
            "status": 201,
            "receipt": 3,
            "committedWrites": 3
          }
        ],
        "faulty": [
          {
            "status": 201,
            "receipt": 1,
            "committedWrites": 1
          },
          {
            "status": 201,
            "receipt": 2,
            "committedWrites": 2
          },
          {
            "status": 200,
            "receipt": 1,
            "committedWrites": 2
          },
          {
            "status": 200,
            "receipt": 1,
            "committedWrites": 2
          }
        ]
      },
      "explanation": [
        "A is created at tick 0 and expires at tick 6 in this contract. The tick-5 replay must leave that deadline unchanged. The sliding-expiry model moves it to tick 11, so the tick-6 request replays an old receipt while the reference commits a new write.",
        "Expiry behavior depends on the API contract: Stripe, for example, documents a retention period and how a key can be reused after pruning. Our fixed six-tick deadline is synthetic. A sliding lifetime is only a fault here because the declared contract requires a fixed one."
      ],
      "takeaway": "Test the deadline itself, and keep absolute timestamps when reducing the failure.",
      "sources": [
        {
          "title": "Stripe API: Idempotent requests",
          "url": "https://docs.stripe.com/api/idempotent_requests"
        }
      ]
    },
    {
      "slug": "forget-on-loss",
      "title": "The vanished acknowledgment",
      "subtitle": "The response disappears. The write stays.",
      "searchTitle": "Lost API response after commit: retry and recovery",
      "summary": "A lost response incorrectly erases deduplication state, so recovery commits the same intent again.",
      "target": "forget-on-loss",
      "color": "blue",
      "glyph": "⌁",
      "artifact": {
        "target": "forget-on-loss",
        "steps": [
          {
            "at": 0,
            "key": "B",
            "value": "B",
            "delivery": "received"
          },
          {
            "at": 1,
            "key": "A",
            "value": "A",
            "delivery": "lost"
          },
          {
            "at": 2,
            "key": "B",
            "value": "B",
            "delivery": "received"
          },
          {
            "at": 3,
            "key": "A",
            "value": "A",
            "delivery": "received"
          },
          {
            "at": 4,
            "key": "B",
            "value": "A",
            "delivery": "received"
          }
        ]
      },
      "expected": {
        "reference": [
          {
            "status": 201,
            "receipt": 1,
            "committedWrites": 1
          },
          {
            "status": null,
            "receipt": null,
            "committedWrites": 2
          },
          {
            "status": 200,
            "receipt": 1,
            "committedWrites": 2
          },
          {
            "status": 200,
            "receipt": 2,
            "committedWrites": 2
          },
          {
            "status": 409,
            "receipt": null,
            "committedWrites": 2
          }
        ],
        "faulty": [
          {
            "status": 201,
            "receipt": 1,
            "committedWrites": 1
          },
          {
            "status": null,
            "receipt": null,
            "committedWrites": 2
          },
          {
            "status": 200,
            "receipt": 1,
            "committedWrites": 2
          },
          {
            "status": 201,
            "receipt": 3,
            "committedWrites": 3
          },
          {
            "status": 409,
            "receipt": null,
            "committedWrites": 3
          }
        ]
      },
      "explanation": [
        "The A request at tick 1 commits before its response is lost. Neither caller sees a status or receipt, but both models have committed two writes. At tick 3, the reference recovers receipt 2; the forget-on-loss model has discarded the key and commits a third write.",
        "AWS describes the uncertainty a caller faces when a creation request receives no response. Here, delivery loss occurs after the modeled service finishes. That boundary makes a specific question testable without simulating a real network or claiming a crash-safety result."
      ],
      "takeaway": "An absent acknowledgment is not evidence that an operation failed. Recovery should preserve the original intent.",
      "sources": [
        {
          "title": "AWS Builders’ Library: Making retries safe with idempotent APIs",
          "url": "https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/"
        }
      ]
    }
  ]
}
