Big picture

The whole system in one pass

T3 Trade extends the T3 Code agent environment with supervised, budgeted trading on Hyperliquid testnet. This page shows a new engineer what the fork keeps, what it adds, which safety rules are fixed, and how a trade moves from server startup to a UI refresh.

bindThreadToMarket mission granted trading.execution.requested command on the spine signInNonceLane record persisted first POST /exchange Hyperliquid testnet reconcile canonical truth back invalidate() doorbell, no data
REAL NAMES, ONE PATH: AUTHORITY IN, DOORBELL OUT
1
code path that spends testnet capital, by its own header apps/server/src/trading/HyperliquidExecutionService.ts:4
5
provider drivers built in: Codex, Claude, Cursor, Grok, OpenCode apps/server/src/provider/builtInDrivers.ts:47-53
7
user controls that work with the agent process dead apps/server/src/trading/TradingControlService.ts:26-27
10
statuses in the mission state machine; revoked and completed are terminal apps/server/src/trading/MissionTransitions.ts:19

A coding environment extended for supervised trading

T3 Trade starts from T3 Code, an agentic coding environment, and makes trading the point. The architecture doc says it in one line: the project is "a fork of T3 Code with supervised, budgeted trading on Hyperliquid testnet as its center" docs/internals/overview.md:5-6. The repository's own instructions open the same way: keep the coding-agent workspace, add supervised trading, and treat the trading system, not the upstream product, as the center AGENTS.md:3.

The fork keeps the inherited runtime: agent sessions, workspaces, version control, and clients that talk to the server over one authenticated Effect RPC WebSocket docs/internals/overview.md:7-8. The server is the execution boundary. Every provider process, terminal, git operation, and filesystem read happens there, never in the client docs/internals/overview.md:8-10.

The fork adds a trading runtime that rides the same loop as coding: services in apps/server/src/trading, with shared types and pure policy in packages/trading-contracts docs/internals/overview.md:119-122. Agents reach it through the typed t3-trade MCP toolkit docs/internals/overview.md:121-123. Exactly one service can submit orders that spend testnet capital: "This is the only code path that spends testnet capital" apps/server/src/trading/HyperliquidExecutionService.ts:4. The Execution page walks it.

The venue is pinned in the type system: TradingEnvironment is the single literal "hyperliquid_testnet" packages/trading-contracts/src/account.ts:13. There is no mainnet path to introduce by accident AGENTS.md:15.

The boundaries the repository will not cross

THE FIRST RULE

The stated priorities are correctness, clarity, and restraint, in that order AGENTS.md:7-9. One rule takes precedence over all three: "Trading safety outranks convenience." Execution guards, loss budgets, protection requirements, authority checks, idempotency boundaries, and reconciliation rules are never weakened to make an agent or UI flow pass AGENTS.md:11. The Safety invariants page enumerates them.

