Skip to content

feat(markets): centralise settlement state transition rules - #1791

Open
bilhokista wants to merge 4 commits into
Arena1X:mainfrom
bilhokista:feat/1616-settlement-transitions
Open

feat(markets): centralise settlement state transition rules#1791
bilhokista wants to merge 4 commits into
Arena1X:mainfrom
bilhokista:feat/1616-settlement-transitions

Conversation

@bilhokista

Copy link
Copy Markdown

Closes #1616.

A note on the states, before anything else

The issue describes the lifecycle as open → closed → resolving → settled/cancelled. The enum actually in the codebase (MarketSettlementState, added by 1776700000000-AddMarketSettlementState.ts and since extended) is:

pending → proposed → settling → settled, with proposed → challenged → settled when a challenge is raised.

I implemented the lifecycle that exists rather than the one in the issue text, since renaming states would be a migration and a breaking API change rather than the guard the issue asks for. Cancellation is carried by the separate is_cancelled flag and is deliberately left out of the enum. If the states in the issue were the intended target rather than shorthand, say so and I will redo this against them.

What was actually missing

The transitions were already guarded — but in four separate places, each written by hand:

  • markets.service.ts had three settlement_state !== X checks (propose, challenge, resolve-challenge)
  • market-settlement.scheduler.ts had a fourth as an inline stillEligible predicate

Each copy was correct. Nothing tied them together, so adding a state or changing a rule meant finding all four by hand, and no test stated the rules anywhere.

The change

backend/src/markets/market-settlement-state.util.ts (new) holds ALLOWED_SETTLEMENT_TRANSITIONS as the single source of truth, plus canTransition(from, to) and describeIllegalTransition(from, to). The rejection message names both states and lists what would have been allowed, so a caller is told what to do rather than only that they were wrong:

Cannot move market settlement from "pending" to "settled" (allowed from "pending": proposed)
Cannot move market settlement from "settled" to "proposed" ("settled" is a terminal state)

The service's three guards and the scheduler's predicate now consult that table.

Existing behaviour is preserved deliberately. The exception types are unchanged (ConflictException on propose, BadRequestException on challenge and adjudication), since markets.service.spec.ts asserts on those. Only the message text is richer. SETTLING → SETTLING is modelled as legal because the scheduler genuinely re-claims a market it already marked SETTLING after a crash — that is existing behaviour, now written down instead of implied.

One guard is intentionally not routed through the table: resolveChallenge still requires exactly CHALLENGED. SETTLING → SETTLED is a legal transition, but only via the scheduler, never via admin adjudication — the table describes the lifecycle, not authorisation. There is a comment at that line saying so.

Tests

market-settlement-state.util.spec.ts (new) writes out the six intended transitions by hand and derives the illegal set from them, so the test states the rules rather than re-deriving them from the table it is testing. It covers:

  • each of the 6 valid transitions is allowed
  • each of the remaining 19 pairs is rejected
  • all 25 pairs are covered exactly once (guards against a hole opening up silently)
  • settled is terminal from every direction
  • every enum member has a table entry, so a newly added state cannot be quietly forgotten
  • both message shapes

Honest note on verification: I could not run the backend Jest suite locally (no full workspace install). Every changed file was parsed with the TypeScript compiler API, and the transition table was transpiled and executed standalone across all 25 pairs — 0 mismatches against the hand-written expectations, and both message shapes were checked against real output (quoted above). CI is the real check on the service- and scheduler-level suites.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CrfEY1tvXrbeMDAUzxfuk7

@vercel

vercel Bot commented Sep 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
insight-arena-4rll Ready Ready Preview Sep 10, 2026 1:40pm UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Backend] — Market Settlement State Machine Validation

1 participant