Idempotency contract
Every handler for a kind listed in subscribed_event_kinds runs under an
at-least-once delivery contract. That means the same event may be delivered
twice with the same event_id, and the second delivery must leave post-state
unchanged.
The kernel enforces that contract in two ways:
- A startup validator fails closed when a subscribed handler declares neither
@idempotentnorreplay_key(event). - The replay harness fires the same event twice with the same
event_idand asserts that state after the first delivery equals state after the second.
Static contract
Section titled “Static contract”Use one of these patterns on every subscribed handler:
@idempotent: truewhen replay is already safe because the handler does an upsert or checksevent_idbefore mutating state.replay_key(event)when the handler needs an explicit, deterministic dedup key.
Counter vs set
Section titled “Counter vs set”This is the simplest way to think about replay safety:
Bad: counter semantics
Section titled “Bad: counter semantics”First delivery:
Second delivery of the same event:
That handler is not idempotent. Replay changed state.
Good: set semantics
Section titled “Good: set semantics”First delivery:
Second delivery of the same event:
State is unchanged, so replay is safe.
What the replay harness checks
Section titled “What the replay harness checks”For each subscribed handler, the harness:
- Builds a fresh handler state.
- Delivers one event.
- Captures post-state.
- Delivers the same event again with the same
event_id. - Captures post-state again.
- Fails if the snapshots differ.
The harness is intentionally simple. If a handler cannot survive this replay, it is not safe for the at-least-once delivery contract.
Author checklist
Section titled “Author checklist”- Dedup by
event_id. - Prefer upsert or set semantics over increment semantics.
- Keep the dedup write and the effect in the same transaction boundary.
- Declare
@idempotentorreplay_key(event)before wiring the handler intosubscribed_event_kinds.