testnet only
Hyperliquid testnet is the only execution target AGENTS.md:15. The environment is a single type literal packages/trading-contracts/src/account.ts:13, and the one spending path resolves its signer fail-closed as its first step apps/server/src/trading/HyperliquidExecutionService.ts:7.
exchange authoritative
"Hyperliquid is the canonical source of market and account truth. Local records (fills, position snapshots, open orders) are hints until canonical account/order/position state confirms them" apps/server/src/trading/HyperliquidReconciler.ts:5-7. Local state records T3 Trade's decisions; the exchange records what actually happened AGENTS.md:16. The Reconciliation page shows the convergence path.
exposure is always protected
"No acknowledged position increase may remain without confirmed exchange-native reduce-only protection beyond the bounded reconciliation window" apps/server/src/trading/TradingProtectionService.ts:4-8. Take-profit works differently: it wakes the agent instead of placing a resting order apps/server/src/trading/TradingProtectionService.ts:34-36. See Risk control.
signing local + fail-closed
With no key configured, every signable action is rejected with interim_signer_not_configured, because "the gate must fail closed until the owner explicitly arms it" apps/server/src/trading/InterimSignerConfig.ts:29-32. The only signature point is a serialized nonce lane apps/server/src/trading/HyperliquidExecutionService.ts:12,527. Under vitest the key-file source is disabled, so no test inherits the developer's real key apps/server/src/trading/InterimSignerConfig.ts:26-27.
research mode is a real mode
With no signer, the product states it plainly: "Research mode: no trading key is configured. Observation, backtests and validations work; orders will be refused." apps/web/src/components/trading/tradeHomePresentation.ts:87-89. The wiring proves the claim: backtest and validation services get dependency sets that cannot reach an order apps/server/src/trading/runtimeLayer.ts:221-235. Covered in Research mode.
user controls beat the agent
Seven controls: pause, resume, cancelEntries, reducePosition, closePosition, revoke, closeAndRevoke. The rule that shapes them: "A user who wants out of a position must be able to get out while the provider process is dead, the session is unreachable, or the model is mid-thought." apps/server/src/trading/TradingControlService.ts:4-8,26-27. They bypass the harness checklist but never the signer, the nonce lane, or reconciliation apps/server/src/trading/TradingControlService.ts:19-24.
web + desktop are the product
The trade home is a route in the active web client apps/web/src/routes/trade.tsx:3-6; the desktop app is an Electron shell that loads that same web app apps/desktop/src/window/DesktopWindow.ts:623. Mobile is inherited upstream code: nothing under apps/mobile/src references trading. Tour in Web & desktop.
retries cannot double-spend
"The exchange itself does NOT deduplicate" marketable IOCs; a resubmitted IOC opens a second order and fills again, which was verified live apps/server/src/trading/HyperliquidExecutionService.ts:17-23. So the execution record and its risk reservation are persisted before signing, and a retry reads that record back and refuses apps/server/src/trading/HyperliquidExecutionService.ts:11,17-19. Failure stories records how this rule was earned.

How the parts stack

Clients send typed WebSocket requests. The server converts each request into a command, and a pure function decides which events to record. One transaction stores those events and updates the read models. Queued workers perform external work, trading services call the exchange, the archive records history, and the relay links environments.

The command spine

Clients never mutate state directly; they dispatch typed commands docs/internals/overview.md:63-66. The engine serializes them through one in-process queue and fans committed events out to every subscriber apps/server/src/orchestration/Layers/OrchestrationEngine.ts:101-102. A pure decider turns each command into events, and one SQLite transaction appends the events, folds the read models, and writes the receipt together docs/internals/overview.md:73-78.

Walk the spine on the Event spine page

Providers behind adapters

Five drivers ship built in: Codex, Claude, Cursor, Grok, OpenCode apps/server/src/provider/builtInDrivers.ts:47-53. The routing layer resolves turns "without knowing which agent is behind them" docs/internals/overview.md:111-115. Provider-specific complexity stays at the adapter boundary.

Provider adapters

Trading services

Execution, guards, protection, missions, watches, and reconciliation live under apps/server/src/trading; pure policy and accounting live in packages/trading-contracts docs/internals/overview.md:119-122. Reactors answer requested events only after the domain write: "The UI never sees a status the domain refused" apps/server/src/trading/TradingMissionReactor.ts:10-12.

Missions watches wakes

The exchange

Writes leave as signed actions to POST /exchange on testnet apps/server/src/trading/HyperliquidExecutionService.ts:13. Reads come back canonical and converge the local tables apps/server/src/trading/HyperliquidReconciler.ts:2-7.

Execution

The archive

A separate always-on process records candles because the info API is "a window, not a history": about 5000 bars per interval, and nothing older is served again apps/server/src/trading/archive/config.ts:4-9. "The archiver never authenticates, never sees a key, and never touches an order endpoint." apps/server/src/trading/archive/config.ts:11-12

The relay

infra/relay is the hosted T3 Connect control plane. It links environments to a cloud account, provisions managed endpoints, issues short-lived credentials, and reports diagnostics infra/relay/README.md:15-24. After connection, normal API and WebSocket traffic goes directly between client and environment; "the relay is not the steady-state data path" AGENTS.md:54.

Relay & environments

Clients

