Two derivations: deriveOrderMatchStatus(sizeVector) + a separate settlement-outcome capture
The order's user-facing state is produced by two separate, modular, pure functions — replacing the
six scattered status mappers that decide it today (Betfair mapPlacementStatus /
mapExecutionCompleteStatus / mapClearedSettlementOutcome, Bifrost mapBetStatusToOrderStatus,
PinnacleMapper, betsApi.deriveStatus):
deriveOrderMatchStatus(sizeVector)— pure, size-vector-only, read-time, zero provider branches. The single source of the match-axis display status:unmatched | partially_matched (+ lossless remainder breakdown) | matched | fully_lapsed | fully_cancelled. Adapters' only job is to normalize their wire payload into the Size Vector.deriveOrderOutcome(...)— the modular "rest": the settlement outcome (win | lose | void)- P&L. A separate axis from the match status.
settled is one status; the outcome is separate
Once an order settles, its status is the single value settled — never win/lose/void. It
remains the stored DB lifecycle terminal (Order.status = 'settled') — already persisted, no new
column. The win/lose/void result is the separate, already-stored Order.settlementOutcome
(produced by function 2), never folded into the status. settled is added to the display-status list.
Composition for display
primary = (Order.status === 'settled')
? 'settled'
: deriveOrderMatchStatus(sizeVector);
The settlement outcome and the lossless match breakdown are shown alongside the primary — a settled
winning bet displays "Settled · Won · ₹60 matched", losslessly (ADR-0003). This keeps
deriveOrderMatchStatus purely size-vector-driven while settled stays the stored lifecycle terminal.
Scope note
This replaces the scattered display-status determination (the six mappers). The coarse DB lifecycle-status write-sites (orderService / orderSyncJob / settlement / Bifrost / Pinnacle) remain as infrastructure (ADR-0002) — unifying those is a separate follow-up, not this change.