Block Trades
Two-sided privately negotiated trade booking on AX — submit, counterparty affirm, then book once to EP3 — landed as a minimal ROME built independently: just the two-sided booking core, which the full RFQ workflow later builds on.
§MMaterials
§0Principles
Consummate at AX; book exactly once to EP3
EP3's InsertTwoSidedBlockTrade is atomic and two-sided — both parties must be known at call time, and one-sided submission is explicitly unsupported by the proto. So the allege/affirm workflow lives entirely on the AX side: a block-trade ticket is submitted by one party, affirmed by the counterparty, and only the consummated ticket produces a single EP3 call. EP3 does not dedup this RPC (vendor-confirmed — see §2a), so the call is made once, ambiguous outcomes latch to manual reconciliation, and nothing ever auto-retries.
Margin is settled before the print
Margin books in two phases, mirroring the ticket lifecycle: at submit, the submitter's side is margin-checked and reserved; at accept, the counterparty's side is margin-checked and reserved in the same motion that consummates the ticket. By the time the trade prints (EP3 booking → drop-copy → positions), both sides' margin was already accounted for — the print converts reservations into real position margin, never surprises it.
A minimal ROME, built independently
ROME (docs/rfc/rome.md) designed the hard half of this feature — the gateway→service IPC spine, the EP3 booking call with its cross_id/idempotency hazards, durable Settling state, the ep3-mock — but block trades only barely touches ROME-the-RFQ-engine. So this plan builds a minimal rome independently on main: the booking core plus a directed two-sided ticket flow, zero quote machinery, with Tin's open stack as reference material rather than a work item. Alignment is kept where it's cheap (service identity, IPC envelope, §2g wire conventions, durable-state and audit-log patterns) so the RFQ stack rebases on top with minimal churn. One booking path, never two: the minimal rome is the sole EP3 caller, and RFQ negotiation later becomes another producer of consummated tickets into it.
Criticality ordering drives sequencing
The most critical aspect is the block trade booking API — the end-to-end submit → affirm → book-once-to-EP3 → print spine — so its shape freezes first (phase 1) and everything else builds against it. The full ranking, and what deliberately trails, is §2h.
Prototype scope is deliberate
AX is self-clearing, so the AIEX mockup's FCM/clearing-firm/broker fields don't apply; minimum block size and the reporting window are stubbed, not enforced; single instrument per ticket (no legs); OCPI-H100-PERP first but symbol-generic, since dated compute futures list imminently. Every cut is catalogued in §2e so the AIEX version starts from a list, not archaeology.
§1Progress / Tracker
Live snapshot — phase status and the bar below are computed from GitHub PR state at build time (2026-06-10). 0/9 phases done · 0%.
0Touch ROME lightly — alignment, not adoptionin progress›
Revised 2026-06-10 (§2d): the minimal rome is built independently on main; Tin's stack demotes to reference material and the RFQ rebase returns to ROME. What remains here: land #2087 (RFQ types in sdk-internal — already reviewed + adjusted; it carries the §2g wire conventions the ticket types follow), delete the dead develop-rome once it merges, and walk Tin through the revision — including the alignment seams held stable so his later rebase is cheap: the rome service/crate identity, the IPC envelope + connect-helper shape, the §2g wire conventions, and the Redis durable-state + ClickHouse audit-log patterns.
develop-rome deleted- #2087 RFQ types in sdk-internal — reviewed + adjusted (§2g); pending approval
1Block trade booking API — freeze the shape firstin progress›
The critical path starts here (§2h): define the booking API at both layers and freeze it early, so the GUI, tests, and margin work build against a stable shape. Client-facing: ticket commands (SubmitBlockTrade / AcceptBlockTrade / RejectBlockTrade / CancelBlockTrade) under a "bt" WS tag plus directed per-user allegation/lifecycle events. Internal: the ticket IPC enum in sdk-internal, the block_trade_log ClickHouse table, and Redis keys. Mirrors #2069's structure (request wrappers carrying caller_user_id / risk_checked_at_ns, PositiveDecimal, async-insert log-writer pattern) and follows the §2g wire conventions (prefixed-ULID TicketId, y for state, validate-on-ingest) — but as fresh code on main, with no dependency on the RFQ types. Public-SDK gating per §2d: the "bt" shape is frozen here; the public tag itself goes live in phase 4, behind a functional flow. Built 2026-06-10 as #2368 (base of the stack): ticket types + "bt" protocol + rome IPC surface in sdk-internal, the block_trade_log table + row types, and rome Redis keys — with the #2095 connect helper lifted in.
2Minimal rome: ticket state machine + EP3 booking corein progress›
Build the rome service fresh, sized to tickets: actor driver, durable Redis state (persist-before-publish, restart recovery), writer tasks, IPC listener — and zero quote machinery. The EP3 booking discipline is adopted wholesale from #2102's design, lifting code where it drops in cleanly: Ep3Booking / BookOutcome, fresh-ULID cross_id, Settling snapshot persisted before the call, off-actor booking task, the definitive-vs-ambiguous classifier, and the never-auto-retry latch. The ticket state machine (§2b) — pending_counterparty → booking → cleared plus rejected / canceled / expired terminals, TTL expiry on a deadline heap — emits a BookTrade effect and settles, rolls back, or latches on BookResult. Built 2026-06-10 as #2370: deliberate deltas from the RFQ reference are an explicit, durable NEEDS_MANUAL_RECONCILIATION state (Booking snapshots latch on restart instead of being dropped), definitive-reject rollback to PENDING_COUNTERPARTY, and restore-then-expire for stale pending tickets so the audit log never skips an expiry. Verified end-to-end against ep3-mock: one booking call, both drop-copy fills with block_trade_indicator, trade id = rome-minted cross id.
- #2370 the minimal rome service — state machine + EP3 booking core → alee/bt-ep3-mock
- Superseded / related
- #2070 driver / durable-state / IPC patterns — lifted selectively → tin/rome-internal-protocol-and-storage
- #2102 EP3 booking core — design adopted (classifier, off-actor task, never-retry latch) → tin/a-3216-rome-refactor
- #2101 log/schema alignment — folded into the fresh schema → tin/a-3216-ep3-mock-two-sided-block-trade
- #1843 older monolithic generation; nothing to cherry-pick
3Merge #2096 + #2104 as-is, retargeted to mainin progress›
The two pieces of Tin's stack taken as-is — rebuilding them has no value. The ep3-mock InsertTwoSidedBlockTrade is already complete for ticket tests — validates parties, preserves the caller's cross_id, emits drop-copy fills for both sides with block_trade_indicator = true — and the compose wiring (rome service, ROME_IPC_PORT, Dockerfile target) has no RFQ coupling. Retarget both at main and merge unchanged. Done 2026-06-10 by cherry-picking Tin's commits (authorship preserved) into the stack: #2369 carries #2096 below the rome service (rome's booking needs the new Ep3Client method), #2371 carries #2104 above it.
- #2369 #2096 retargeted to main — Tin's commit, unchanged → alee/bt-ticket-types
- #2371 #2104 retargeted to main — Tin's commit, unchanged → alee/bt-rome
- Superseded / related
- #2096 superseded by #2369 (same commit, retargeted) → tin/a-3216-rome-ipc-messages
- #2104 superseded by #2371 (same commit, retargeted) → tin/a-3216-order-gateway-rfq-ws
4Gateway wiring — the "bt" front doornot started›
Implement the order-gateway ⇄ rome spine ourselves, mirroring #2103's shape rather than reworking the PR: durable rome connection with reconnect, per-user directed event subscriptions, and the risk-check-before-IPC pattern — including the both-parties-one-receipt shape of check_accept_margin (shared risk_checked_at_ns for caller and counterparty). The "bt" WS tag frozen in phase 1 goes live on OrderGatewayRequest here; it is new public-SDK surface, so the #2068-revert policy (§2d) applies: it merges only with the ticket flow functional behind it (phases 2–3 landed), never ahead of it. The "rfq" tag plumbing from #2094 stays with ROME.
5Margin reservation: reserve at submit, settle at acceptnot started›
At submit, run the submitter's side through the order-gateway margin check and hold a reservation so a pending ticket can't be used to exceed limits; at accept, check + reserve the counterparty in the same transition that consummates the ticket — a failed counterparty check is a typed reject and the ticket stays pending. Mechanism (decided): pending tickets contribute to the order-gateway's existing open-order margin term (§2c). Reservations release on every terminal state, are replaced by real position margin when the drop-copy print lands, must be restart-safe (rebuilt from the durable ticket store), and stay coherent with position-feed-promotion's margin-authority cutover. Design can proceed in parallel with phase 2; the wiring lands after phase 4.
6Blotter queries + position flow-throughnot started›
Query endpoints over block_trade_log power a blotter of pending/affirmed/cleared tickets per account. Verify the booked trade flows through the existing machinery untouched: drop-copy → trade-engine2 trade/position rows, fees, PnL, and the order-gateway position cache for the next margin check. Public tape print with a block condition marker is deferred with ROME's tape work.
7GUI prototype: book, affirm, tracknot started›
Three surfaces in the web app, trimmed from the AIEX mockup to AX reality: a Book a block trade form (symbol, side, quantity, price, counterparty account — no FCM/clearing fields), an incoming allegations inbox with a confirm-to-affirm modal (the ROME GUI's confirm-then-send pattern), and a lifecycle blotter with the Draft → Submitted → Affirmed → Cleared timeline. Reuses the existing WS provider + order-entry validation plumbing. Can start as soon as phase 1 freezes the "bt" shape — it need not wait for phase 4 to merge.
8Lifecycle test matrix + demo rolloutnot started›
Per the repo's standing rule for order-lifecycle features: test client disconnect, server-initiated disconnect, rome/order-gateway restart/crash, and reconnect/recovery at every ticket state — including the submit-vs-reserve and accept-vs-book races and the ambiguous-EP3-outcome latch. (#2071's tests cover durable-state recovery but not this matrix — it stays ours.) End-to-end docker-stack run against ep3-mock, then deploy to demo and book real two-sided OCPI-H100-PERP blocks between test accounts.
Notebook
Reference design — the detailed mechanics behind the tracker.
§2aThe EP3 primitive — what the vendor surface actually supports
Everything below is from the proto vendored in-repo
(rs/ep3/api/protos/connamara/ep3/block_trades/v1beta1/ and
admin/v1beta1/admin.proto) plus vendor answers in
#ext-architect-connamara.
| Fact | Consequence for this plan |
|---|---|
InsertTwoSidedBlockTrade is two-sided and atomic — buyer + seller Party (user + account) required in one call; BlockTrade.side is documented "One-Sided trade (not currently supported)" |
The allege/affirm workflow must be AX-side; EP3 is called once, after consummation |
Single-leg only — one symbol, one price, one quantity; no leg fields anywhere |
No spreads in V1; vendor question pending (§2f) |
No dedup (vendor-confirmed 2026-05-26): identical retry "silently book[s] a second trade"; no idempotency_key on this RPC |
Never auto-retry; ambiguous outcomes latch to manual reconciliation; vendor RFE for idempotency_key stays open (ROME RFC §10) |
cross_id propagates (vendor-confirmed 2026-05-27) to both resulting Order records — queryable via SearchOrders(cross_id=…) and visible on the embedded Order in every drop-copy Execution |
Drop-copy self-healing and post-hoc reconcile both work; ROME RFC's Q9(iv) is resolved affirmative |
Drop-copy Executions carry block_trade_indicator = true and the EP3 trade_id |
Positions, margin cache, trade-engine, blotter all update via existing machinery — no new plumbing after booking |
TwoSidedBlockTrade has a broker Party and submitting_participant |
The future AIEX broker-of-record persona maps cleanly; unused in V1 |
Per-instrument BlockTradeThreshold (min quantity OR min notional) exists on the EP3 instrument |
The regulatory minimum-size knob already has a vendor-side home when AIEX needs it; unenforced in V1 |
The RPC requires Supervisor-admin auth and surfaces PERMISSION_DENIED as a definitive 4xx that plain InsertOrder doesn't |
Extend the order-gateway reject classifier's allowlist when wiring (ROME RFC §10) |
| No gRPC status-code specification exists; Connamara has an internal task to document it | The definitive-vs-ambiguous split below stays conservative until that doc lands |
Status-code classification (implemented in #2102's classify_booking_error,
restated here because the booking core inherits it): definitive-no-commit =
INVALID_ARGUMENT, FAILED_PRECONDITION, OUT_OF_RANGE, NOT_FOUND,
ALREADY_EXISTS, UNAUTHENTICATED, PERMISSION_DENIED,
RESOURCE_EXHAUSTED; ambiguous (latch, never retry) = UNAVAILABLE,
DEADLINE_EXCEEDED, INTERNAL, CANCELLED, ABORTED, UNKNOWN,
DATA_LOSS, UNIMPLEMENTED, plus any non-gRPC error.
§2bThe ticket state machine
A ticket is the AX-side record of a privately negotiated trade working
its way to consummation. EP3 is only involved in the booking → cleared
hop.
| State | Entered by | Margin effect | Exits |
|---|---|---|---|
pending_counterparty |
submitter's SubmitBlockTrade (passes margin check) |
submitter side reserved | accept / reject / cancel / expire |
accepted → booking |
counterparty's AcceptBlockTrade (passes margin check) |
counterparty side reserved; ticket consummated | EP3 call outcome |
cleared |
drop-copy confirms (or definitive RPC success) | reservations released; real position margin takes over | terminal |
rejected |
counterparty declines | submitter reservation released | terminal |
canceled |
submitter withdraws while pending | submitter reservation released | terminal |
expired |
TTL sweep | submitter reservation released | terminal |
needs_manual_reconciliation |
ambiguous EP3 outcome | both reservations held until an operator resolves | operator → cleared or rollback |
Design notes:
- Acceptance is the point of no return for the counterparty. Their
margin check runs inside the accept transition; if it fails, the accept
is a typed reject and the ticket stays
pending_counterparty. - Both directions are directed events on the per-user channel the ROME spine provides — the counterparty learns of the allegation the same way a ROME maker learns of a targeted RFQ.
- Reservations must never leak. Every terminal state releases them;
the
needs_manual_reconciliationlatch deliberately does not, since the trade may have booked. - Price is taken as given (privately negotiated). No price-band check in the prototype; flagged in §2e as an AIEX-tier requirement ("fair and reasonable" review).
§2cTwo-phase margin — mechanism
The decided policy (margin settled before the print) needs a mechanism for "reserve". Two candidates were considered; (1) is decided (2026-06-10):
- Synthetic open-order contribution (decided). The order-gateway
already computes an open-order margin term and publishes it
(position-feed-promotion phase 2). A pending ticket contributes to
that term exactly like a resting order of the same side/size/price —
submitter from
pending_counterparty, counterparty from the moment their accept passes. Reuses battle-tested code, shows up coherently inUserRiskSnapshot, and disappears naturally when the drop-copy print replaces it with real position margin. - Explicit hold ledger (rejected). A separate margin-holds structure
consulted by
can_send_order. More moving parts, a second source of truth, and it must reconcile against the print anyway.
Either way the reservation lives in the order-gateway (where
can_send_order runs), is keyed by ticket ID, and must be rebuilt on
restart from the durable ticket store. Coordinate with
position-feed-promotion: the reservation should sit on whichever margin
path is authoritative at the time it lands, behind the same
ORDER_GATEWAY_POSITIONS_SOURCE selection if both are still live.
§2dRelationship to ROME — the decided carve
Brett's framing (group DM, 2026-06-09): block trades for AX "use a subset of ROME which is the two sided trade booking thing." Concretely:
- Shared, landed by this plan (phases 1–4): the
romeservice + gateway IPC spine, the EP3 booking core with cross_id discipline and the ep3-mock, the ClickHouse audit-log pattern, directed per-user events, and the confirm-then-send GUI pattern. - This plan only: the directed two-sided ticket flow (§2b) and two-phase margin reservations (§2c) — RFQs margin-check at accept time only, tickets reserve earlier because they can pend for hours.
- ROME later, on top: RFQ negotiation (request → quotes → accept), the public RFQ protocol (#2087, #2094), anonymity/targeting (#1914), quote books, maker UX. When it lands, an accepted RFQ becomes another producer of consummated tickets feeding the same booking core — supersession is additive, not a rewrite.
Sequencing — first decided 2026-06-10 as restack the prefix
ourselves after a full diff review of the open stack, rather than
wait for the 11-PR stack to land (and never build a parallel
implementation — two independent callers of the no-dedup EP3 RPC was
ruled out outright). The findings from that review remain the ground
truth for what's worth lifting: #2102's booking core is hermetic;
~70% of the #2070 engine (driver, durable state, writers, IPC) is the
right shape; #2096's mock is already complete for two-sided ticket
tests; #1843/#1844 are a superseded generation with nothing to
cherry-pick; and develop-rome, the stack's base, is a dead branch
(no unique commits, 206 behind main). The restack itself was
superseded the same day — see the revision below.
Public-surface gating (established 2026-06-09): #2068 — the public
RFQ types — merged to main and was reverted within hours
(0bbb43a3d, Michael): the public SDK and docs must not advertise
RFQs before the feature is integrated. develop-rome + #2087 were
the response — an off-main integration branch re-homing the reverted
types (and #2087 holding that shared head branch open is what kept
#2069 pinned to its stale base). Decided instead: retire
develop-rome, land everything on main in pieces, and rework #2087
so the RFQ types live in sdk-internal until RFQ integration
ships, then graduate to the public SDK. The same policy governs this
plan's phase-4 "bt" WS tag.
Revision (2026-06-10, later) — build a minimal ROME
independently. Reading the plan back against the goal made it
clear: block trades barely touches ROME-the-RFQ-engine, and owning an
11-PR restack coupled this plan to RFQ review and rebase work that
isn't its business. Decided: build the minimal rome independently
on main — fresh PRs, sized to tickets — with Tin's stack demoted
to reference material, lifting code only where it drops in cleanly
(#2102's booking core; #2096/#2104 still merge as-is, having no RFQ
coupling). Alignment is kept where it's cheap, to cut the churn of
the RFQ stack's later rebase onto the minimal rome: the rome
service/crate identity, the IPC envelope + connect-helper shape, the
§2g wire conventions, and the Redis durable-state + ClickHouse
audit-log patterns. Unchanged from the restack decision: one
booking path (the minimal rome is the sole EP3 caller; RFQ later
produces consummated tickets into it), the diff-review findings
above, and #2087's sdk-internal landing (done 2026-06-10, reviewed +
adjusted, pending approval). What changes hands: the rebase of the
RFQ negotiation PRs returns to ROME — with the heads-up that the
upper stack's quote handlers match on the old RfqQuote enum
variants and switch to field access on optional bid/ask after
§2g.
Landed (2026-06-10, later still): the independent build shipped
as a four-PR stack targeting main — #2368 (types + schema + IPC
surface) → #2369 (#2096 retargeted, Tin's commit) → #2370 (the
minimal rome) → #2371 (#2104 retargeted). Stack order puts the
ep3-mock PR below rome because rome's booking task needs the new
Ep3Client::insert_two_sided_block_trade. Phase 4's gateway wiring
stacks on top, preserving the #2068-revert gating: the public "bt"
tag merges only above a functional flow.
§2eDeliberately deferred — the AIEX delta
The AIEX block-trade-desk mockup (June 2026) is the destination; this prototype is the AX-sized subset. Cut, with reasons:
- Minimum block size — regulatory requirement, not enforced in the
prototype. EP3's
BlockTradeThresholdis the natural enforcement point when AIEX rulebook numbers exist. - Reporting window / timeliness ("report within 15 min") — same; needs rulebook-sourced numbers and a policy decision (hard-reject vs accept-and-flag for surveillance — most DCMs do the latter).
- FCM / clearing firm / clearing account / counterparty-FCM fields — AX is self-clearing with direct members; no FCM entity model exists or is needed yet.
- Executing broker (IDB) as a third-party submitter — the EP3 proto
already carries a
brokerParty, but a broker role (submitting on behalf of accounts you can't trade) is a new permission concept; deferred with the AIEX persona. - Multi-leg / spreads — blocked on the vendor question (§2f) or on listing spread strategies as EP3 instruments.
- Dated futures contract months (H100 JUL-26 etc.) — listing dated compute futures is imminent but separate; this plan stays symbol-generic so they work the day they list.
- Settlement/value date, as-of (back-dated) trades — EFRP-flavored fields with no semantics for a daily-margined perp; revisit with AIEX.
- Fees — block-specific fee schedule in the transaction-engine; prototype books with existing per-account fee config.
- Price reasonability ("fair and reasonable") — no band check in the prototype; AIEX-tier surveillance item.
- Public tape
blockcondition marker — rides with ROME's tape work.
§2fVendor follow-up — atomic multi-leg block trades
Searched #ext-architect-connamara (2026-06-09): the multi-leg question has never been asked — only the dedup/idempotency thread (2026-05-25 → 27). The ROME RFC §10.1 asserts the RPC "accepts the multi-leg form", but the vendored proto contradicts that (single symbol/price/quantity, no leg fields). Needs vendor ground truth before multi-leg is planned. Draft for the channel:
Hi Connamara team — following up on
InsertTwoSidedBlockTrade: do you currently support, or plan to support, atomic multi-leg block trades (e.g. a calendar spread booked as one all-or-nothing transaction across two instruments, with per-leg prices)? The proto'sBlockTradecarries a single symbol/price/quantity, so today we'd book N legs as N independent calls and risk partial booking. If multi-leg isn't on the roadmap, is the recommended pattern listing the spread as its own instrument? (Related, still open from May: theidempotency_keyRFE for this RPC.)
While open, the fallbacks are: (a) N sequential single-leg bookings with a compensating-bust runbook for partial failure, or (b) spread-as- instrument. Neither is committed; V1 is single-leg regardless.
§2gWire conventions set in the #2087 review
Decided 2026-06-10 while reviewing #2087; they bind the ticket types
(phase 1) and the "bt" gateway surface (phase 4) as much as the RFQ
types they were written into:
y= state on the wire, for both row state and comma-separated state filters. (stwas rejected — it already means self-trade-prevention and is-snapshot elsewhere — and barescollides with symbol.)- Row types are noun-typed for graduation:
QuoteDetailsmirrors the publicOrderDetails;RfqFilled/RfqCancelAck/RfqRejectcarry the feature prefix so they read next toOrderFilledin generated client types. Ticket analogs follow suit (TicketDetails, …). - Prefixed-ULID ids (
R-…/Q-…, modeled onO-/L-OrderId); tickets mint their own prefix. - Optional-sides quote shape:
RfqQuoteis a struct ofOption<Decimal>bid/ask — key presence encodes sides; neither tagged nor untagged enum. Shape rules live invalidate()(≥1 side, positive prices, not crossed/locked) andSubmitQuoteRequest::validate()(positive quantity),ensure!-based. Ingest must callvalidate, and the gateway must echo the parsed quote (response or posted event): unknown keys are ignored on decode, so a mistyped side key silently degrades to one-sided — pinned insubmit_quote_ignores_unknown_keys. RfqQuoteSidestokens areb/a/2(two-sided).
§2hThe critical path — what matters most
Determined 2026-06-10 alongside the independent-build revision (§2d). Ranked:
- The block trade booking API — the end-to-end submit → affirm → book-once-to-EP3 → drop-copy-print spine. It is the product and the integration risk, so its shape freezes first (phase 1) and every other workstream — GUI, margin, tests — builds against it.
- Book-exactly-once discipline — the no-dedup RPC (§2a) makes the definitive-vs-ambiguous classifier, the Settling-before-call snapshot, and the never-auto-retry latch non-negotiable before any environment with a real EP3 behind it.
- Two-phase margin reservation — tickets can pend for hours; demo exposure without the submit-side reserve is an open limit bypass (phase 5).
- Durability + recovery — restart-safe ticket state and the phase-8 lifecycle matrix. Gates rollout, not development.
- Blotter + GUI — demo surfaces that trail the spine; they
start once phase 1 freezes the
"bt"shape and block nothing.
Explicit non-goals for the minimal rome: RFQ negotiation, quote books, anonymity/targeting, maker UX — all ROME's, none of it built or carried here.
§3Design Questions
-
Does EP3 support (or plan to support) atomic multi-leg block trades?
- Not yet answered Never asked in #ext-architect-connamara (verified 2026-06-09); ROME RFC §10.1's claim conflicts with the vendored proto. Draft message in §2f — Andrew to send. Determines whether spreads are native, N-call, or spread-as-instrument.
-
Should the allege/affirm flow be AX-side, with EP3 called only on consummation?
- Answered — affirmative Decided 2026-06-09. EP3's primitive is two-sided/atomic and one-sided is unsupported, so the ticket workflow lives in AX and EP3 is called once, after both sides affirm.
-
When do RFQ (and block-trade) types appear in the public SDK?
- Answered — affirmative Decided 2026-06-09 — Michael's revert of #2068 (
0bbb43a3d) sets the policy: public SDK surface ships only with an integrated feature. #2087's RFQ types are reworked into sdk-internal (done 2026-06-10, reviewed + adjusted per §2g, pending approval) and graduate when RFQ ships; phase 4's public"bt"WS tag likewise lands only with the ticket flow functional behind it (§2d).
- Answered — affirmative Decided 2026-06-09 — Michael's revert of #2068 (
-
Land Tin's stack, restack it ourselves, or build a minimal ROME independently?
- Answered — affirmative Decided 2026-06-10, revised the same day (§2d): build independently. The morning's call was to restack the bottom of Tin's stack; reading it back showed block trades barely touches the RFQ engine, so the minimal rome is built fresh on
mainwith Tin's PRs as reference, aligned where convenient to cut later rebase churn. Unchanged: one booking path for a no-dedup RPC — a second parallel EP3 caller stays ruled out; the minimal rome is the only one.
- Answered — affirmative Decided 2026-06-10, revised the same day (§2d): build independently. The morning's call was to restack the bottom of Tin's stack; reading it back showed block trades barely touches the RFQ engine, so the minimal rome is built fresh on
-
Margin policy — settled before the print?
- Answered — affirmative Decided 2026-06-09: reserve the submitter at submit, check + reserve the counterparty at accept, so the print never moves margin that wasn't already accounted.
-
What mechanism implements the margin reservation?
- Answered — affirmative Decided 2026-06-10 — pending tickets contribute to the order-gateway's existing open-order margin term (§2c option 1). Must be restart-safe (rebuilt from the durable ticket store) and coherent with position-feed-promotion's authority cutover.
-
Where do tickets durably live — Redis (ROME pattern) or Postgres?
- Answered — affirmative Resolved 2026-06-10: Redis, following #2070's
durable_state.rspattern (persist-before-publish + restart recovery; lift the code if it drops in cleanly), withblock_trade_login ClickHouse as the audit trail. Revisit Postgres only if the blotter's relational queries demand it.
- Answered — affirmative Resolved 2026-06-10: Redis, following #2070's
-
Who may a submitter allege against — any account, or a permissioned set?
- Not yet answered Prototype default: any trading account, visible to users with trade permission on it. Open whether counterparty selection needs a directory/ consent model before demo exposure (enumeration + spam-allegation concerns are real even at prototype scale).
-
What is the pending-ticket TTL?
- Not yet answered Proposal: end of trading day, with submitter-settable shorter expiry. Mostly matters because the TTL bounds how long a margin reservation can sit against the submitter.
-
Are minimum block size and the reporting window enforced in the prototype?
- Answered — negative Decided 2026-06-09 — left out of the prototype. EP3's
BlockTradeThresholdand a timeliness policy slot in at the AIEX tier (§2e).
- Answered — negative Decided 2026-06-09 — left out of the prototype. EP3's
-
Are multi-leg spreads, FCM fields, or dated futures in V1 scope?
- Answered — negative Decided 2026-06-09. Single-leg, single symbol; AX is self-clearing so no FCM/clearing-firm fields; OCPI-H100-PERP first with symbol-generic plumbing for the imminently-listing dated compute futures.
-
Will ROME supersede this front door?
- Answered — affirmative Decided 2026-06-09: this plan is the ROME subset landed first; RFQ negotiation later becomes another producer of consummated tickets into the same booking core (§2d).