[Exchange] Settlement mechanics — resolve positions on lifecycle events done

← Capital Markets
Auto-settle on lifecycle transitions, P&L distribution, frozen positions during challenges, AMM counterparty

Completion Notes

Auto-completed by supervisor after successful deploy to main

Git Commits (20)

[Exchange] Update spec work log — rebase and cleanup [task:exch-cm-04-BOOK]2026-04-26
[Exchange] Restore market bidding compatibility routes [task:exch-cm-04-BOOK]2026-04-26
[Exchange] Guard market-book depth payload shape [task:exch-cm-04-BOOK]2026-04-25
[Exchange] Add LMSR order book and artifact market APIs [task:exch-cm-04-BOOK]2026-04-25
Squash merge: orchestra/task/exch-cm--bidding-system-capital-backed-positions (11 commits)2026-04-25
Squash merge: orchestra/task/exch-cm--bidding-system-capital-backed-positions (11 commits)2026-04-25
[Exchange] Add Bearer API-key auth to /api/market/* write endpoints [task:exch-cm-03-BID]2026-04-25
[Exchange] Market bidding system — capital-backed positions on artifacts [task:exch-cm-03-BID]2026-04-25
Squash merge: orchestra/task/exch-cm--settlement-mechanics-resolve-positions-o (1 commits)2026-04-25
Squash merge: orchestra/task/exch-cm--settlement-mechanics-resolve-positions-o (2 commits)2026-04-25
Squash merge: orchestra/task/exch-cm--blockchain-bridge-prep-ledger-compatible (1 commits)2026-04-25
[Exchange] Portfolio management — positions, P&L, exposure, Sharpe [task:exch-cm-05-PORT]2026-04-25
Squash merge: orchestra/task/exch-cm--settlement-mechanics-resolve-positions-o (1 commits)2026-04-25
[Verify] Settlement mechanics atomicity — already resolved on main [task:exch-cm-06-SETL]2026-04-25
Squash merge: orchestra/task/exch-cm--settlement-mechanics-resolve-positions-o (2 commits)2026-04-25
Squash merge: orchestra/task/exch-cm--blockchain-bridge-prep-ledger-compatible (1 commits)2026-04-25
Squash merge: orchestra/task/exch-cm--blockchain-bridge-prep-ledger-compatible (1 commits)2026-04-25
[Exchange] Blockchain bridge prep — ledger EVM-compatible, export/import, migration guide [task:exch-cm-08-BRGE]2026-04-25
[Exchange] Portfolio management — positions, P&L, exposure, Sharpe [task:exch-cm-05-PORT]2026-04-25
[Verify] Capital-weighted resource allocation — already resolved on main [task:exch-cm-07-RALL]2026-04-25
Spec File

Goal

When an artifact's lifecycle state changes (validated, deprecated, etc.), all open positions
on that artifact must settle. Long holders profit when artifacts are validated; short holders
profit when artifacts are deprecated. Settlement is the mechanism that makes market
predictions consequential.

Settlement Rules

Lifecycle TransitionLong PositionShort Position
validated (price > 0.7 sustained)Profit: tokens × (settle_price - entry_price)Loss: tokens committed lost
deprecated (price < 0.1 sustained)Loss: tokens committed lostProfit: tokens × (entry_price - settle_price)
flagged (price < 0.3)Partial loss: 50% of committed tokens returnedPartial profit: 25% bonus
challenged (debate initiated)Positions frozen until debate resolvesPositions frozen
Artifact remains listed >90 daysPositions can be closed at current market priceSame

Acceptance Criteria

settlement.py module with:
- settle_artifact(artifact_id, new_lifecycle_state) — settle all positions
- settle_position(position_id, settlement_price) — settle individual position
- compute_settlement_pnl(position, settlement_price) — P&L calculation
- distribute_bounty(bounty_id, claimants) — distribute bounty tokens
☑ Settlement triggered automatically by lifecycle transitions (POST /api/artifacts/{id}/lifecycle-transition)
☑ Frozen positions during challenged state (no close, no cancel — 409 on cancel; status set to 'challenged')
☑ Settlement P&L credited/debited to token accounts atomically
☑ Settlement logged to token_ledger with reason='settlement:profit' or settlement:loss
☑ Correct predictions increase agent believability; incorrect decrease it (via actor_reputation)
☑ API: GET /api/market/settlements?artifact_id=X — settlement history
☑ Settlement notifications to affected agents (logged to events table)

Settlement Edge Cases

  • Partial settlement: If lifecycle goes to flagged then back to listed, positions
partially settle but can be reopened
  • No counterparty: LMSR AMM absorbs the other side — it's the counterparty
  • Zero-sum: Long profits = short losses + AMM subsidy/tax. The system is slightly
positive-sum because token minting (earning) exceeds settling losses, keeping the
economy growing

Dependencies

  • exch-cm-03-BID — Positions to settle
  • exch-cm-05-PORT — Portfolio updates on settlement
  • exch-qm-03-LIFE — Lifecycle transitions trigger settlement

Dependents

  • exch-cm-02-EARN — Correct predictions mint reward tokens

Work Log

2026-04-25 — Slot exch-cm-06-SETL

  • Read AGENTS.md, explored Exchange layer: scidex/exchange/, api.py, scripts/settlement.py
  • Found scripts/settlement.py (479 lines) with core logic — moved to production module
  • Created scidex/exchange/settlement.py with:
- compute_settlement_pnl(position, settlement_price, lifecycle_state) — P&L logic per spec
- settle_position(position_id, settlement_price, lifecycle_state, db) — individual settlement with atomic token_accounts + token_ledger + actor_reputation update
- settle_artifact(artifact_id, new_lifecycle_state, db) — batch settle all open positions; challenged state freezes (status='challenged') instead of settling
- distribute_bounty(bounty_id, claimants, db) — proportional token distribution
- get_settlement_history(artifact_id, agent_id, limit, db) — settlement audit trail
- _log_settlement_notification() — logs to events table (non-real-time per spec)
  • Modified api.py (api.py changes):
- Added GET /api/market/settlements?artifact_id=X&agent_id=Y&limit=N — settlement history endpoint
- Added POST /api/artifacts/{artifact_id}/lifecycle-transition — triggers settle_artifact for validated/deprecated/flagged/challenged transitions, logs to artifact_lifecycle_history
- Modified cancel position endpoint: returns 409 (not 404) when position status='challenged', with clear freeze message
  • Tested compute_settlement_pnl against all 6 spec scenarios — all pass
  • Tested get_settlement_history against live DB: 1 existing settled record found
  • Syntax checked both files: pass
  • DB state: 302 tables, market_positions ✓, token_accounts ✓, token_ledger ✓, 3 open positions
  • Result: All acceptance criteria satisfied

2026-04-26 — Gate fix (retry 1)

  • Review gate rejected: lifecycle-transition handler committed artifact state before calling settle_artifact, breaking atomicity — a settlement failure would return 500 with the artifact already transitioned.
  • Fix: removed premature db.commit(), passed db=db to settle_artifact(resolved_id, new_state, db=db), moved db.commit() to after settlement returns. Settlement module already supported an external-db path (own_db = db is None) and skips its own commit when db is supplied, so all writes (artifact state, lifecycle history, positions, token_accounts, token_ledger) now commit atomically or roll back together.

Already Resolved — 2026-04-26 16:00:00Z

Evidence:

  • Current api.py lines 24673–24696: lifecycle UPDATE, history INSERT, and settle_artifact(resolved_id, new_state, db=db) all use the same db connection; db.commit() fires only after settlement returns; any exception triggers db.rollback().
  • scidex/exchange/settlement.py: settle_artifact and settle_position check own_db = db is None and only self-commit when no external db is passed — confirmed at lines 221, 256 (if own_db: db.commit()).
  • Commit SHA: f9b1a0d56 — "[Exchange] Fix lifecycle-transition atomicity: settle within same transaction [task:exch-cm-06-SETL]", squash-merged to main as f8284bf5a.
Summary: The atomicity issue raised in Review 1 was addressed in f9b1a0d56 before the prior squash merge. All acceptance criteria remain satisfied on current main HEAD 6e2b3b699.

Sibling Tasks in Quest (Capital Markets) ↗