Execution & orders

The one path that spends capital

Most of T3 Trade records decisions and state. Capital moves only when one service signs an order and sends it to the exchange. Its header states the boundary plainly: "This is the only code path that spends testnet capital." This page follows that path from an agent request to a recorded outcome.

submitIntent mapOrder + slippage second stop-gate wire price, item 17 persist + reserve before signing signInNonceLane serialized nonces POST /exchange signed action inspectOrderStatuses positional legs
THE SUBMIT SEQUENCE, REAL FUNCTION NAMES: ONE LANE, ONE INSPECTOR, NOTHING SKIPPED
7
submit entry points share one signer resolution, one nonce lane, one inspector
9
ordered steps in the submit sequence, and the code runs them in exactly that order
3
statuses, previewed / reserved / signed, from which a retry is allowed to submit again
20 s
bounded wait for the durable outcome; past it the honest answer is "still in flight"

Sources: apps/server/src/trading/HyperliquidExecutionService.ts:129-273, apps/server/src/trading/HyperliquidExecutionService.ts:5-15, apps/server/src/trading/HyperliquidExecutionService.ts:301-305, apps/server/src/trading/TradingExecutionOutcome.ts:43-47.

Seven entry points share one submission path

The module doc of HyperliquidExecutionService spends its first line on scope: "This is the only code path that spends testnet capital" apps/server/src/trading/HyperliquidExecutionService.ts:4. Seven public submit functions live behind that claim: the harness entry, the cancel, the protective stop, the deterministic exit, the working-order replacement, and two manual-lane siblings apps/server/src/trading/HyperliquidExecutionService.ts:129-273.

Every entry point uses the same final submission sequence. One signer resolution, fail-closed when no interim signer is armed apps/server/src/trading/HyperliquidExecutionService.ts:503-519. One serialized nonce lane, and the comment is blunt about why the block exists once: "a second copy of this block would be a second chance to sign outside the lane" apps/server/src/trading/HyperliquidExecutionService.ts:521-552. One response inspector that reads every per-order status apps/server/src/trading/HyperliquidExecutionService.ts:440-489.

This shared sequence replaces duplicated signer logic. The signer resolution "used to be pasted at six call sites" apps/server/src/trading/HyperliquidExecutionService.ts:503-507. Cancels and protective stops now take the same lane as orders explicitly, "so cancels serialize with orders and never race a nonce" apps/server/src/trading/HyperliquidExecutionService.ts:972-975, and protection "never races an order for a nonce" apps/server/src/trading/HyperliquidExecutionService.ts:1014-1016. The signing material itself is covered on Signer & authority.

The signature domain is derived from the configured endpoints, not from a second flag, "so the signature domain can never disagree with the exchange the signed action is sent to" apps/server/src/trading/HyperliquidExecutionService.ts:499-501. Testnet is the only execution target the repository wires.

THE SEVEN SUBMIT ENTRY POINTS, ONE SERVICE
submitOrder
the harness entry: full checklist preview, then the submit sequence
submitCancel
cancel one resting order by cloid; confirmed only by an exchange acknowledgement
submitProtectiveStop
independent reduce-only stop, sent with na grouping so it outlives its parent
submitReduceOnlyIoc
the deterministic exit; no preview because the exchange enforces reduce-only
submitWorkingEntry
re-place an already approved working order outside a harness turn
submitManualOrder
the user's ticket; manual cloid namespace, rows carry mission_id NULL
submitManualReduceOnlyIoc
manual reduce or close, the same preview-free exit with manual identity

apps/server/src/trading/HyperliquidExecutionService.ts:129-273, apps/server/src/trading/HyperliquidExecutionService.ts:140-153, apps/server/src/trading/HyperliquidExecutionService.ts:97-107.

One order, from request to recorded result

This is the full lifecycle of one entry in code order. Steps 1 through 6 prepare and validate the request. Steps 7 through 15 submit it. The final steps update local state from the exchange and return the recorded result to the agent runtime.

STEP 01 · INTENT

The harness names a trade, not an order

Inside a harness turn, the trading_enter MCP tool receives what a language model can actually state: a market, a side, a stop price, and optionally a size or notional. Urgency is the model's vocabulary, never a wire concept: "now crosses, patient rests" apps/server/src/trading/TradingEntryService.ts:286-290.

apps/server/src/trading/TradingEntryService.ts:69-83

