Dust2 Live Kill Market — Execution Flows (C4 Dynamic Diagrams) #

Companion to architecture.md. Each diagram traces real data through the stream DAG. Offsets, state_version, and the spec's worked example ($0.47 entry, 3 final kills, $60 settlement) are used throughout.


Flow 0 — Offline: ingestion, preflight, training #

Runs once per dataset, before any market exists. Preflight gates the entire build. The ING role is source-generic: for the OpenCS2 training corpus the same step is performed by the quarantined opencs2-edge adapter (event-catalog §6.12) reading parquet tables instead of demo files — identical canonical output into ML.

sequenceDiagram
    autonumber
    actor OP as Operator
    participant ING as Demo Ingestor
    participant ML as Match Logs (store)
    participant PRE as Preflight Validator
    participant TR as Training Pipeline
    participant AR as Artifact Store
    participant REG as Model Registry

    OP->>ING: ingest match_0042.dem
    ING->>ING: parse (demoparser2/awpy), canonical serialization
    ING->>ML: append events (round_start, tick_state, kill, round_end) plus hash chain and lineage
    OP->>PRE: run preflight
    PRE->>ML: read all match logs
    PRE->>PRE: settlement contract asserts (0 ≤ final_kills ≤ enemy roster size which is at most 5, teamkill rule, post-round kills, eligibility incl. backup-restore exclusion)
    PRE-->>OP: preflight report and eligibility manifest
    Note over OP,PRE: GATE — stop and reassess the dataset if fewer than 1,500 usable rounds, fewer than 300 test rounds, or coverage gaps
    OP->>TR: train (chronological match split)
    TR->>ML: read training-split logs only
    TR->>TR: shared feature extractor → train GBM → quantize to fixed-point
    TR->>AR: freeze baseline_v1 and gbm_v1 (content hashes) plus run manifest (seeds, pinned versions)
    OP->>REG: register both artifacts (control-stream events)
    REG->>REG: admission gates — static, determinism, parity (Rust ≡ Python golden vectors), backtest on validation split
    REG-->>OP: report cards → shadow → promote (baseline_v1 to Bookmaker A, gbm_v1 to Bookmaker B, effective next market_open)
    Note over OP,REG: every model — the two house models included — passes the same four gates and lifecycle (model-plugin-layer.md §9–§10). The baseline is not special-cased, which is what keeps the comparison honest
Diagram source
sequenceDiagram
    autonumber
    actor OP as Operator
    participant ING as Demo Ingestor
    participant ML as Match Logs (store)
    participant PRE as Preflight Validator
    participant TR as Training Pipeline
    participant AR as Artifact Store
    participant REG as Model Registry

    OP->>ING: ingest match_0042.dem
    ING->>ING: parse (demoparser2/awpy), canonical serialization
    ING->>ML: append events (round_start, tick_state, kill, round_end) plus hash chain and lineage
    OP->>PRE: run preflight
    PRE->>ML: read all match logs
    PRE->>PRE: settlement contract asserts (0 ≤ final_kills ≤ enemy roster size which is at most 5, teamkill rule, post-round kills, eligibility incl. backup-restore exclusion)
    PRE-->>OP: preflight report and eligibility manifest
    Note over OP,PRE: GATE — stop and reassess the dataset if fewer than 1,500 usable rounds, fewer than 300 test rounds, or coverage gaps
    OP->>TR: train (chronological match split)
    TR->>ML: read training-split logs only
    TR->>TR: shared feature extractor → train GBM → quantize to fixed-point
    TR->>AR: freeze baseline_v1 and gbm_v1 (content hashes) plus run manifest (seeds, pinned versions)
    OP->>REG: register both artifacts (control-stream events)
    REG->>REG: admission gates — static, determinism, parity (Rust ≡ Python golden vectors), backtest on validation split
    REG-->>OP: report cards → shadow → promote (baseline_v1 to Bookmaker A, gbm_v1 to Bookmaker B, effective next market_open)
    Note over OP,REG: every model — the two house models included — passes the same four gates and lifecycle (model-plugin-layer.md §9–§10). The baseline is not special-cased, which is what keeps the comparison honest

Flow 1 — Runtime tick: state → quotes (~4Hz) #

The single push-based causal chain, fired ~4× per replay second. The spec's repricing KPI — state update to new executable quotes — covers steps 1→9 and must run under 250 ms p99 (in practice single-digit ms locally). Steps 10–12 are display fan-out, outside the KPI.

sequenceDiagram
    autonumber
    participant RE as Replay Engine
    participant FX as Feature Extractor
    participant MB as Baseline Model
    participant MT as Trained Model (gbm_v1)
    participant BA as Bookmaker A
    participant BB as Bookmaker B
    participant ME as Matching Engine
    participant GW as Gateway
    participant UI as Trading Screen

    RE->>FX: game_state (state_version=482, ts=3134.75, T with 1 kill / 4 alive, bomb planted)
    FX->>MB: features@482 (fixed-point)
    FX->>MT: features@482 (fixed-point)
    MB->>BA: P(k=0..5) truncated and renormalized (k ≥ current kills)
    MT->>BB: P = [0, .20, .44, .27, .07, .02] → E[k]=2.27 → fair $0.454
    BA->>ME: quote@482 (cancel_all_prior, bid $0.42 × 300, ask $0.49 × 300 — same risk rules, wider spread from a more diffuse distribution)
    BB->>ME: quote@482 (bid $0.44 × 400, ask $0.47 × 400 — spread around fair $0.454 with flat inventory, shade zero, asymmetry from outward grid rounding — architecture.md quote fold)
    ME->>ME: atomically expire all quotes with version ≤ 481 (zero stale-state fills)
    ME->>ME: rest new quotes — if books cross, ordinary matching occurs (bookmaker vs bookmaker)
    ME->>GW: market events (cancels, accepts, any fills)
    RE->>GW: game_state@482
    GW->>UI: push book, price-history point, replay clock
Diagram source
sequenceDiagram
    autonumber
    participant RE as Replay Engine
    participant FX as Feature Extractor
    participant MB as Baseline Model
    participant MT as Trained Model (gbm_v1)
    participant BA as Bookmaker A
    participant BB as Bookmaker B
    participant ME as Matching Engine
    participant GW as Gateway
    participant UI as Trading Screen

    RE->>FX: game_state (state_version=482, ts=3134.75, T with 1 kill / 4 alive, bomb planted)
    FX->>MB: features@482 (fixed-point)
    FX->>MT: features@482 (fixed-point)
    MB->>BA: P(k=0..5) truncated and renormalized (k ≥ current kills)
    MT->>BB: P = [0, .20, .44, .27, .07, .02] → E[k]=2.27 → fair $0.454
    BA->>ME: quote@482 (cancel_all_prior, bid $0.42 × 300, ask $0.49 × 300 — same risk rules, wider spread from a more diffuse distribution)
    BB->>ME: quote@482 (bid $0.44 × 400, ask $0.47 × 400 — spread around fair $0.454 with flat inventory, shade zero, asymmetry from outward grid rounding — architecture.md quote fold)
    ME->>ME: atomically expire all quotes with version ≤ 481 (zero stale-state fills)
    ME->>ME: rest new quotes — if books cross, ordinary matching occurs (bookmaker vs bookmaker)
    ME->>GW: market events (cancels, accepts, any fills)
    RE->>GW: game_state@482
    GW->>UI: push book, price-history point, replay clock

