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.

Environment: AX (self-clearing, direct members) V1 product: OCPI-H100-PERP (symbol-generic) Tracking: A-3622 Related: ROME — docs/rfc/rome.md Scope: Prototype Status snapshot: 2026-06-10

§MMaterials

AIEX block trades — teaser27s concept spot: "Negotiate & report block trades on The American Innovation Exchange."
Block trade desk — UI mockupThe AIEX destination UI — multi-leg spread entry, FCM/clearing fields, reporting-window timer, allege→match lifecycle. The AX prototype trims this per §2e.

§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%.

Done — 0 In progress — 4 Not started — 5
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.

awaiting sign-off Tin briefed on the independent build + alignment seams; 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.

  • #2368 ticket types, log schema, rome IPC surface — stack base
  • Superseded / related
  • #2069 structure mirrored — not a dependency → tin/rome-sdk-rfq-types
  • #2095 connect helper lifted into #2368 → tin/a-3216-rfq-sdk-protocol
  • #2087 lands via phase 0 (sdk-internal) — not needed by tickets
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.

  • Superseded / related
  • #2103 spine + risk-gate shape to mirror — reference, not a rework → tin/a-3216-rome-ep3-accept-booking
  • #2094 stays with ROME — public "rfq" WS tag → tin/rome-tests
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.

awaiting sign-off lifecycle matrix green (disconnect / restart / recovery at every ticket state)
awaiting sign-off two-sided block booked end-to-end on demo, margin verified at submit and accept

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
acceptedbooking 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_reconciliation latch 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):

  1. 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 in UserRiskSnapshot, and disappears naturally when the drop-copy print replaces it with real position margin.
  2. 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 rome service + 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 BlockTradeThreshold is 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 broker Party, 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 block condition 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's BlockTrade carries 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: the idempotency_key RFE 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. (st was rejected — it already means self-trade-prevention and is-snapshot elsewhere — and bare s collides with symbol.)
  • Row types are noun-typed for graduation: QuoteDetails mirrors the public OrderDetails; RfqFilled / RfqCancelAck / RfqReject carry the feature prefix so they read next to OrderFilled in generated client types. Ticket analogs follow suit (TicketDetails, …).
  • Prefixed-ULID ids (R-… / Q-…, modeled on O-/L- OrderId); tickets mint their own prefix.
  • Optional-sides quote shape: RfqQuote is a struct of Option<Decimal> bid/ask — key presence encodes sides; neither tagged nor untagged enum. Shape rules live in validate() (≥1 side, positive prices, not crossed/locked) and SubmitQuoteRequest::validate() (positive quantity), ensure!-based. Ingest must call validate, 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 in submit_quote_ignores_unknown_keys.
  • RfqQuoteSides tokens are b / a / 2 (two-sided).

§2hThe critical path — what matters most

Determined 2026-06-10 alongside the independent-build revision (§2d). Ranked:

  1. 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.
  2. 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.
  3. Two-phase margin reservation — tickets can pend for hours; demo exposure without the submit-side reserve is an open limit bypass (phase 5).
  4. Durability + recovery — restart-safe ticket state and the phase-8 lifecycle matrix. Gates rollout, not development.
  5. 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

  1. 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.
  2. 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.
  3. 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).
  4. 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 main with 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.
  5. 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.
  6. 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.
  7. Where do tickets durably live — Redis (ROME pattern) or Postgres?
    • Answered — affirmative Resolved 2026-06-10: Redis, following #2070's durable_state.rs pattern (persist-before-publish + restart recovery; lift the code if it drops in cleanly), with block_trade_log in ClickHouse as the audit trail. Revisit Postgres only if the blotter's relational queries demand it.
  8. 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).
  9. 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.
  10. 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 BlockTradeThreshold and a timeliness policy slot in at the AIEX tier (§2e).
  11. 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.
  12. 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).