Skip to main content

ADR 0005: Persist financial intent before provider submission

This record defines safe write and retry behavior.

Context

Perflo documents idempotency for only four agent operations. A timeout after a money write may mean that the provider received and executed it.

Decision

Require Idempotency-Key on financial and card mutations. Store the actor, route, key, canonical request hash, operation, and outbox event in one PostgreSQL transaction before provider work. Treat delivery as at least once. A possible submission enters indeterminate; reconciliation checks a stable provider operation reference and never retries blindly. The dispatcher claims one outbox row immediately before processing it with a five-minute lease. An abandoned claim becomes eligible again. The worker commits submitting before provider I/O. If delivery later resumes with an operation still in submitting, it marks the operation indeterminate without calling the provider. Mandate execution takes the mandate lock before this transition; suspension or revocation cancels queued accepted reservations, while a committed submitting transition is the linearization point after which submission has begun. Provider writes that must complete in the request path follow the same state rule. Beneficiary and card writes commit submitting before provider I/O. A scheduled reconciler marks stale synchronous submissions indeterminate and never resubmits them. Card projections move to indeterminate with their operation, allowing a later provider read or operator investigation to resolve the state.

Consequences

Equal retries return the original operation even if mutable prerequisites later expire. Reusing a key with different input returns HTTP 409. Clients retain the same idempotency key and confirmation intent through transport retries. Provider operation lookup and write idempotency remain production requirements. The replay window, the replay marker, and record retention are decided in ADR-0018.

Rejected alternatives

  • Untracked request-bound writes: client disconnects and upstream timeouts lose durable state.
  • Exactly-once claim: no distributed system boundary supports that guarantee here.