Key invariant: a quote is only valid against the state_version it was derived from. Version advance ⇒ automatic expiry before any new matching.

Best executable book at version 482: $0.44 / $0.47 — the spec's worked example, which Flow 2 trades against.


Flow 2 — User trade and cash-out #

The one asynchronous entry point. The append to the order log is the moment the action becomes history; everything after is derivation.

sequenceDiagram
    autonumber
    actor U as Bettor
    participant UI as Trading Screen
    participant GW as Gateway / Input Gateway
    participant OL as Order Log (store)
    participant CT as Complement Transform
    participant MG as Deterministic Merge
    participant ME as Matching Engine
    participant LD as Ledger (fold)

    U->>UI: click Higher, size 100, market T-kills
    UI->>GW: submit order
    GW->>OL: append (seq=17, ts=3134.75 — the latest published state's ts, per the stamping rule, buy higher 100, IOC, quoted_version=482)
    Note over OL: order is now immutable history — replay reproduces it exactly
    OL->>CT: Lower orders rewritten here — buy Lower at p becomes sell Higher at 1−p
    CT->>MG: normalized order
    MG->>ME: merged after quotes@482 — total order (replay_ts, class, stream, offset) with quotes before orders, per matching-engine.md §2
    ME->>ME: admission check vs own collateral view — a fold of market events + settlements, never a query to the ledger
    ME->>ME: IOC crosses best ask $0.47 × 100 — the trained bookmaker's ask from Flow 1 (price-time priority, partial fills allowed)
    ME->>LD: fill (taker user, taker_is_buy, maker bookmaker, px .47, qty 100, state_version 482)
    LD->>LD: user pays $47 into the market pot, maker pays the complement (1.00−0.47)×100 = $53 — the pair pre-funds the $1.00/unit worst case (pot = $1 × open interest, matching-engine §6)
    ME->>UI: fill confirmation via gateway — position long 100 at $0.47
    loop every subsequent tick (Flow 1)
        UI->>UI: mark position vs best bid = executable cash-out value
    end
    opt cash out before settlement
        U->>UI: click cash out
        UI->>GW: IOC sell 100 vs standing bid
        Note over GW,LD: identical path — append → merge → match → the pot pays the closing tranche at the trade price (matching-engine §6)
    end
Diagram source
sequenceDiagram
    autonumber
    actor U as Bettor
    participant UI as Trading Screen
    participant GW as Gateway / Input Gateway
    participant OL as Order Log (store)
    participant CT as Complement Transform
    participant MG as Deterministic Merge
    participant ME as Matching Engine
    participant LD as Ledger (fold)

    U->>UI: click Higher, size 100, market T-kills
    UI->>GW: submit order
    GW->>OL: append (seq=17, ts=3134.75 — the latest published state's ts, per the stamping rule, buy higher 100, IOC, quoted_version=482)
    Note over OL: order is now immutable history — replay reproduces it exactly
    OL->>CT: Lower orders rewritten here — buy Lower at p becomes sell Higher at 1−p
    CT->>MG: normalized order
    MG->>ME: merged after quotes@482 — total order (replay_ts, class, stream, offset) with quotes before orders, per matching-engine.md §2
    ME->>ME: admission check vs own collateral view — a fold of market events + settlements, never a query to the ledger
    ME->>ME: IOC crosses best ask $0.47 × 100 — the trained bookmaker's ask from Flow 1 (price-time priority, partial fills allowed)
    ME->>LD: fill (taker user, taker_is_buy, maker bookmaker, px .47, qty 100, state_version 482)
    LD->>LD: user pays $47 into the market pot, maker pays the complement (1.00−0.47)×100 = $53 — the pair pre-funds the $1.00/unit worst case (pot = $1 × open interest, matching-engine §6)
    ME->>UI: fill confirmation via gateway — position long 100 at $0.47
    loop every subsequent tick (Flow 1)
        UI->>UI: mark position vs best bid = executable cash-out value
    end
    opt cash out before settlement
        U->>UI: click cash out
        UI->>GW: IOC sell 100 vs standing bid
        Note over GW,LD: identical path — append → merge → match → the pot pays the closing tranche at the trade price (matching-engine §6)
    end

Flow 3 — Round end and settlement #

Settlement and the ledger live in the Clearing House — a separate logical component from the exchange runtime, communicating only via streams (never request/response). It runs off the hot path, so round-end settlement work can never touch the repricing budget. Co-located in one process; physically separable without redesign (see architecture.md scope ledger).

sequenceDiagram
    autonumber
    participant RE as Replay Engine
    participant LCM as Lifecycle Manager
    participant ME as Matching Engine
    participant SET as Settlement Engine
    participant LD as Ledger
    participant UI as Trading Screen

    RE->>LCM: round_end (round 17, canonical — reported_kills_t=3, reported_kills_ct=2, diagnostic per SC-1)
    LCM->>ME: market_closed (both team markets)
    ME->>ME: cancel all resting quotes, reject in-flight orders
    RE->>SET: round_end event (canonical cutoff per SC-2 — k recomputed from kill events per SC-1)
    SET->>LD: read final positions
    SET->>SET: Higher pays k/5 = $0.60 and Lower pays 1−k/5 = $0.40 (always sums to $1.00)
    SET->>LD: settlement events — user gets 100 × $0.60 = $60 (cost $47, profit $13), the short side gets 100 × $0.40 = $40 — the $100 pot drains to exactly zero (matching-engine §6)
    SET->>UI: settlement result displayed via gateway
    SET->>LCM: settlement_complete (round 17)
    Note over SET,LCM: barrier — market_open for the next epoch (CS2 epoch = round) is emitted only after settlement_complete — on this log it fires in the next epoch's own observation step, the round's first snapshot, and when the barrier is still outstanding there it fires on the barrier's own step instead (state-layers §3, Same-step open) — so balance availability is a deterministic function of stream order, not scheduler timing
    Note over SET,LD: settlement events are ordinary appended stream events — independently recomputable from the match log
Diagram source
sequenceDiagram
    autonumber
    participant RE as Replay Engine
    participant LCM as Lifecycle Manager
    participant ME as Matching Engine
    participant SET as Settlement Engine
    participant LD as Ledger
    participant UI as Trading Screen

    RE->>LCM: round_end (round 17, canonical — reported_kills_t=3, reported_kills_ct=2, diagnostic per SC-1)
    LCM->>ME: market_closed (both team markets)
    ME->>ME: cancel all resting quotes, reject in-flight orders
    RE->>SET: round_end event (canonical cutoff per SC-2 — k recomputed from kill events per SC-1)
    SET->>LD: read final positions
    SET->>SET: Higher pays k/5 = $0.60 and Lower pays 1−k/5 = $0.40 (always sums to $1.00)
    SET->>LD: settlement events — user gets 100 × $0.60 = $60 (cost $47, profit $13), the short side gets 100 × $0.40 = $40 — the $100 pot drains to exactly zero (matching-engine §6)
    SET->>UI: settlement result displayed via gateway
    SET->>LCM: settlement_complete (round 17)
    Note over SET,LCM: barrier — market_open for the next epoch (CS2 epoch = round) is emitted only after settlement_complete — on this log it fires in the next epoch's own observation step, the round's first snapshot, and when the barrier is still outstanding there it fires on the barrier's own step instead (state-layers §3, Same-step open) — so balance availability is a deterministic function of stream order, not scheduler timing
    Note over SET,LD: settlement events are ordinary appended stream events — independently recomputable from the match log

On a BOARD (several claim families on one match) the barrier NAMES ITS FAMILY: epochs are family-local, so two families' epoch 0 settle under two barriers, each settlement_complete / void_complete carrying the family whose epoch it closes, and the settlement consumers that key on the epoch — the exchange engine and the bookmaker fold — retire the markets whose (series.family, epoch) match (a bare barrier — the single-family run's, the only form it emits — retires the epoch's markets of every family, as before). The lifecycle fold is one fold PER FAMILY on a board: every fold sees every barrier on the merged feed and advances only on its own family's (a bare one is its own), so the §3 barrier is "the prior epoch OF ITS FAMILY" there — the scheduler wires one fold per column. The clearing engine holds one column per family, folding in the board's canonical (wire-name) order so the stream's cross-family record order is a function of the manifest (event-catalog §6.8).

The barrier is also the outcome's last use inside the settlement engine: settlement_complete retires the epoch in the clearing fold (a later void of it is the reversal path, out of scope — settlement-contract SC-V rule 4) and the title's outcome source releases what it recorded for the epoch with it (OutcomeSource::retire — no price for a retired epoch is ever asked; the void_complete barrier of Flow 5 retires the source the same way, though a voided epoch recorded nothing — SC-8 rule 3 / SC-V). What a source keeps is therefore bounded by the epochs between their finality and their barrier. The one title that keeps its settled outcomes past their barrier says so: CS2 retires nothing, because a post-cutoff kill still folds into the LAST settled round's diagnostic (settlement-contract SC-2; event-catalog §6.12 rule 9 for the corpus emission) — its map is bounded by a match's rounds (the epoch is the round_no, a later match's rounds overwrite), not the run. The clearing fold's own settled_epochs and settled_markets deliberately stay for the run: SC-V rule 4's refusal keys on them.

Match-winner family (LoL / Dota2 / synth; settlement-contract SC-7): the same flow with the title's match_complete (synth_complete on synth, event-catalog §6.18) in the role of the canonical round_end (the one finality signal — forwarded to the lifecycle, which closes the market on it, and read from the match log by clearing), epoch = the match, and the settlement price [0, 1000]: the winning side's claim pays $1.00 and the other side $0.00 per unit, the pot drains to exactly zero identically, and the settlement_complete barrier gates the next match's open. Worked instance (crates/clearing/tests/match_winner_settlement.rs): the house asks 640 × 500 on the blue claim (the architecture.md quote fold applied to the family's [0, 1000] vector; p = 0.4 lose / 0.6 win → fair 600.006 mills (39 322 000 q16), bid/ask 560/640 — the bookmaker::risk binary-ladder test), the account buys 100 → cost $64, pot $100; red wins → the account redeems $0, the house sleeve $100.

Race-to-kills family (LoL and Dota2; settlement-contract SC-8): the same flow per LINE with the first per-frame snapshot (state/v2 on LoL; the betty player_frame/v1 with its per-slot kills summed through the roster on Dota2) on which a side's cumulative kills reach the line in the role of the canonical round_end (read by the lifecycle off the game-state stream and by clearing off the match log through the title's own projector — one race fold, three drivers), epoch = the line (sequential lines as consecutive epochs, so the settlement_complete barrier gates the NEXT LINE's open, not the next match's), and the same [0, 1000] price. A tie on the crossing frame or a line the match ends before is Flow 5's unwind (SC-V) instead: market_voided → one void_refund per party → void_complete{unwind}, which lifts the next line's barrier exactly as settlement_complete does. The kills-at-minute family (SC-9) — and its per-side variant, the side-kills-at-minute family (SC-10: the same fold judging each line on its TEAM's own count; its serves' economics are the minute instance's to the mill, judged per team — runtime/tests/side_minute_serve.rs) and its gold variant, the gold-lead-at-minute family (SC-11: the same fold reading GOLD and judging each line on its team's lead — runtime/tests/gold_lead_serve.rs) and the tally families (SC-12: the same fold reading the frame reader's TALLY — towers taken, dragons or barons slain — and judging each line on its team's count — runtime/tests/tally_serve.rs) — is the same flow with the snapshot whose ORDINAL is the line's minute in the round_end role — OVER and UNDER are both settlements (the partition has no push), so only the match ending before the minute takes Flow 5's path. The house-line families (SC-13: the final total kills and the kill handicap over a line the HOUSE sets — runtime/tests/house_line_serve.rs) are the same flow with three sequence differences: the market is CREATED not on the match envelope but on the match's open_minute-th snapshot, with the line the setter chose on that snapshot, and — its barrier, the prior MATCH's settlement_complete, already folded by then — opened in the same lifecycle step; the forwarded match_complete stands in the round_end role with the outcome read off the LAST snapshot before it; epoch = the match, so the barrier gates the next MATCH's open, never a next line; and there is no Flow 5 path at all — a match ending before its opening snapshot has no market, and an opened market always settles (the partition has no push). Worked instance (crates/clearing/tests/house_line_settlement.rs, the race fixture's schedule): the last frame reads 19–18, total 37 — the total over 36 pays the claim (1000 mills to OVER, 0 to UNDER), the total over 37 pays the house (equal is UNDER); blue's lead 1 over 0 pays the claim, over 1 the house. Worked instance (crates/clearing/tests/race_to_kills_settlement.rs, the fixture's schedule, rebuilt in-crate): the account lifts the house's 640 ask for 100 on the race to 10 (cost $64, pot $100); minute 6 reads 11–11 — the line voids, $64 returns to the account and $36 to the sleeve, both back to their deposits; the race to 15 opens on that void_complete, the account lifts it again, minute 9 reads 15–13 — blue first, the account redeems $100, the sleeve $0. The minute family's worked instance (crates/clearing/tests/kills_at_minute_settlement.rs): the account lifts the 640 ask for 100 on minute 5's line over 17 (cost $64, pot $100); minute 5 reads 18 — over — and the account redeems $100, the sleeve $0; minute 8's line over 27 opens on that barrier, the account lifts it again, minute 8 reads exactly 27 — under — and the sleeve redeems $100, the account $0.


