Minimal durable idempotency for ambiguous mutation outcomes

The design question is to identify the smallest durable protocol and storage invariant that makes retried mutations safe after an ambiguous timeout. Known constraints are that a server commit may precede response loss and that volatile request tracking is insufficient. The key unknowns are the minimum atomic state required, how payload mismatches should be handled, and where the guarantee ends for external side effects.

The minimal general design is a durable command ledger keyed by an authenticated scope, operation kind, and client-generated idempotency key. Store a request fingerprint and the completed semantic result. Creation of that ledger entry, the business mutation, and storage of the result must commit in one database transaction under a unique constraint. A retry with the same key and fingerprint replays the stored result; a different fingerprint is rejected. If the first process fails before commit, both the ledger entry and mutation roll back. If it fails after commit but before replying, the retry finds the completed entry and cannot repeat the mutation. A separately committed pending claim is not minimal because it requires leases and recovery for abandoned work. The guarantee only lasts as long as the ledger is retained. Effects outside that database require a transactional outbox plus consumer deduplication, or the same key must be enforced by the external provider.

A fingerprint must be stored as an immutable version and digest pair, while the uniqueness key remains only the authenticated scope, operation, and idempotency key. On a key hit, the existing record selects the verifier; the current server release must not silently rehash with its newest algorithm. Safe cross-version replay requires an explicit compatibility mapping that proves no request meaning is discarded. New fields must be absent or fixed at legacy defaults before a newer request may be projected into a legacy fingerprint domain, and legacy parsing must reject rather than ignore unknown fields. Trying multiple canonicalizers until one digest matches is unsafe. For simultaneous old and new representations, the unique constraint chooses one record; the waiter compares through the approved lossless mapping and either replays the stored result or returns an idempotency conflict. Deployment should first make every server able to read both versions, then enable new-version writes, and retain legacy verification through the oldest record's retry horizon. Existing unversioned rows are labeled with the legacy version rather than rehashed speculatively.