STEP 02 · PREPARE

The server derives eight values the model cannot know

The execution request once required eight values that the model could not reliably provide. They include current strategy and authority versions, the run that holds the decision lease, an execution sequence, a marketable limit price, a size within four limits, the planned loss, and exchange-valid precision. "Every one of them is a refusal when it is wrong, and all eight are things the server already knows" apps/server/src/trading/TradingEntryService.ts:5-11. TradingEntryService.prepare reads the mission, the lease, the book, the account and the budget, derives a feasible size against the ceilings apps/server/src/trading/TradingEntryService.ts:362-399, and runs the same checklist the submit will run apps/server/src/trading/TradingEntryService.ts:423-428.

apps/server/src/trading/TradingEntryService.ts:2-24

STEP 03 · THE EVENT

The tool waits for a recorded execution result

The tool handler dispatches a trading.execution.requested command onto the orchestration spine, then blocks on the outcome service instead of answering itself. The comment at the dispatch site states the rule: "The dispatch is a question; the reactor answers it on its own worker", because a harness told "submitted" for a request that was refused at preview "goes on to manage a position that does not exist" apps/server/src/mcp/toolkits/trading/handlers.ts:856-872. The wait is bounded at 20 seconds; past it, the answer is that the execution is still in flight apps/server/src/trading/TradingExecutionOutcome.ts:43-47.

apps/server/src/mcp/toolkits/trading/handlers.ts:849-888 apps/server/src/trading/TradingExecutionReceipts.ts:2-19

STEP 04 · REACTOR INTAKE

The mission moves to executing, then reconciles

TradingMissionReactor.processExecutionRequested first advances the mission from waiting or position_open to executing, before preview, "so preview reads the status this request just established" apps/server/src/trading/TradingMissionReactor.ts:1871-1883. Then it reconciles canonical exchange state with the before_execution trigger, "so preview and the budget gate see reconciled truth, not a stale local cache" apps/server/src/trading/TradingMissionReactor.ts:1890-1895. The event spine that carries the request is covered on Event spine; the status machine those mission states belong to is on Missions watches wakes.

apps/server/src/trading/TradingMissionReactor.ts:1865-1895

STEP 05 · THE GUARD

Exhaustion blocks increases before a nonce is spent

With the loss budget evaluated from reconciled state, the §16.4 guard runs: guard.guardAction blocks position-increasing actions when the budget is exhausted, "before the submit sequence spends a nonce. Cancel/reduce/close pass through" apps/server/src/trading/TradingMissionReactor.ts:1951-1953. What the budget counts and how it exhausts is the Risk control story.

apps/server/src/trading/TradingMissionReactor.ts:1897-1953

STEP 06 · ROUTING

The action type selects one of five write paths

close goes to the guard's reduce-only close and reduce to its sized variant; both are forced reduce-only on the wire and sized against the canonical position, which is what stops a reduce from crossing through flat into an unprotected reversal. cancel and modify_stop are deterministic paths answered directly. Everything else, the entries and scale-ins, reaches execution.submitOrder apps/server/src/trading/TradingMissionReactor.ts:1983-2008.

apps/server/src/trading/TradingMissionReactor.ts:1989-2008

STEP 07 · SIGNER

Resolve the signer, fail-closed

submitOrder begins at step 1 of the module's own numbered list: resolve the interim signer, and if none is armed the sequence stops with signer_not_configured rather than a defect apps/server/src/trading/HyperliquidExecutionService.ts:870-871. No signer, no signed action, no capital spent.

apps/server/src/trading/HyperliquidExecutionService.ts:5-15 apps/server/src/trading/HyperliquidExecutionService.ts:503-519

STEP 08 · FRESH BOOK, THEN PREVIEW

The checklist runs against the live book

Market metadata and a fresh best bid and offer are resolved next apps/server/src/trading/HyperliquidExecutionService.ts:873-879, and the §16.3 preview runs with that book in context apps/server/src/trading/HyperliquidExecutionService.ts:881-896. The checklist is 14 ordered items for entries, from mission_active through leverage, gross notional, the per-position planned-loss ceiling, reservations plus the proposal within budget, and a valid stop apps/server/src/trading/TradingPreviewService.ts:39-66.

apps/server/src/trading/TradingPreviewService.ts:39-66

STEP 09 · MAP

Intent becomes a wire order