Web and desktop share packages/client-runtime: connection lifecycle, authentication, RPC session, cached environment data, and domain state as atom factories docs/internals/overview.md:55-61. React components never construct transports, retry loops, or RPC clients docs/internals/overview.md:59-61.

Web & desktop

How the system comes to life

One trade, end to end, with the code that owns each step. Monospaced labels show the names used in code, and every claim cites its source.

01 · BOOT / LEASE

The trading runtime boots by taking a single-writer lease: a lock file <dbPath>.trading.lock holding pid, host, and heartbeat, created atomically apps/server/src/trading/TradingRuntimeLease.ts:12-14. The lease exists because a second process once ran the same maintenance against the same database and terminated a long-running test. It turns that collision into an explicit startup failure apps/server/src/trading/TradingRuntimeLease.ts:4-10. While the lease is refused, the rest of the server boots normally; only the destructive trading runtime stays down apps/server/src/trading/TradingRuntimeLease.ts:28-30.

02 · ARCHIVE

A supervised archiver starts recording market history, because the exchange serves only a recent window of candles and "nothing older is ever served again" apps/server/src/trading/archive/config.ts:4-9. It makes public reads only and never touches an order endpoint apps/server/src/trading/archive/config.ts:11-12.

03 · MISSION GRANTED

In a chat, the agent binds its thread to a market. bindThreadToMarket creates a mission and freezes the provider binding for the mission's life apps/server/src/trading/TradingAuthorityBinding.ts:279,306-309. A mission is the persistent record of authority delegated to an agent. It moves through ten statuses, and tests verify every allowed transition against the specification apps/server/src/trading/MissionTransitions.ts:4-5,40-42. The Missions watches wakes page owns this story.

04 · WATCH FIRES

The mission arms typed predicates: price crossings, candle closes, scheduled reassessments. On a match, the evaluator flips the watch from active to triggered atomically and persists a dedup-keyed inbox event, so a watch fires exactly once even across restarts apps/server/src/trading/WatchEvaluator.ts:11-20. A firing is a nudge, not permission: "A watch firing does not authorize a position" apps/server/src/trading/WatchEvaluator.ts:22-23.

05 · WAKE

TradingTurnCoordinator.requestRun runs the pre-run checks, acquires the one-per-mission decision lease (a concurrent second run becomes queued_behind_active_run), composes a bounded wakeup snapshot, and dispatches thread.turn.start apps/server/src/trading/TradingTurnCoordinator.ts:5-18. The coordinator never calls the provider layer directly; dispatch is the only sanctioned way apps/server/src/trading/TradingTurnCoordinator.ts:15-18.

06 · ENTER

The resumed agent calls trading_enter. The server derives what a language model cannot know and raises the typed command trading.execution.requested on the spine packages/contracts/src/trading.ts:1492. The submit sequence is fixed and ordered: resolve the signer, resolve market metadata and fresh pricing, run the preview checklist, map the order, then persist the execution record and risk reservation before any signature apps/server/src/trading/HyperliquidExecutionService.ts:4-15.

07 · FILL

Signing happens once, inside the serialized nonce lane apps/server/src/trading/HyperliquidExecutionService.ts:12,527, then the order goes to POST /exchange apps/server/src/trading/HyperliquidExecutionService.ts:13 and every per-order status is inspected on the way back. A grouped response is not proof: coverage is read back from the exchange's open orders apps/server/src/trading/TradingProtectionService.ts:20-22.

08 · RECONCILE

Canonical truth flows back, and local tables converge to it: local records are hints until canonical state confirms them apps/server/src/trading/HyperliquidReconciler.ts:5-7. Every confirmed increase gets its exchange-native reduce-only protection confirmed against the canonical position apps/server/src/trading/TradingProtectionService.ts:4-8. The Reconciliation page maps every trigger.

09 · DOORBELL

invalidate() bumps a revision counter and publishes an event that deliberately carries no data, only the revision apps/server/src/trading/TradingAccountProjection.ts:250-260: the fetched state is authoritative, while the event only tells clients to fetch again. The web re-fetches and renders projector output, and "There is no client-side mission state to stale" apps/web/src/lib/tradingMissionsState.ts:41-47.

