Skip to main content

Order display status is a read-time derivation, not a persisted column

SUPERSEDED by ADR-0012 (2026-07-09). Pran's #950 makes the canonical statuses the DB literal set, so the status IS now stored (derive-then-store) — not read-time-only. What survives: the single provider-agnostic pure classifier, no per-provider branches, and netting/reconciliation never depending on display wording. What's reversed: "never stored". Read ADR-0012 first.

We show users a richer order status than the DB Order.status enum can express — a single "partially matched" primary plus a remainder disposition sub-message ("X still to match" / "Y lapsed" / "Y cancelled by you"), and the fully-lapsed / fully-cancelled / fully-voided terminal states. We derive this Display Status at read-time (the /orders serializer + the WS order:status payload) from the Size Vector already persisted on the order (sizeMatched / sizeRemaining / sizeLapsed / sizeCancelled + settlementOutcome + hasLiveRemainder

  • requested stake). It is never stored, and reconciliation and netting must never depend on it (Bhargav, 2026-07-08).

Considered options

  • Stored, indexed display_status column (write on the same order-update tx). Rejected: forces a migration + backfill + a second write path that can drift from the derivation, and — the deciding factor — it invites reconciliation/reporting to couple to display wording, which we explicitly want to keep independent going forward.
  • Read-time pure derivation (chosen): zero migration, always consistent with the source columns, trivially reversible, and structurally impossible for netting/reconciliation to depend on.

Consequences

  • Admin/ops cannot WHERE display_status = … in SQL. Acceptable: such filters operate on the raw size/lifecycle columns, or client-side over already-fetched rows. If a hard SQL-filter need ever appears, the same pure function can back a Postgres generated column with no logic change.
  • The derivation must be a single pure deriveDisplayStatus(sizeVector) → DisplayStatus with no per-provider branches — each adapter's only job is to normalize its wire payload into the Size Vector. A new provider adds a normalizer, never a status rule.