# ADR 003: Use retry-safe commands with explicit conflict checks

- HTML version: https://robbiepalmer.me/projects/personal-finance-app/adrs/003-online-only-shared-financial-writes
- Project: Personal Finance App (https://robbiepalmer.me/projects/personal-finance-app.md)
- Status: Proposed
- Date: 2026-09-24
- Initiatives: Digital Twins for Everyday Life (https://robbiepalmer.me/initiatives/digital-twins-for-everyday-life.md)

## Context

The product policy in
[PDR 005](/projects/personal-finance-app/pdrs/005-online-only-shared-writes)
requires connectivity to commit shared financial changes in the first multi-user release. An online
request can still fail after the service commits a write but before the client receives the result.
Another household member may also change the same state between a read and a later command.

The service needs transport and storage rules that make retries safe and expose stale edits without
using device time as a source of truth.

## Decision

Give each mutating command an idempotency key scoped to the household and operation. Repeating a
completed command with the same key returns its recorded outcome and does not apply the mutation
again. A reused key with a different command is rejected.

Commands that change existing financial state include the version they read. The service commits the
change only when that version is still current. It returns a conflict with the current version when
another accepted command has changed the state. The client then asks the user to review the new state
rather than applying last-write-wins.

The API contract will define command identity, version preconditions, conflict responses, and the
retention period for completed command outcomes. Implementation tests will cover a response lost
after commit, concurrent edits, and idempotency-key misuse.

## Consequences

Every shared write path must store command outcomes and check versions. That adds storage, expiry,
and test work, but it prevents retries from duplicating accepted financial changes and stops stale
edits from silently replacing newer state.

The client must handle an explicit conflict and help the user reapply an intended change. This ADR
does not define offline ordering or recovery. If PDR 005 changes, offline mutation will require a new
architecture decision for the bounded commands it permits.

## Alternatives considered

### Retry without a durable command identity

This was rejected because the client cannot know whether a timed-out request committed. Retrying the
operation could create a duplicate transfer, correction, or import.

### Last write wins

This was rejected because it can silently discard another household member's accepted change. Device
time also does not establish a trustworthy order between clients.

---

Markdown index of this site: https://robbiepalmer.me/llms.txt
