Skip to main content

deriveOrderOutcome is obviated by #950; the strong primitives are computeOrderPnl + classifyQuarterLineOutcome

Status: ACCEPTED (Bhargav + Architect, 2026-07-09). Supersedes the "re-apply deriveOrderOutcome" item of the merge plan / ADR-0007's extraction.

The two settlement paths (grounded, 953)

Settlement outcome is derived on two structurally different paths with different inputs — and a deliberately different zero-boundary rule:

  1. Per-order, provider-reportedreleaseMarketCredit (settlement.ts:2240) / the BetsApi+cleared path. Input: a per-order provider result + the order's own pnl. Rule: pnl.isZero() → void, >0 → win, else lose. A per-order zero pnl means the provider refunded → void.
  2. Market-wide nettingsettleMarketForUser (settlement.ts:820). Input: the netted exposure contribution vector at winnerIdx, NOT a provider result. Rule: pnl.gte(0) → win. A netted zero contribution at the winning index means the bet was on the winner and broke even → win (and is therefore included in commission), NOT a refund.

Our deriveOrderOutcome (#951) is a WRAPPER for path 1 only — its own docstring states "settleMarketForUser … deliberately does NOT route through this function." Both paths already build on the same pure math: computeOrderPnl and classifyQuarterLineOutcome (settlement.ts:230/304).

Verdict

  • The strong, architecturally-correct functions that MUST remain = computeOrderPnl + classifyQuarterLineOutcome. They are the single source of settlement math; #950 kept them and both paths use them. Neither PR rewrites them.
  • deriveOrderOutcome is NOT superior to #950's structure. It was valuable on dev1 where the per-order path had messy inline outcome logic. #950's rewrite already achieved clean settlement: math primitives shared, each path's outcome logic expressed where it belongs. deriveOrderOutcome's one extra (bundling the quarter-line refinement into path 1) is still available by calling classifyQuarterLineOutcome directly. It is therefore obviated on 953.
  • Do NOT unify the two paths under one outcome function. Forcing path 1's isZero → void onto the netting path would flip break-even winning orders from winvoid. Void orders are excluded from commission (settlementOutcome === 'void'continue, settlement.ts:1398), so this would under-charge commission on break-even wins — a real ledger change. That is the divergence ADR-0007 protects and the reason to leave the two rules distinct.

Consequence

  • The #951 "settlement axis" delta does not re-apply to 953. Slices that DO apply: decline→declined (done) + size columns (done). The only clean settlement remnant is dropping the dead 'push' value from the canonical SettlementResult type (prod-verified no rows; provider-input push→void mapping stays) — a type cleanup, done separately.
  • Any future consolidation of the two outcome rules is a deliberate settlement decision owned by the settlement path (Pran) + financial-guardian, per ADR-0007 — never a mechanical re-apply.