The map on disk

One line each. Start in the server; read the trading contracts before changing any math.

apps/server
Orchestration, providers, persistence, and the WebSocket server AGENTS.md:33. The trading runtime, including the archive runtime, lives under apps/server/src/trading AGENTS.md:31.
apps/web
The active React/Vite client. The /trade route mounts the trade home apps/web/src/routes/trade.tsx:3-6.
apps/desktop
An Electron shell that loads the web app apps/desktop/src/window/DesktopWindow.ts:623 and adds desktop behavior AGENTS.md:34.
packages/contracts
Wire contracts for every surface. A schema change must be handled by every active producer and consumer AGENTS.md:35. Tour on the Contracts page.
packages/trading-contracts
Shared trading types plus pure policy and accounting logic AGENTS.md:32.
packages/client-runtime
Client logic shared across surfaces: connection, auth, RPC, state AGENTS.md:36.
packages/hyperliquid
The in-repo Hyperliquid client: signing, nonce coordination, gateway, market resolution, key location (Signing.ts, NonceCoordinator.ts, Gateway.ts, MarketResolver.ts, KeyLocation.ts under packages/hyperliquid/src).
infra/relay
The hosted T3 Connect control plane for linking and credentials AGENTS.md:37, responsibilities at infra/relay/README.md:15-24. Not the steady-state data path AGENTS.md:54.
docs/internals
Durable architecture, including the system overview docs/internals/overview.md and the trading product constitution docs/internals/trading-final-form.md AGENTS.md:40.
docs/operations
Operational procedures: observability, releases, relay operations AGENTS.md:40.
docs/user
Shipped behavior, including research-mode.md and trading.md docs/user AGENTS.md:40.
.repos
Vendored, read-only references. Never edit or import from them AGENTS.md:38.

Three ways through the atlas

Pick the path that matches your question. Every page cites the repository the way this one does.

The new engineer

You want to change code without breaking the bank.

  1. 1Big picture (this page)
  2. 2Event spine, because every write travels it
  3. 3Contracts, the shared types
  4. 4Missions watches wakes, the agent loop
  5. 5Web & desktop, where it renders

The safety auditor

You want to know what stops the bad day.

  1. 1Safety invariants
  2. 2Risk control, budgets and gates
  3. 3Execution, the spending path
  4. 4Reconciliation, canonical truth
  5. 5Signer & authority, keys and ceilings
  6. 6Failure stories, how the rules were earned

The product viewer

You want to see what the user actually gets.

  1. 1Research mode, the product without a key
  2. 2Missions watches wakes
  3. 3Provider adapters, which agent answers
  4. 4Relay & environments, remote access
  5. 5Glossary, when a term bites

Go deeper

Read first

  • docs/internals/overview.md: the repository's own architecture page, source of the stack diagram and the spine description.
  • docs/internals/trading-final-form.md: the settled trading product constitution.
  • AGENTS.md: priorities, product boundaries, and key locations (lines 7 through 40).

The spine

  • apps/server/src/orchestration/Layers/OrchestrationEngine.ts:90-102: the queue, the pubsub, the engine.
  • apps/server/src/trading/TradingMissionReactor.ts:1-18: requested events are questions; reactors answer.

The money path

  • apps/server/src/trading/HyperliquidExecutionService.ts:1-26: the nine-step submit sequence in its own header.
  • apps/server/src/trading/TradingProtectionService.ts:1-32: the protection invariant and its repairs.
  • apps/server/src/trading/HyperliquidReconciler.ts:1-7: canonical truth, local hints.
  • apps/server/src/trading/TradingControlService.ts:1-29: the seven controls and the rule that shapes them.

The fork's edges

  • packages/shared/src/forkPaths.ts:1-26: why the names fork and nothing else.
  • apps/server/src/trading/InterimSignerConfig.ts:1-43: the key, the gate, the fail-closed rule.
  • apps/server/src/trading/archive/config.ts:1-19: the archive's reason and its limits.
  • apps/server/src/provider/builtInDrivers.ts:47-53: the five drivers.