Version-safe durable mutation idempotency
Preserve idempotent mutation semantics across request-canonicalization rollouts, mixed client versions, ambiguous replies, and uncertain external completion.
Version-safe durable mutation idempotency
Trigger
Use this guidance when a mutation can commit before its reply is observed and retries may cross a request-schema, defaulting, or canonicalization rollout.
Practical steps
- Keep one durable uniqueness key over caller scope, stable operation, and client idempotency key. Fingerprint versions are record metadata, never additional uniqueness dimensions.
- In one transaction, claim the key, apply the database effect, and persist the request contract version, fingerprint-semantics version, digest, result schema version, and immutable result envelope.
- On a duplicate, load the existing record before choosing comparison rules. Decode the incoming request under its declared contract, then compare using the stored record's fingerprint semantics.
- Cross versions only through an explicit semantic-equivalence adapter. Reject the retry if conversion erases an effect-relevant distinction, relies on an ambiguous default, or produces a different digest. A historical digest is trustworthy only if it covered every effect-relevant distinction in its own contract.
- Let the datastore serialize overlapping old-client and new-client claims. A loser must wait for or reread the committed winner before comparing; a uniqueness error alone is not a replay decision.
- Replay from an immutable envelope that contains everything needed to reproduce the historical client-visible outcome. Use a deterministic compatible renderer. If rendering is impossible, return a compatibility error or stable result reference without repeating the effect.
- Deploy readers, adapters, and renderers for retained versions before enabling new-version writes. Preserve them for the full retry and record-retention horizon, and do not rewrite old digests under new rules.
- For effects outside the database transaction, use a transactional outbox and downstream deduplication. If a worker fails before sending, the effect remains denied. If it may have sent and the destination may have committed, mark the outcome unconfirmed, block new sends, and recover with the same operation identity while the primary task continues.
- Bound auxiliary warnings with an atomically enforced three-plus-one per ten-minute window rule: retain one card for each of the first three normalized reason classes and coalesce the rest into one other card with a counter.
Limits
Versioning cannot repair a legacy fingerprint that omitted a mutation-relevant distinction unless trusted original intent remains. A compatibility error prevents duplication but does not reproduce the original response. A crash after a possible external commit cannot prove denial; only recovery or downstream lookup can resolve the outcome. Warning bounds control observability cardinality, not execution correctness.
Failure prevented
This prevents duplicate mutations after lost replies, false matches when new fields are ignored by an old canonicalizer, incorrect replay decisions by concurrent mixed-version clients, mutation reexecution when response rendering changes, and unsafe retries after an externally committed but unacknowledged send.
Evidence
This guidance is supported by reasoned analysis and an independent reasoning review. No tests were executed.