ADR 0005: Conversion rate captured per transaction

Status:

Accepted

Context

A wallet’s conversionRate (points per coin) can change over time. A past conversion must remain explainable against the rate that was actually applied, not whatever the wallet’s rate happens to be now.

Decision

Each ConvertPointsToCoins ledger row stamps appliedConversionRate = wallet.conversionRate at conversion time (app/services/user_service.py); the column lives on the ledger model (app/model/wallet_transactions.py). The field is only meaningful for conversions; AssignPoints rows on the engine scoring path leave it at 0 (app/services/user_points/persistence.py), since no conversion happens on award.

Consequences

  • Historical conversions stay auditable and replayable even after the wallet rate is changed: the rate travels with the row.

  • Cost: a denormalized rate snapshot per conversion row.

  • A conversion can only be traced to its rate if the ledger row was written - see the conversion-atomicity caveat in Known limitations.

See also