Inside submitIntent, mapOrder turns the intent into the exchange's shape: IOC or GTC, priced off the fresh BBO with the slippage allowance, sized to the exchange's precision, and stamped with the deterministic cloid apps/server/src/trading/HyperliquidExecutionService.ts:577-603. The manual path overrides the cloid with one derived from the account instead of the mission apps/server/src/trading/HyperliquidExecutionService.ts:584-594. The typed shapes that travel this boundary, intent to record, are covered on Contracts.

apps/server/src/trading/HyperliquidExecutionService.ts:562-603

STEP 10 · THE SECOND STOP-GATE

The stop is re-checked at the price going on the wire

Preview already ran the stop check against the limit the harness requested. This gate runs the same checkStopInformation against the price actually going on the wire, which for a marketable IOC is the BBO-derived limit, not the requested one. An increase whose stop stopped making sense between preview and mapping "is refused here rather than signed. Nothing has been persisted and no nonce has been spent at this point" apps/server/src/trading/HyperliquidExecutionService.ts:605-624.

apps/server/src/trading/HyperliquidExecutionService.ts:605-624

STEP 11 · THE LINKED CHILD

An increase ships with its stop in one grouped action

When the action increases a position and carries a stop, the entry and a reduce-only trigger child go out together in one normalTpsl grouped action apps/server/src/trading/HyperliquidExecutionService.ts:626-666, with the child's cloid derived from the same triple plus the suffix _protect apps/server/src/trading/HyperliquidExecutionService.ts:276-281. The child is sized to the requested size because no fill has happened yet, and the code is explicit that submitting it "proves nothing about whether the child is live"; reconciliation against canonical state is what confirms protection.

apps/server/src/trading/HyperliquidExecutionService.ts:626-666

STEP 12 · PERSIST BEFORE SIGNING

The record and the reservation exist before the signature does

Step 5 of the module's list: persist the execution record and the risk reservation before signing. The record is inserted with status reserved apps/server/src/trading/HyperliquidExecutionService.ts:668-732, keyed by an idempotency key minted here, and the insert upserts on conflict apps/server/src/trading/HyperliquidExecutionService.ts:343. The reservation row follows apps/server/src/trading/HyperliquidExecutionService.ts:774-784. If the process dies between these writes and the POST, the system still knows an order may be in flight.

apps/server/src/trading/HyperliquidExecutionService.ts:320-354 apps/server/src/trading/HyperliquidExecutionService.ts:356-384

STEP 13 · THE RETRY READ-BACK

The row is read back, and only three statuses may proceed

Immediately after the insert, the record is selected back by idempotency key. If its status is anything other than previewed, reserved or signed, the persisted record is returned and nothing is submitted apps/server/src/trading/HyperliquidExecutionService.ts:734-772. That read-back is the whole retry mechanism, and it is local: the exchange is never asked whether it saw this action before.

apps/server/src/trading/HyperliquidExecutionService.ts:290-305

STEP 14 · SIGN AND SEND

One nonce lane, then the wire

The action, single order or grouped pair, is signed inside signInNonceLane apps/server/src/trading/HyperliquidExecutionService.ts:786-798. The record is marked submitted apps/server/src/trading/HyperliquidExecutionService.ts:801, and only then is the signed action POSTed to /exchange apps/server/src/trading/HyperliquidExecutionService.ts:803. The lane keeps nonces strictly monotonic and serializes the whole assign, sign and submit section packages/hyperliquid/src/NonceCoordinator.ts:4-18.

apps/server/src/trading/HyperliquidExecutionService.ts:798-811

STEP 15 · INSPECT

Every per-order status, attributed by position

The response is inspected leg by leg. The exchange returns one status per order in submission order, so each row is attributed to the leg sent at that index, because "a filled row does not reliably echo the cloid back" apps/server/src/trading/HyperliquidExecutionService.ts:440-452. An action-level rejection that carries no rows at all is reported as a failure, not an empty success apps/server/src/trading/HyperliquidExecutionService.ts:467-472. The entry leg, not "some leg succeeded", decides the record's final status of filled, accepted or rejected, and a rejection releases the risk reservation apps/server/src/trading/HyperliquidExecutionService.ts:816-844.

apps/server/src/trading/HyperliquidExecutionService.ts:440-489

STEP 16 · CONVERGE AND SETTLE

Reconcile, protect, re-read the budget, open the latch