Flow 4 — Continuous ingestion (live / simulator) #

Out of scope (the decomposition below; the single-session GRID Series Events capture, in scope, is the deviation noted after the diagram). Downstream of the match log, nothing changes — live means the replay cursor rides the head. The live simulator, out of scope with the path, exercises it using historical data. Match Discovery, Tailing Fetcher, and Incremental Parser are the live-path decomposition of the single Demo Ingestor node in the Level 3 DAG.

sequenceDiagram
    autonumber
    participant SRC as CSTV Broadcast / Live Simulator
    participant DISC as Match Discovery
    participant FET as Tailing Fetcher
    participant PRS as Incremental Parser
    participant ML as Match Log (store)
    participant RE as Replay Engine (cursor at head)
    participant BM as Bookmakers
    participant ME as Matching Engine

    DISC->>ML: match_discovered, match_live (lifecycle events)
    FET->>SRC: GET /sync → fragment N
    loop every fragment (~seconds)
        FET->>SRC: GET /full/N, /delta/N+1, ...
        FET->>PRS: fragment bytes
        PRS->>ML: append parsed events (same schema as batch path)
        PRS->>ML: watermark (complete_up_to ts)
        ML->>RE: new events at head → Flow 1 fires
    end
    alt watermark stale beyond threshold (gap / reconnect)
        BM->>ME: pull all quotes (stale-data halt)
        ME->>ME: market halted — no version advance means no market
        FET->>SRC: resume from checkpointed fragment (append-only makes resumption trivial)
        FET->>PRS: backfill missed fragments, then tail
        BM->>ME: re-quote once watermark fresh
    end
    Note over SRC,ME: public broadcast is ~90–105 s delayed — the latency floor. Commercial feeds reduce it (licensing, not architecture)
