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
- Flow 1 — Runtime tick: state → quotes (~4Hz)
- Flow 2 — User trade and cash-out
- Flow 3 — Round end and settlement
- Flow 4 — Continuous ingestion (live / simulator)
- Flow 5 — Round restore / market void
- Flow 6 — Verification and evaluation
- Flow 7 — Crash recovery
- Flow 8 — The allocation epoch (vault capacity, read by the engine)
- Flow 9 — The desk period (margin call, liquidation, run-off)
- Flow 10 — Operator-routed retail (signed instructions, a resting order)
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 honestFlow 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 clockKey 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)
endFlow 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 logOn 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 idFlow 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 changesOne 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 crashedFlow 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 yieldThree 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'sFlow 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
endReading 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.