Back in the reactor, the after_submission reconcile converges local state to the exchange apps/server/src/trading/TradingMissionReactor.ts:2018-2023, then protectIncrease confirms protection for the actual canonical position, because the grouped child that went out with the entry proves nothing apps/server/src/trading/TradingMissionReactor.ts:2025-2032. The budget is re-read, and if it is now exhausted the mission is blocked and told so apps/server/src/trading/TradingMissionReactor.ts:2034-2070. Finally the receipt latch opens and the waiting tool reads the durable record once apps/server/src/trading/TradingExecutionReceipts.ts:10-19. What reconciliation repairs along the way is the Reconciliation page.

apps/server/src/trading/TradingMissionReactor.ts:2018-2070

The record exists before the signature does

Most systems sign first and log after. This one writes the execution record and the risk reservation first, then signs apps/server/src/trading/HyperliquidExecutionService.ts:668-784. The ordering is the crash story: a process that dies between the INSERT and the POST leaves a record still inside the retry window, and any later attempt of the same action reads that row back and refuses to submit again apps/server/src/trading/HyperliquidExecutionService.ts:734-772. The unknown is contained by a durable fact instead of a hope.

The statuses carry exact meaning. filled is written when the exchange says filled, because an IOC reports its own outcome in the submit response and "there is no later reconciliation question to leave open" apps/server/src/trading/HyperliquidExecutionService.ts:824-832. accepted now "means only what it says", acknowledged and resting on the book. rejected releases the reservation the same instant apps/server/src/trading/HyperliquidExecutionService.ts:842-844, so a refused order never keeps claiming budget.

The design deliberately prevents retries after the status becomes submitted. After the signed action is POSTed and before the response is read, the record says submitted and nothing more. A crash there is indistinguishable from success at the exchange, so the record is treated as spent.

WHY SUBMITTED IS NEVER RESUBMITTED

submitted is deliberately absent from PRE_SUBMISSION_STATUSES, even though its outcome is unknown: "An unresolved submission is exactly the case that duplicates a position if it is retried blind, so the retry returns the unresolved record and leaves it to reconciliation to settle" apps/server/src/trading/HyperliquidExecutionService.ts:296-305.

THE RECORD'S STATUS RAIL, ONE SUBMISSION
previewed the checklist passed; nothing is on the wire yet
reserved record written, budget claimed, still unsigned
signed signature taken in the nonce lane, POST not yet sent
submitted on the wire; outcome unknown, retry refuses, reconciliation settles
filled the exchange's own answer to its submit; terminal
accepted acknowledged and resting on the book, still managed
rejected refused; the risk reservation is released at once

The three cyan states are the retry window apps/server/src/trading/HyperliquidExecutionService.ts:301-305. In practice the record is written as reserved apps/server/src/trading/HyperliquidExecutionService.ts:725 and jumps to submitted after signing apps/server/src/trading/HyperliquidExecutionService.ts:801; the entry leg decides the final state apps/server/src/trading/HyperliquidExecutionService.ts:833-841.

Idempotency is local, and it is a row in SQLite

The exchange does not deduplicate marketable orders, so every guard against double execution lives in this repository. Six mechanisms carry that load together.

idem_mission_seq_action

The deterministic idempotency key, built as idem_ plus the mission id, the per-mission monotonic execution sequence, and the action type apps/server/src/trading/HyperliquidExecutionService.ts:673-676. The manual lane mints the same shape under idem_manual_ with the account as owner. Retries of one action compute the same key and land on the same row.

the upsert

The record insert carries ON CONFLICT(idempotency_key) DO UPDATE SET updated_at apps/server/src/trading/HyperliquidExecutionService.ts:343, and the reservation insert is idempotent on the execution id apps/server/src/trading/HyperliquidExecutionService.ts:373. A second attempt of the same action touches the same rows rather than growing new ones.

the read-back gate

Right after persisting, the row is selected by key and its status decides everything: only previewed, reserved or signed may proceed to the wire apps/server/src/trading/HyperliquidExecutionService.ts:734-772. The module doc states the reason in one line: "The exchange itself does NOT deduplicate" apps/server/src/trading/HyperliquidExecutionService.ts:17-23.

the cloid