Diagram source
sequenceDiagram
    autonumber
    participant SRC as CSTV Broadcast / Live Simulator
    participant DISC as Match Discovery
    participant FET as Tailing Fetcher
    participant PRS as Incremental Parser
    participant ML as Match Log (store)
    participant RE as Replay Engine (cursor at head)
    participant BM as Bookmakers
    participant ME as Matching Engine

    DISC->>ML: match_discovered, match_live (lifecycle events)
    FET->>SRC: GET /sync → fragment N
    loop every fragment (~seconds)
        FET->>SRC: GET /full/N, /delta/N+1, ...
        FET->>PRS: fragment bytes
        PRS->>ML: append parsed events (same schema as batch path)
        PRS->>ML: watermark (complete_up_to ts)
        ML->>RE: new events at head → Flow 1 fires
    end
    alt watermark stale beyond threshold (gap / reconnect)
        BM->>ME: pull all quotes (stale-data halt)
        ME->>ME: market halted — no version advance means no market
        FET->>SRC: resume from checkpointed fragment (append-only makes resumption trivial)
        FET->>PRS: backfill missed fragments, then tail
        BM->>ME: re-quote once watermark fresh
    end
    Note over SRC,ME: public broadcast is ~90–105 s delayed — the latency floor. Commercial feeds reduce it (licensing, not architecture)

Deviation — the GRID Series Events capture (d2-grid live, state-layers §1 GRID-live note, event-catalog §6.13 rules 12–13) is a single-session capture: no watermark, no checkpointed resume, no backfill; a transport failure aborts it and a retry is a fresh dial from fromSequenceNumber=0. The alt branch above is the CSTV / simulator decomposition's. The capture's own stall branch is its detector (event-catalog §6.13 rule 12 (d)): the capture appends feed_watch_armed first, feed_stalled when its rule's silence passes with no envelope, when a pending round's roster poll fails, or on a transport or IO error before it exits, and feed_recovered before the first fresh envelope's records once the continuity rule holds. The stall record is a tick of its own, at which the engine receives the pull of every bound sleeve with resting levels on a market Open and, under R7 v2, cancels every resting retail order on such a market after the pulls, and the run stays paused to the recovery record (risk-engine.md §3 R7) — no watermark and no market halt are involved.


Flow 5 — Round restore / market void #

Scope: the money path (the unwind — settlement-contract SC-V) is in scope and gated; the restore TRIGGER and the settled-then-voided reversal are out of scope (live operation), like Flow 4. In historical replays, rounds affected by a backup-restore are marked ineligible by the Flow 0 preflight and never open a market, so the CS2 restore TRIGGER and the settled-then-voided reversal stay out of scope; the unwind itself is exercised in historical replays by the race family's tie / unreached-line voids and the kills-at-minute, side-kills-at-minute, gold-lead-at-minute and tally families' unreached-minute voids (settlement-contract SC-8 / SC-9 / SC-10 / SC-11 / SC-12). It is specified because the append-only design makes incident handling cheap to define up front and expensive to retrofit.

CS2 mp_backup_restore: a round can be rolled back and replayed after a technical failure. A market may be mid-trading or already settled — or still pending behind the settlement barrier, or halted mid-gap (state-layers §3, the settlement-lag race) — for a round that officially un-happened. Immutability means we never edit — we append compensating events.

sequenceDiagram
    autonumber
    participant ING as Ingestor
    participant ML as Match Log
    participant RE as Replay Engine
    participant LCM as Lifecycle Manager
    participant ME as Matching Engine
    participant SET as Settlement Engine
    participant LD as Ledger
    participant UI as Trading Screen

    ING->>ML: append round_restored (round 17, superseded_from_offset k)
    ML->>RE: round_restored at cursor head
    RE->>LCM: round_restored (forwarded in the game-state stream)
    LCM->>ME: market_voided (round 17, both teams)
    ME->>ME: cancel all resting quotes
    alt round was already settled
        SET->>LD: append reversal events — unwind settlement payouts (out of scope: the fold halts with a typed error on this path, SC-V rule 4)
    else open, closed or pending — unsettled
        SET->>LD: append void_refund per party — its posted (the sum of its tranche cash) returns from the pot, and Σ posted = pot drains it to zero (matching-engine §6, settlement-contract SC-V)
    end
    LD->>UI: positions flat, balances restored, market shows VOID
    SET->>LCM: void_complete (round 17, mode unwind)
    Note over ML,LD: original events remain in the log forever — the void is new history, not an edit. Solvency checker validates the refunds (and the reversals, out of scope) like any other events
    RE->>LCM: round 17 replays → barrier honored, fresh market opens under a new market id
Diagram source
sequenceDiagram
    autonumber
    participant ING as Ingestor
    participant ML as Match Log
    participant RE as Replay Engine
    participant LCM as Lifecycle Manager
    participant ME as Matching Engine
    participant SET as Settlement Engine
    participant LD as Ledger
    participant UI as Trading Screen

    ING->>ML: append round_restored (round 17, superseded_from_offset k)
    ML->>RE: round_restored at cursor head
    RE->>LCM: round_restored (forwarded in the game-state stream)
    LCM->>ME: market_voided (round 17, both teams)
    ME->>ME: cancel all resting quotes
    alt round was already settled
        SET->>LD: append reversal events — unwind settlement payouts (out of scope: the fold halts with a typed error on this path, SC-V rule 4)
    else open, closed or pending — unsettled
        SET->>LD: append void_refund per party — its posted (the sum of its tranche cash) returns from the pot, and Σ posted = pot drains it to zero (matching-engine §6, settlement-contract SC-V)
    end
    LD->>UI: positions flat, balances restored, market shows VOID
    SET->>LCM: void_complete (round 17, mode unwind)
    Note over ML,LD: original events remain in the log forever — the void is new history, not an edit. Solvency checker validates the refunds (and the reversals, out of scope) like any other events
    RE->>LCM: round 17 replays → barrier honored, fresh market opens under a new market id

Flow 6 — Verification and evaluation #

