Prove your retry fix with Nemisis
You are fixing a bug where a crash between an effect and its "done" marker lets a retry double the
effect (a webhook credited twice, an order reserved twice, an email sent twice). A passing unit
test does not prove the crash is gone. Nemisis does: it kills the handler at every store commit and
checks that a fresh retry ends with the effect exactly once. Use it whenever the bug is about
retries, replays, duplicate delivery, or idempotency.
You do not change Nemisis and you do not restructure the user's code. You write a port: a
minimal handler against Nemisis's store that mirrors the real handler's logic, iterate on the port
until it is proven, then apply the same change to the real handler.
The loop
list_scenarios. Pick the shape that matches the bug. One durable effect plus its marker is
sqlite-credit-v1. A decrement with a marker is sqlite-inventory-v1. The tool names each
store's methods and its one-sentence invariant.
port_template(scenario). It returns the handler skeleton, the store API, and the three
shapes that forfeit a verdict. Write .nemisis/port/<scenario>/app/<file>.py with the one
handler function, its logic mirroring the real one, expressed through the store's methods.
map(candidate=".nemisis/port/<scenario>", scenario). See where the handler can die before
you fix it: each store commit, the durable state a crash there leaves, and the state after the
retry. This tells you which commit boundary is the dangerous one.
check(candidate=".nemisis/port/<scenario>", scenario). Iterate until the verdict is
FIX_PROVEN_FOR_THIS_CAPSULE (exit 0). The other verdicts are not passes:
PATCH_FAILED_STILL_REPRODUCES / PATCH_FAILED_INVARIANT_BROKEN: the port still has a bug.
EVIDENCE_INCOMPLETE: the port wrote around the store (raw SQL on the connection, a dedup
table of its own, a file beside the database). Read the summary's remedy and express the fix
through the store's methods instead. Never change the kernel to make this go away.
- Apply the same change to the real handler. Then fill in the port ledger and attach it to
the PR beside the receipt: which lines of the real handler map to which store calls, and what
the port does not carry (a log line, a second datastore, a Redis key). The receipt proves the
port; you are responsible for the correspondence, and the ledger is where you state it. A receipt
without a ledger is a claim above its evidence.
The port ledger (paste into the PR)
Nemisis port ledger — <scenario>, verdict FIX_PROVEN_FOR_THIS_CAPSULE, run <run_id>
real handler line -> store call:
<path>:<line> credit + insert marker -> store.credit_and_mark(...)
the port does not carry:
- <the log line / metric / second write the real handler also does>
receipt: <receipt_resource>
What the receipt does and does not prove
It proves that the ported handler, killed at every store commit and retried, lands the effect
exactly once against the scenario's store. It does not prove anything the port did not carry, and
it says so. The kernel never calls a model. list_scenarios, port_template, map, check, and
doctor run locally and upload nothing. draft_contract and propose_patch are the exceptions:
with a Token Factory key they send the issue text and the base handler you name to the Nebius
endpoint; without a key they are BLOCKED and write nothing, never mocked.
1---2name: nemisis3description: Use when you are fixing a retry, replay, duplicate-delivery, or idempotency bug and want to prove the fix is crash-safe. Nemisis kills the handler at every store commit and checks that a retry lands the effect exactly once, then hands back a receipt you attach to the PR.4---56# Prove your retry fix with Nemisis78You are fixing a bug where a crash between an effect and its "done" marker lets a retry double the9effect (a webhook credited twice, an order reserved twice, an email sent twice). A passing unit10test does not prove the crash is gone. Nemisis does: it kills the handler at every store commit and11checks that a fresh retry ends with the effect exactly once. Use it whenever the bug is about12retries, replays, duplicate delivery, or idempotency.1314You do not change Nemisis and you do not restructure the user's code. You write a **port**: a15minimal handler against Nemisis's store that mirrors the real handler's logic, iterate on the port16until it is proven, then apply the same change to the real handler.1718## The loop19201. **`list_scenarios`.** Pick the shape that matches the bug. One durable effect plus its marker is21 `sqlite-credit-v1`. A decrement with a marker is `sqlite-inventory-v1`. The tool names each22 store's methods and its one-sentence invariant.232. **`port_template(scenario)`.** It returns the handler skeleton, the store API, and the three24 shapes that forfeit a verdict. Write `.nemisis/port/<scenario>/app/<file>.py` with the one25 handler function, its logic mirroring the real one, expressed through the store's methods.263. **`map(candidate=".nemisis/port/<scenario>", scenario)`.** See where the handler can die before27 you fix it: each store commit, the durable state a crash there leaves, and the state after the28 retry. This tells you which commit boundary is the dangerous one.294. **`check(candidate=".nemisis/port/<scenario>", scenario)`.** Iterate until the verdict is30 `FIX_PROVEN_FOR_THIS_CAPSULE` (exit 0). The other verdicts are not passes:31 - `PATCH_FAILED_STILL_REPRODUCES` / `PATCH_FAILED_INVARIANT_BROKEN`: the port still has a bug.32 - `EVIDENCE_INCOMPLETE`: the port wrote **around** the store (raw SQL on the connection, a dedup33 table of its own, a file beside the database). Read the `summary`'s remedy and express the fix34 through the store's methods instead. Never change the kernel to make this go away.355. **Apply the same change to the real handler.** Then fill in the **port ledger** and attach it to36 the PR beside the receipt: which lines of the real handler map to which store calls, and what37 the port does **not** carry (a log line, a second datastore, a Redis key). The receipt proves the38 port; you are responsible for the correspondence, and the ledger is where you state it. A receipt39 without a ledger is a claim above its evidence.4041## The port ledger (paste into the PR)4243```44Nemisis port ledger — <scenario>, verdict FIX_PROVEN_FOR_THIS_CAPSULE, run <run_id>45 real handler line -> store call:46 <path>:<line> credit + insert marker -> store.credit_and_mark(...)47 the port does not carry:48 - <the log line / metric / second write the real handler also does>49 receipt: <receipt_resource>50```5152## What the receipt does and does not prove5354It proves that the ported handler, killed at every store commit and retried, lands the effect55exactly once against the scenario's store. It does not prove anything the port did not carry, and56it says so. The kernel never calls a model. `list_scenarios`, `port_template`, `map`, `check`, and57`doctor` run locally and upload nothing. `draft_contract` and `propose_patch` are the exceptions:58with a Token Factory key they send the issue text and the base handler you name to the Nebius59endpoint; without a key they are `BLOCKED` and write nothing, never mocked.