A client order ID, or cloid, links an exchange order to its local execution record. It is "a correlation id, not an idempotency key". Hyperliquid enforces cloid uniqueness only among resting orders, and a marketable IOC never rests, so a resubmitted one "is verified live to open a second order that fills again". What the stable cloid buys is reconciliation: the Info API echoes it on the order and its fills, so a fill joins the execution record that caused it packages/hyperliquid/src/Cloid.ts:8-15. The derivation is SHA-256 over the mission, sequence and action type, first 16 bytes, hex with the 0x prefix packages/hyperliquid/src/Cloid.ts:17-20.

the 0x prefix

The prefix is not decoration. A prior version returned a bare 32-character hex string; the live entry it produced was accepted by the exchange, but reading it back showed it had been stored as "cloid": null, "so retry-deduplication rested entirely on local idempotency-key convergence, never on exchange-side cloid match" packages/hyperliquid/src/Cloid.ts:22-29. The 0x form is now applied at the single boundary where cloids are made, so one representation flows to the wire, the database, the reconciler's join and the UI.

the nonce lane

Idempotency stops double execution of the same action; the nonce lane stops two different actions from racing one signature slot. Nonces are strictly monotonic, fast-forwarded to current Unix milliseconds when the wall clock has moved past the last issued value, and the whole assign, sign, submit section runs under a single permit packages/hyperliquid/src/NonceCoordinator.ts:4-18. A restart needs no persisted nonce state: "real time has always moved past anything signed before it" packages/hyperliquid/src/NonceCoordinator.ts:8-13.

No market order exists, so price is policy

Hyperliquid has no market order type. Every "market" fill is a limit order priced to cross, and the crossing distance is a number somebody chose. The repository keeps those choices visible, and owns two further lanes beyond the immediate entry.

Crossing is a policy number

"There is no market order on Hyperliquid. Every 'market' fill is a limit order priced to cross, and the crossing distance is a policy number: too tight and the order rests unfilled, too wide and a thin book fills it somewhere the loss model never anticipated" apps/server/src/trading/IocSlippageConfig.ts:4-7. The two allowances live in one module so the two paths are "visibly two different policies rather than two accidents", tunable per testnet run without a rebuild.

entry / harness IOC50 bps
deterministic exit IOC100 bps

apps/server/src/trading/IocSlippageConfig.ts:1-29

The exit is twice as wide

The deterministic exit may cross 100 bps against the entry's 50, and the asymmetry is deliberate: "An entry that misses can be retried at leisure; an exit that misses leaves the exposure it was placed to escape" apps/server/src/trading/IocSlippageConfig.ts:18-19. Defaults 50 and 100 apps/server/src/trading/IocSlippageConfig.ts:26-29; a knob that fails to parse falls back rather than refusing to trade apps/server/src/trading/IocSlippageConfig.ts:53-56.

T3_TRADES_IOC_SLIPPAGE_BPS · T3_TRADES_IOC_EXIT_SLIPPAGE_BPS

Working orders: patience with an owner

A patient entry is a post-only ALO at the near side, and a post-only order only fills when the market comes to it. TradingWorkingOrderService owns the wait and gives every resting entry one of four answers per pass: nothing while it is young, a re-price at the current near side, a cross once the stated max wait is up, or an abandon apps/server/src/trading/TradingWorkingOrderService.ts:4-19. Entries cancel first and place after, because overlapping entries are double exposure, and every replacement re-places the entry with its grouped stop child together apps/server/src/trading/TradingWorkingOrderService.ts:49-69.

nothing · re-price · cross · abandon

The manual lane: the user is the strategy

The manual ticket is the mirror of the agent's prepare with the mission machinery removed: no mandate, no lease, no plan target. What stays is everything about the order, and one rule the doc sets in bold: "every manual entry carries a stop", refused at preview without one apps/server/src/trading/TradingManualEntryService.ts:5-16. The stop field's own comment: "Mandatory. A ticket without a stop is refused, never defaulted" apps/server/src/trading/TradingManualEntryService.ts:63-65. Manual rows persist mission_id NULL with the account as owner apps/server/src/trading/HyperliquidExecutionService.ts:97-107, under D4 exclusivity where a market held by a mission refuses the ticket with market_owned_by_mission apps/server/src/trading/TradingManualEntryService.ts:13-16.

idem_manual_ · deriveManualCloid · manual:{accountId}

Grouped legs, read positionally