Verification taps run alongside the runtime (every event, every offset — the solvency and equality taps do that in-process over the collateral folds' live state, state-layers §9). Evaluation runs offline on the held-out test split and produces the pass/fail gates.

sequenceDiagram
    autonumber
    participant ST as Stream Store (all logs)
    participant VSOL as Solvency Checker
    participant VSTALE as Stale-Fill Checker
    participant VSET as Settlement Checker
    participant VEQ as Collateral-View Equality Checker
    participant VDET as Determinism Harness
    participant EV as KPI Evaluator
    participant REP as Report Generator
    actor OP as Operator

    par continuous taps during the run
        ST->>VSOL: every market event → assert collateral ≥ liability at every offset (KPI zero violations)
        ST->>VSTALE: every fill → assert quote version was current at match time (KPI zero stale fills)
        ST->>VSET: settlement stream + match log → independent payout recompute (KPI zero errors)
        ST->>VEQ: market-event + settlement streams → the matching engine's collateral view equals the ledger's, field for field, at every shared offset outside an in-flight settlement or void group (matching-engine §6)
    end
    OP->>VDET: verify run
    VDET->>ST: re-run every transformer from declared inputs and run manifest
    VDET->>VDET: diff output stream hashes — byte-identical or FAIL (KPI determinism)
    OP->>EV: evaluate (test split only)
    EV->>ST: read probability streams A/B and recorded outcomes
    EV->>EV: RPS per snapshot → within-round average → aggregate with match-clustered 95% CI, calibration buckets (mean error below 5 pp), coverage ≥ 95%
    EV->>REP: model gates per regime (6-class: trained RPS ≤ 0.95 × baseline, CI above zero — 2-class match winner: any strict improvement, one-sided 95% bound above zero, model-plugin-layer §10 promotion regimes) plus market gates plus diagnostics (recorded, never gating)
    REP-->>OP: held-out evaluation report
    Note over EV,REP: a failed model gate is a valid negative result — do not compensate with UI polish or test-set changes
Diagram source
sequenceDiagram
    autonumber
    participant ST as Stream Store (all logs)
    participant VSOL as Solvency Checker
    participant VSTALE as Stale-Fill Checker
    participant VSET as Settlement Checker
    participant VEQ as Collateral-View Equality Checker
    participant VDET as Determinism Harness
    participant EV as KPI Evaluator
    participant REP as Report Generator
    actor OP as Operator

    par continuous taps during the run
        ST->>VSOL: every market event → assert collateral ≥ liability at every offset (KPI zero violations)
        ST->>VSTALE: every fill → assert quote version was current at match time (KPI zero stale fills)
        ST->>VSET: settlement stream + match log → independent payout recompute (KPI zero errors)
        ST->>VEQ: market-event + settlement streams → the matching engine's collateral view equals the ledger's, field for field, at every shared offset outside an in-flight settlement or void group (matching-engine §6)
    end
    OP->>VDET: verify run
    VDET->>ST: re-run every transformer from declared inputs and run manifest
    VDET->>VDET: diff output stream hashes — byte-identical or FAIL (KPI determinism)
    OP->>EV: evaluate (test split only)
    EV->>ST: read probability streams A/B and recorded outcomes
    EV->>EV: RPS per snapshot → within-round average → aggregate with match-clustered 95% CI, calibration buckets (mean error below 5 pp), coverage ≥ 95%
    EV->>REP: model gates per regime (6-class: trained RPS ≤ 0.95 × baseline, CI above zero — 2-class match winner: any strict improvement, one-sided 95% bound above zero, model-plugin-layer §10 promotion regimes) plus market gates plus diagnostics (recorded, never gating)
    REP-->>OP: held-out evaluation report
    Note over EV,REP: a failed model gate is a valid negative result — do not compensate with UI polish or test-set changes

One spec §10 gate has no system component by nature and so appears in no diagram: the product comprehension test. It is a manual UX protocol run against the finished trading screen — five people unfamiliar with the implementation; at least four must explain what the claim pays and complete an enter-and-cash-out cycle unaided; median time to first completed trade under 60 seconds. Its results go into the evaluation report alongside the automated gates, and passing it cannot override a failed model-quality gate.


Flow 7 — Crash recovery #

Recovery is not a special mode — it is the stream law exercised under duress: time travel to the last checkpoint, then replay to head. Because every piece of state is a fold over immutable logs, a crash can lose only unacknowledged bytes; everything else is recomputed, and the recomputation doubles as a determinism check.

sequenceDiagram
    autonumber
    participant PR as Process (restarted)
    participant ST as Stream Store (segments on disk)
    participant SN as Snapshot Manager
    participant RT as Runtime (all folds)
    participant GW as Gateway

    PR->>ST: scan all segments — verify length prefixes and hash chains
    ST-->>PR: torn tail detected on stream X at offset K (crash mid-append)
    alt torn tail on a DERIVED stream
        PR->>ST: truncate to last valid record — derived bytes are a cache, nothing is lost
    else torn tail on a ROOT log
        PR->>ST: truncate to last valid record — fsync-before-ack guarantees the lost record was never acknowledged to anyone
    end
    PR->>SN: load newest checkpoint at or below the truncation frontier
    SN-->>RT: fold states at offset C (derivable and discardable — never authoritative)
    RT->>ST: replay root logs from C to head through every transformer — same folds, same law
    RT->>RT: recomputed derived events MUST hash-match every surviving segment (free determinism check on every recovery)
    RT->>RT: resume at head — bookmakers quote at the current state_version
    GW->>GW: clients reconnect and re-subscribe from their last delivered offsets
    Note over PR,GW: recovery cost is bounded by snapshot cadence, never by history length — and a recovered run is byte-identical to one that never crashed
Diagram source
sequenceDiagram
    autonumber
    participant PR as Process (restarted)
    participant ST as Stream Store (segments on disk)
    participant SN as Snapshot Manager
    participant RT as Runtime (all folds)
    participant GW as Gateway

    PR->>ST: scan all segments — verify length prefixes and hash chains
    ST-->>PR: torn tail detected on stream X at offset K (crash mid-append)
    alt torn tail on a DERIVED stream
        PR->>ST: truncate to last valid record — derived bytes are a cache, nothing is lost
    else torn tail on a ROOT log
        PR->>ST: truncate to last valid record — fsync-before-ack guarantees the lost record was never acknowledged to anyone
    end
    PR->>SN: load newest checkpoint at or below the truncation frontier
    SN-->>RT: fold states at offset C (derivable and discardable — never authoritative)
    RT->>ST: replay root logs from C to head through every transformer — same folds, same law
    RT->>RT: recomputed derived events MUST hash-match every surviving segment (free determinism check on every recovery)
    RT->>RT: resume at head — bookmakers quote at the current state_version
    GW->>GW: clients reconnect and re-subscribe from their last delivered offsets
    Note over PR,GW: recovery cost is bounded by snapshot cadence, never by history length — and a recovered run is byte-identical to one that never crashed

Flow 8 — The allocation epoch (vault capacity, read by the engine) #

The Allocation Layer (allocation-market.md) runs ACROSS runs: one epoch document, one epoch log (root — written by the epoch gateway, an impure edge tailing the partner chain's final blocks), one derived allocation stream. Nothing here touches a run's hot path: a run reads the allocation once, as its manifest's sleeve ceilings, and the layer reads each run back once, at its run-sealed offset. Every money movement is the chain's; the engine records facts and computes.

sequenceDiagram
    autonumber
    participant OP as Operator
    participant CH as Partner chain (final blocks)
    participant EG as Epoch Gateway (impure edge)
    participant EL as Epoch log (root)
    participant AF as Allocation Fold
    participant AS as Allocation stream (derived)
    participant RUN as Run

    OP->>EG: the epoch document (the config loader edge — futarchy-system.md §7 E1)
    EG->>EL: epoch_opened, with the system hash
    EL->>AF: epoch_opened
    AF->>AS: document_checked (the fold re-runs the document checks)
    CH->>EG: a final block carrying a stake, or a move between pools
    EG->>EL: block_final, then stake_recorded or stake_moved (the block's row precedes its stakes)
    CH->>EG: the close block
    EG->>EL: block_final
    EL->>AF: fold to the close
    AF->>AS: book_closed, or epoch_aborted if a pool is unseeded or empty, or no one but the seeder voted
    AF->>AS: raw_share, capped_share by the note's redistribution, allocation
    Note over AF,AS: under a v2 document — the leverage and group caps and the cash minimum
    OP->>RUN: freeze a manifest at those ceilings (the shared-line chain: sublimits within each pool's line)
    OP->>EG: include the run
    EG->>EL: run_included (v2: with its sublimits), with its height
    RUN->>EG: the harness's verdict
    EG->>EL: run_verdict, with its height
    alt verified
        RUN->>AF: sealed streams
        AF->>AS: return_observed
    else failed — v1/v2 listed with nothing observed, v3 (B7 v5) the recovery
        Note over EL,AF: pending — the window waits, the line held, the pool's new inclusions refused
        RUN->>EG: the rebuilt record's verdict — the same manifest re-folded over the same root logs
        EG->>EL: a second run_verdict, verified, with its height
        RUN->>AF: the rebuilt sealed streams
        AF->>AS: return_observed at the second row's height
    end
    Note over AF,AS: repeats per run included before the deadline
    CH->>EG: the deadline block, the grace blocks, and the first final after them
    EG->>EL: block_final
    AF->>AS: window_closed, or epoch_aborted if a run is unjudged, on v3 if a run is still Failed at the grace end (run_unrecovered), or, on v1/v2, too few matches (v3: below the floor the window closes and settles on the fallback)
    AF->>AS: balance_series under a v2 or v3 document — one row per bookmaker per snapshot k = 0 … K, from the runs (v3: with the weekly and monthly returns)
    AF->>AS: v1/v2: utilization, winsorized, estimate, feasible_set, then settlement — v3: utilization, feasible_set, then settlement, the sparse fallback below the floor
    AF->>AS: pool_liability, then one stake_payout per net position
    AF->>AS: one participation_yield per futarchist under a v2 or v3 document, then epoch_settled
    Note over AS,CH: payouts and yield are published records — the vault pays payouts, the reward budget the yield
Diagram source
sequenceDiagram
    autonumber
    participant OP as Operator
    participant CH as Partner chain (final blocks)
    participant EG as Epoch Gateway (impure edge)
    participant EL as Epoch log (root)
    participant AF as Allocation Fold
    participant AS as Allocation stream (derived)
    participant RUN as Run

    OP->>EG: the epoch document (the config loader edge — futarchy-system.md §7 E1)
    EG->>EL: epoch_opened, with the system hash
    EL->>AF: epoch_opened
    AF->>AS: document_checked (the fold re-runs the document checks)
    CH->>EG: a final block carrying a stake, or a move between pools
    EG->>EL: block_final, then stake_recorded or stake_moved (the block's row precedes its stakes)
    CH->>EG: the close block
    EG->>EL: block_final
    EL->>AF: fold to the close
    AF->>AS: book_closed, or epoch_aborted if a pool is unseeded or empty, or no one but the seeder voted
    AF->>AS: raw_share, capped_share by the note's redistribution, allocation
    Note over AF,AS: under a v2 document — the leverage and group caps and the cash minimum
    OP->>RUN: freeze a manifest at those ceilings (the shared-line chain: sublimits within each pool's line)
    OP->>EG: include the run
    EG->>EL: run_included (v2: with its sublimits), with its height
    RUN->>EG: the harness's verdict
    EG->>EL: run_verdict, with its height
    alt verified
        RUN->>AF: sealed streams
        AF->>AS: return_observed
    else failed — v1/v2 listed with nothing observed, v3 (B7 v5) the recovery
        Note over EL,AF: pending — the window waits, the line held, the pool's new inclusions refused
        RUN->>EG: the rebuilt record's verdict — the same manifest re-folded over the same root logs
        EG->>EL: a second run_verdict, verified, with its height
        RUN->>AF: the rebuilt sealed streams
        AF->>AS: return_observed at the second row's height
    end
    Note over AF,AS: repeats per run included before the deadline
    CH->>EG: the deadline block, the grace blocks, and the first final after them
    EG->>EL: block_final
    AF->>AS: window_closed, or epoch_aborted if a run is unjudged, on v3 if a run is still Failed at the grace end (run_unrecovered), or, on v1/v2, too few matches (v3: below the floor the window closes and settles on the fallback)
    AF->>AS: balance_series under a v2 or v3 document — one row per bookmaker per snapshot k = 0 … K, from the runs (v3: with the weekly and monthly returns)
    AF->>AS: v1/v2: utilization, winsorized, estimate, feasible_set, then settlement — v3: utilization, feasible_set, then settlement, the sparse fallback below the floor
    AF->>AS: pool_liability, then one stake_payout per net position
    AF->>AS: one participation_yield per futarchist under a v2 or v3 document, then epoch_settled
    Note over AS,CH: payouts and yield are published records — the vault pays payouts, the reward budget the yield

Three steps of the sequence are the 19 October note's, BUILT under a v2 document (B3 v2, B4 v2, B6b, B8 v3, B9 v2, B10 v2 (B9/B10 v1/v2 chains; absent on v3), B11 v2, B12 v2 and B14b in the v2 chain declared_chain_v2 (task 143 step 3j-1) and folded end to end by the composition's v2 branch (step 3j-2) — futarchy-system.md §4): the redistribution step gains the leverage and group caps and the cash minimum; the balance series is derived from the runs once the window closes; the participation yield rows come before the epoch's end record. Under a v3 document (declared_chain_v3(), the Note on Futarchy as rewritten) the settlement scores the three unfiltered return series directly — no winsorized, no estimate — and a window below the match floor closes and settles on the stake-split fallback, taxed (futarchy-system.md §9 C93).

Four sequence facts carry the design. Config is the first edge input (CLAUDE.md law 8(b); futarchy-system.md §3): the loader reads the operator's document once, appends epoch_opened with the epoch id and the system hash, and from then on every block of the fold reads values through the stored document — no constant, no decoder default; the fold's first row, document_checked, re-runs the loader's checks. The close and the deadline are observed, not scheduled: block_final records every final block through the first final above the window's grace end, so the fold closes the book at M and the window at h* — the first final at or above M + window_blocks by whose height every included run has its verdict row (futarchy-system.md §9 C37), no later than the grace end — even when no stake or inclusion arrives after them — no clock, no timer. A run joins the window only after allocation: its manifest cites the record by offset and carries the record's capacities as its sleeve ceilings; the gateway refuses the inclusion otherwise, and the runtime refuses the manifest, so no run quotes under a ceiling the allocation did not grant (a cut restricts new quoting only — the matching-engine §6 headroom, never a pot). Settlement prices on the raw share: v_i = ŵ*_i / w^M_i uses the closing pool shares, never the capped w^I — the pari-mutuel accounting (the futarchy note, "Settlement"), which keeps the pools zero-sum before the tax and the cap.

Worked instance (allocation-market.md §12): pools cash / A / B close at (R_1, R_2, R_3) = (60 000, 25 000, 15 000) cents — w^M = (0.60, 0.25, 0.15), within the caps (1, 0.50, 0.50), so w^I = w^M and V = 1 000 000 mills gives V_i = (600 000, 250 000, 150 000): A quotes under 250 000 mills, B under 150 000, 600 000 stays in reserve; the window's runs settle; the optimizer returns ŵ* = (0.30, 0.40, 0.30) → v = (0.5, 1.6, 2.0); with τ_pool = 1/50 (the instance's illustration — the CONFIG default is 0, futarchy-system.md §9 C16) the pool liabilities are (29 400, 39 200, 29 400) and the tax 2 000, summing to 100 000.


Flow 9 — The desk period (margin call, liquidation, run-off) #

The desk-period chain (risk-engine.md §4) runs inside the allocation fold BETWEEN runs: at each run's verdict row and each top-up row it compares a desk's loss with its own capital and appends the desk records to the allocation stream. A run reaches it only through its sealed streams, and it reaches a run by refusing the next inclusion and by the loss band the inclusion pins in the run's manifest, and, from the first epoch whose rule chain declares R6 v1, every epoch before it carrying R41's reading unchanged (futarchy-system.md §9 R50; risk-engine.md §12 C91, C104), by the cross-run stop: the desk watch (risk-engine.md §8 E7) sends a reason-2 sleeve_suspend frame to the gateway of each of the desk's other unsealed runs, which appends sleeve_suspended{sleeve, 2 liquidated, 0} to that run's control root; the run's fold pulls the sleeve's quotes and posts no new one to its seal, its accepted bets riding to settlement. Inside a run R9 is the engine's own lever: it stops the sleeve's quoting where its realized loss reaches that band (risk-engine.md §3). The operator's pause, through the run's control root, is the other lever (risk-engine.md §5). The numbers are risk-engine.md §4's illustration: own capital 5 000, the call at a loss of 4 000, the liquidation at 4 500, the default at 5 000.

sequenceDiagram
    autonumber
    participant CH as Partner chain (final blocks)
    participant EG as Epoch Gateway (impure edge)
    participant EL as Epoch log (root)
    participant AF as Allocation Fold
    participant AS as Allocation stream (derived)
    participant OP as Operator
    participant DW as Desk watch (E7)
    participant RUN as The live run (its control root)
    participant RUN2 as Another live run of the desk (its control root)

    RUN->>EG: run 1's verdict
    EG->>EL: run_verdict, with its height
    AF->>AS: return_observed, pnl −4 200
    AF->>AS: margin_called, loss 4 200, base 5 000
    Note over AF,AS: a demand only — the desk keeps quoting and may be included again
    CH->>EG: a final block carrying a deposit of 500
    EG->>EL: block_final, then desk_topup
    AF->>AS: margin_cleared, loss 3 700
    OP->>EG: include run 2
    EG->>EL: run_included, manifest loss_band_mills 800
    OP->>RUN: sleeve_suspended, reason operator (a pause)
    Note over RUN: the quote fold pulls the sleeve's quotes, open positions ride to settlement
    OP->>RUN: sleeve_resumed
    RUN->>RUN: a settlement takes the sleeve's realized loss to 800 or more → quote_cancelled on its open markets, later batches quote_rejected{stop_loss} (R9)
    Note over RUN: open positions ride, the run closes at −1 000
    RUN->>EG: run 2's verdict
    EG->>EL: run_verdict, with its height
    AF->>AS: return_observed, pnl −1 000
    alt loss 4 700
        AF->>AS: margin_called, then liquidated, loss 4 700
    else run 2 at −1 300, loss 5 000
        AF->>AS: margin_called, then liquidated, then defaulted
    end
    Note over AF,AS: run 2 is sealed — nothing of it is live at that row
    opt shared line under R6 v1 — another run of the desk unsealed at that row
        DW->>RUN2: sleeve_suspend, reason liquidated (through its gateway, appended at its stamp)
        Note over RUN2: sleeve_suspended reason 2 — the pull at its stamp, no new quote to the seal, no resume · accepted bets ride with their collateral locked
    end
    OP->>EG: include run 3
    EG--xOP: refused — InclusionLiquidated
    Note over AS,CH: the desk records are published, the shortfall past own capital is the credit-economics chain's
Diagram source
sequenceDiagram
    autonumber
    participant CH as Partner chain (final blocks)
    participant EG as Epoch Gateway (impure edge)
    participant EL as Epoch log (root)
    participant AF as Allocation Fold
    participant AS as Allocation stream (derived)
    participant OP as Operator
    participant DW as Desk watch (E7)
    participant RUN as The live run (its control root)
    participant RUN2 as Another live run of the desk (its control root)

    RUN->>EG: run 1's verdict
    EG->>EL: run_verdict, with its height
    AF->>AS: return_observed, pnl −4 200
    AF->>AS: margin_called, loss 4 200, base 5 000
    Note over AF,AS: a demand only — the desk keeps quoting and may be included again
    CH->>EG: a final block carrying a deposit of 500
    EG->>EL: block_final, then desk_topup
    AF->>AS: margin_cleared, loss 3 700
    OP->>EG: include run 2
    EG->>EL: run_included, manifest loss_band_mills 800
    OP->>RUN: sleeve_suspended, reason operator (a pause)
    Note over RUN: the quote fold pulls the sleeve's quotes, open positions ride to settlement
    OP->>RUN: sleeve_resumed
    RUN->>RUN: a settlement takes the sleeve's realized loss to 800 or more → quote_cancelled on its open markets, later batches quote_rejected{stop_loss} (R9)
    Note over RUN: open positions ride, the run closes at −1 000
    RUN->>EG: run 2's verdict
    EG->>EL: run_verdict, with its height
    AF->>AS: return_observed, pnl −1 000
    alt loss 4 700
        AF->>AS: margin_called, then liquidated, loss 4 700
    else run 2 at −1 300, loss 5 000
        AF->>AS: margin_called, then liquidated, then defaulted
    end
    Note over AF,AS: run 2 is sealed — nothing of it is live at that row
    opt shared line under R6 v1 — another run of the desk unsealed at that row
        DW->>RUN2: sleeve_suspend, reason liquidated (through its gateway, appended at its stamp)
        Note over RUN2: sleeve_suspended reason 2 — the pull at its stamp, no new quote to the seal, no resume · accepted bets ride with their collateral locked
    end
    OP->>EG: include run 3
    EG--xOP: refused — InclusionLiquidated
    Note over AS,CH: the desk records are published, the shortfall past own capital is the credit-economics chain's

Flow 10 — Operator-routed retail (signed instructions, a resting order) #

An operator relays customers' signed instructions; it never holds their keys and never moves their money. The fold judges every instruction from its recorded bytes (matching-engine §5) and keeps each subaccount's balance, reservations and positions its own (matching-engine §6, whose worked example these numbers are). Alice's subaccount sits under operator A, Bob's under operator B; S is a supplier sleeve resting an ask of 20 @ 600.

sequenceDiagram
    autonumber
    actor AL as Alice (customer key)
    participant OA as Operator A (operator session)
    participant GW as Gateway
    participant OL as Order Log (root)
    participant ME as Matching Engine (exchange fold)
    participant MES as Market-event stream
    participant OB as Operator B (operator session)
    actor BO as Bob (customer key)

    OA->>GW: operator_hello/v1, then open_subaccount/v1 with Alice's public key and 100 000
    GW->>OL: subaccount_opened, operator A, verbatim key, offset = Alice's subaccount id
    GW-->>OA: order_ack, seq = the subaccount id
    AL->>OA: signed place — run id, subaccount, nonce 1, market, Higher, buy, 50, limit 600, GTC, quoted version
    OA->>GW: relay_instruction/v1 — the bytes and the signature
    GW->>OL: instruction_relayed, operator A, bytes and signature verbatim, offset = the order id
    Note over ME: decode, operator, verify_strict, nonce 1 consumed, open, stale, self-cross, funds 50 × 600 within 100 000
    ME->>MES: fill — taker Alice, maker sleeve S, 20 @ 600 — Alice pays 12 000, S pays 8 000, pot 20 000
    ME->>MES: retail_rested — the order, bid 30 @ 600, reserving 18 000
    AL->>OA: signed cancel — nonce 2, target order
    OA->>GW: relay_instruction/v1
    GW->>OL: instruction_relayed
    ME->>MES: retail_cancelled, customer, remaining 30 — 18 000 released, the 20 units kept
    OB->>GW: operator_hello/v1, then open_subaccount/v1 with Bob's public key and 50 000
    GW->>OL: subaccount_opened, operator B, verbatim key, offset = Bob's subaccount id
    GW-->>OB: order_ack, seq = the subaccount id
    BO->>OB: signed place — buy Higher 10 @ 650, GTC
    OB->>GW: relay_instruction/v1
    GW->>OL: instruction_relayed, operator B
    ME->>MES: retail_rested — Bob's order, bid 10 @ 650, reserving 6 500
    AL->>OA: signed place — nonce 3, sell Higher 10, limit 650, IOC
    OA->>GW: relay_instruction/v1
    GW->>OL: instruction_relayed
    ME->>MES: fill — taker Alice, maker account Bob, 10 @ 650 — Alice receives 6 500, Bob pays 6 500, pot unchanged
    opt the same bytes relayed again, or bytes with a changed size or side
        OA->>GW: relay_instruction/v1
        GW->>OL: instruction_relayed
        ME->>MES: instruction_rejected — stale_nonce for the replay, bad_signature for the altered bytes
    end
    opt operator A relays an instruction for a subaccount opened under operator B
        ME->>MES: instruction_rejected foreign_account — that subaccount untouched
    end
    opt Alice sells 11 Higher while holding 10
        ME->>MES: order_rejected insufficient_position — nothing moves
    end
Diagram source
sequenceDiagram
    autonumber
    actor AL as Alice (customer key)
    participant OA as Operator A (operator session)
    participant GW as Gateway
    participant OL as Order Log (root)
    participant ME as Matching Engine (exchange fold)
    participant MES as Market-event stream
    participant OB as Operator B (operator session)
    actor BO as Bob (customer key)

    OA->>GW: operator_hello/v1, then open_subaccount/v1 with Alice's public key and 100 000
    GW->>OL: subaccount_opened, operator A, verbatim key, offset = Alice's subaccount id
    GW-->>OA: order_ack, seq = the subaccount id
    AL->>OA: signed place — run id, subaccount, nonce 1, market, Higher, buy, 50, limit 600, GTC, quoted version
    OA->>GW: relay_instruction/v1 — the bytes and the signature
    GW->>OL: instruction_relayed, operator A, bytes and signature verbatim, offset = the order id
    Note over ME: decode, operator, verify_strict, nonce 1 consumed, open, stale, self-cross, funds 50 × 600 within 100 000
    ME->>MES: fill — taker Alice, maker sleeve S, 20 @ 600 — Alice pays 12 000, S pays 8 000, pot 20 000
    ME->>MES: retail_rested — the order, bid 30 @ 600, reserving 18 000
    AL->>OA: signed cancel — nonce 2, target order
    OA->>GW: relay_instruction/v1
    GW->>OL: instruction_relayed
    ME->>MES: retail_cancelled, customer, remaining 30 — 18 000 released, the 20 units kept
    OB->>GW: operator_hello/v1, then open_subaccount/v1 with Bob's public key and 50 000
    GW->>OL: subaccount_opened, operator B, verbatim key, offset = Bob's subaccount id
    GW-->>OB: order_ack, seq = the subaccount id
    BO->>OB: signed place — buy Higher 10 @ 650, GTC
    OB->>GW: relay_instruction/v1
    GW->>OL: instruction_relayed, operator B
    ME->>MES: retail_rested — Bob's order, bid 10 @ 650, reserving 6 500
    AL->>OA: signed place — nonce 3, sell Higher 10, limit 650, IOC
    OA->>GW: relay_instruction/v1
    GW->>OL: instruction_relayed
    ME->>MES: fill — taker Alice, maker account Bob, 10 @ 650 — Alice receives 6 500, Bob pays 6 500, pot unchanged
    opt the same bytes relayed again, or bytes with a changed size or side
        OA->>GW: relay_instruction/v1
        GW->>OL: instruction_relayed
        ME->>MES: instruction_rejected — stale_nonce for the replay, bad_signature for the altered bytes
    end
    opt operator A relays an instruction for a subaccount opened under operator B
        ME->>MES: instruction_rejected foreign_account — that subaccount untouched
    end
    opt Alice sells 11 Higher while holding 10
        ME->>MES: order_rejected insufficient_position — nothing moves
    end

Reading order #

New to the system: architecture.md Level 1 → Level 2 → Flow 1 → Flow 2 → Flow 3. Implementing: Level 3 stream DAG plus determinism rules, then the flow that touches your component. Going live later: Flow 4 and Flow 5 only — nothing else changes. The allocation epoch (Flow 8) sits across runs and touches a run only through its frozen manifest's sleeve ceilings. The desk period (Flow 9) runs inside the allocation fold between runs and touches a run by refusing its inclusion and by the loss band its manifest carries, which R9 enforces inside the run as the engine's own lever beside the operator's pause on its control root, and under R6 v1 by the desk watch's reason-2 suspension on the desk's other live runs' control roots. Operator-routed retail (Flow 10) is Flow 2 with an operator session between the bettor and the gateway and a signature the exchange fold verifies.