A grouped submission sends the entry and its stop child in one action, and the response returns one status per order "in submission order". Each row is attributed to the leg sent at that index, and the code prefers the leg T3 sent over any echoed cloid, "because a mismatch would mean the positional contract broke, which the caller must not paper over with a blank cloid" apps/server/src/trading/HyperliquidExecutionService.ts:440-489.

Two failure shapes are refused outright: an action-level error such as insufficient margin, and a response with zero per-order rows, which "is reported as a failure rather than an empty success" apps/server/src/trading/HyperliquidExecutionService.ts:461-472.

row[0]filled · 0.35 @ 3121.5entry leg
row[1]resting · trigger 3040.0protection leg
row[0]error: insufficient marginaction-level
(none)zero rows is a failure, never an empty successrefused

The bug log still quotes itself

Two old failures are preserved as comments where the current code answers them. Both are about the same sin: reporting an outcome the system did not have.

A SYNTHESIZED ANSWER, BEFORE THE QUESTION WAS ASKED

The trading_enter tool used to return a synthesized status: "submitted" the moment the dispatch landed, before preview, before signing, before the exchange had been asked anything. Downstream refusals were logged server-side and never reached the harness, so a mission whose every entry was being rejected read, to the harness, as a mission that had entered.

"That is the worst possible failure mode for a trading agent: it believes it holds a position it does not hold." apps/server/src/trading/TradingExecutionOutcome.ts:4-11

The fix is the outcome service this page has already walked: the tool blocks on a latch per mission and sequence, opened only when the reactor reaches a durable conclusion, and reads the persisted record once apps/server/src/trading/TradingExecutionReceipts.ts:2-19. The waiting itself used to be its own small scandal, re-reading the database every 250 milliseconds for twenty seconds: "up to eighty queries per execution, each one a guess".

AN IOC PARKED AS ACCEPTED, FOREVER

A response that says filled used to be recorded as accepted. Since nothing in the system ever wrote filled, the record stayed in flight forever, its risk reservation stayed reserved, and preview's pending-execution item "refused every subsequent intent for the mission" apps/server/src/trading/HyperliquidExecutionService.ts:824-832. The reactor's side of the same wound: counting a resting accepted order as a blocking pending execution "is what made one filled entry permanently lock the mission out of every subsequent write" apps/server/src/trading/TradingMissionReactor.ts:1922-1928. Filled is now terminal and written at once; accepted means only "acknowledged and resting on the book". More of these live on Failure stories.

Go deeper by reading

The submit sequence

  • apps/server/src/trading/HyperliquidExecutionService.ts:1-26 the module doc: the nine ordered steps and the no-dedupe finding
  • apps/server/src/trading/HyperliquidExecutionService.ts:562-863 submitIntent, the shared tail from mapping to the final status
  • apps/server/src/trading/HyperliquidExecutionService.ts:865-907 submitOrder: signer, fresh book, preview, then the tail
  • apps/server/src/trading/HyperliquidExecutionService.ts:440-489 inspectOrderStatuses: positional leg attribution

Identity and idempotency

  • packages/hyperliquid/src/Cloid.ts:1-32 the cloid contract, the correlation-id rule, the 0x prefix finding
  • packages/hyperliquid/src/NonceCoordinator.ts:1-21 the serialized lane and the millisecond fast-forward
  • apps/server/src/trading/HyperliquidExecutionService.ts:290-384 PRE_SUBMISSION_STATUSES and the two idempotent inserts
  • apps/server/src/trading/TradingExecutionReceipts.ts:1-22 the latch that replaced eighty polls

Policy and lanes

  • apps/server/src/trading/IocSlippageConfig.ts:1-29 the two crossing allowances and why they differ
  • apps/server/src/trading/TradingWorkingOrderService.ts:1-75 the working loop: four answers, cancel-first ordering
  • apps/server/src/trading/TradingManualEntryService.ts:1-19 the manual ticket and its one rule without exceptions
  • apps/server/src/trading/TradingPreviewService.ts:39-66 the 14-item entry checklist plus the exit-only rows

The reactor side

  • apps/server/src/trading/TradingMissionReactor.ts:1865-2071 processExecutionRequested, intake through blocked
  • apps/server/src/trading/TradingExecutionOutcome.ts:1-60 the bounded waiter and its fallback sweep
  • apps/server/src/mcp/toolkits/trading/handlers.ts:849-888 executeIntent: dispatch the question, await the answer