§11 · State machines

Four machines hold the mission, run, watch, and execution lifecycles

These machines are persisted, versioned, and the only legal paths through trading state. A harness run may not invent transitions; the deterministic authority and execution services may only move a mission to a blocked terminal for a fixed reason. Hyperliquid exchange state, not a local cache, decides whether an execution record rests, fills, or fails.

Platform · T3 server

§11.1 Mission state

The main loop runs initializinganalysingwaitingexecutingposition_openwaiting, and any active state may exit to a terminal.

Harness · Model

§11.2 Harness run

One TradingHarnessRun at a time owns the decision lease; events persist and coalesce behind an active run.

Harness · Model

§11.3 Watch state

A watch moves activetriggeredconsumed, or to cancelled / expired / superseded, bound to a strategy version.

Exchange · Hyperliquid

§11.4 Execution state

A record flows plannedvalidatedqueuedsigningsubmittedacknowledged → live → terminal.

§11.1 · Mission state machine

The mission loop revisits waiting; safety and authority move it to terminals

The active loop expresses the continuing-mandate shape: analyse, publish strategy and watches, end the turn, wake on event, reassess, enter or decline, manage, close, and publish new watches. Any active state may exit to a terminal when the user, the provider, or a deterministic safety condition requires it.

initializingaccount · provider bound
analysingfirst harness turn
waitingmonitoring active
executingvalidated · signing
position_openreconciled · protected
waitingnext watch
pauseduser · blocks entries, preserves protection
agent_unavailableprovider binding offline
blockeddeterministic safety, reason persisted
revokedauthority permanently ended
completedharness declared objective done

User · Human

paused

Blocks new discretionary entries and scale-ins, cancels mission-owned non-reduce-only entry orders, and preserves confirmed reduce-only stop and take-profit orders. It does not close the current position unless the user separately requests a reduction or close.

Harness · Model

agent_unavailable

Blocks new discretionary actions. Existing exchange-native protection remains active and market and account monitoring may continue. No new discretionary entry is permitted; the user may retry the same provider, pause, close, or revoke.

Authority · Risk

blocked

A deterministic safety condition prevents further autonomous exposure. The reason is persisted and visible: cumulative_loss_limit, protection_failure, account_unavailable, or reconciliation_failure.

User · Human

revoked · completed

revoked permanently ends mission authority and cancels mission-owned position-increasing orders while preserving valid protection if a position remains. completed means the harness declared the objective complete.

Pause preserves protection. paused blocks new entries and scale-ins but does not strip confirmed reduce-only stop and take-profit orders. blocked is the deterministic safety counterpart — reached only for a persisted reason — and likewise preserves valid protection while blocking further exposure.

§11.2 · Harness run state machine

One run owns the decision lease; events coalesce behind it

Each wake-up produces at most one TradingHarnessRun. Only one run may own the mission decision lease at a time, so a second provider turn is never started while another is active. New events are still persisted and equivalent events coalesced; a follow-up run is considered only after the current run completes.

Harness · Model

TradingHarnessRun

type TradingHarnessRun = {
  id: string;
  missionId: string;

  cause:
    | "mission_created"
    | "market_watch_triggered"
    | "scheduled_reassessment"
    | "order_updated"
    | "position_updated"
    | "user_message"
    | "mission_resumed";

  status:
    | "queued"
    | "starting"
    | "running"
    | "waiting_for_tool"
    | "completed"
    | "failed";

  startedAt?: number;
  completedAt?: number;
};

Single lease

Only one run may own the mission decision lease. A wake-up that arrives while a run is active is persisted and coalesced; it does not start a second provider turn.

Persist and coalesce

While a run is active, new events are persisted to the inbox and equivalent events are coalesced, so the follow-up run begins with the merged authoritative set rather than a re-evaluation per event.

Follow-up after completion

A follow-up run is considered only after the current run completes. The cause field records why each run started — watch, timer, exchange update, user message, or mission resume.

§11.3 · Watch state machine

Watches are version-bound, simple, and inspectable

A watch moves activetriggeredconsumed when a wake-up consumes it, or to cancelled, expired, or superseded from the active state. Watches are bound to a strategy version, so publishing a new strategy supersedes the watches of the prior version.

activebound to strategy version
triggeredpredicate matched
consumedincluded in a run
cancelledharness or user removed
expiredtimebound window passed
supersedednew strategy version published

A triggered watch is consumed by exactly one run; a duplicate firing is dropped by the inbox deduplication key. Supersession is automatic when a new strategy version is published, so an obsolete watch can never wake a harness against stale intent.

§11.4 · Execution state machine

An execution record carries full provenance from plan to reconciliation

Every typed action becomes an execution record that moves plannedvalidatedqueuedsigningsubmittedacknowledged, then to a live state of resting, partially_filled, or filled, and finally to a terminal of canceled, rejected, expired, or failed.

plannedharness request
validatedauthority · precision
queuedrecord persisted
signingnonce · execution wallet
submittedexchange action
acknowledgedper-order status read
restingGTC working
partially_filledpartial protection required
filledcanonical position updated
canceled
rejected
expired
failed

Exchange · Hyperliquid

Each execution record stores

  • The mission and harness run that produced it
  • The strategy version under which it was validated
  • The reason it was requested
  • The deterministic client order ID (cloid)
  • Normalized size and price values
  • The exchange response, including per-order statuses
  • The reconciliation result against canonical account state
No assumed fill. acknowledged is not filled. A grouped TP/SL response, a cancellation reason, or a locally cached fill is a hint until canonical Hyperliquid order, fill, and position state confirms it. The record's terminal state is set by reconciliation, not by submission response.

§12 · Event-driven harness lifecycle

Between turns T3 observes facts; it never generates discretionary intent

The harness ends a turn; the mission does not end. T3 evaluates simple typed watch predicates, persists and coalesces events, acquires a single decision lease, resumes the bound provider through the existing ProviderService, and hands the harness an authoritative snapshot. Complex interpretation stays a harness responsibility.

Harness · Model

§12.1 Watch types

A discriminated union of simple, deterministic, typed, inspectable predicates.

Platform · T3 server

§12.2 Wake-up payload

An authoritative mission snapshot, not a prompt from unbounded raw streams.

Platform · T3 server

§12.3 Turn coordinator

Acquires the lease and resumes the session through existing provider APIs.

Exchange · Hyperliquid

§12.5 Sleeping activity

Exchange-native orders keep working while the harness sleeps.

§12.1 · Watch types

Five deterministic, inspectable predicates; complex interpretation is harness work

A MarketWatch is a simple condition the runtime can evaluate without judgment: a price crossing, a finalized candle close, an order update, a position update, or a scheduled reassessment. Anything that requires weighing evidence — regime, volume quality, structural invalidation — is harness responsibility and does not belong in a watch predicate.

Harness · Model

MarketWatch

type MarketWatch =
  | {
      type: "price_cross";
      market: "ETH";
      priceSource: "mark" | "mid";
      direction: "above" | "below";
      price: number;
    }
  | {
      type: "candle_close";
      market: "ETH";
      interval: TradingTimeframe;
      direction: "above" | "below";
      price: number;
    }
  | {
      type: "order_update";
      cloid: string;
    }
  | {
      type: "position_update";
      market: "ETH";
    }
  | {
      type: "scheduled_reassessment";
      runAt: number;
    };

Simple

A watch is a single typed comparison or a fixed schedule. It does not encode strategy logic.

Deterministic

The same market state and the same watch produce the same firing decision, so reconnects and replays can be deduplicated.

Typed and inspectable

Every watch is a persisted record bound to a strategy version, visible in the workspace, and cancelable by harness or user.

Watches carry no discretion. Predicates must remain simple, deterministic, typed, and inspectable. Complex interpretation — whether a candle close is meaningful, whether volume confirms a breakout — remains a harness responsibility and is resolved in the next turn, not in the watch evaluator.

§12.2 · Wake-up payload

The harness receives an authoritative mission snapshot, not a raw stream

When a run starts, the harness does not receive unbounded market data or a free-form prompt. It receives a TradingHarnessWakeup: the cause, the triggering watch or user message, and a fresh, versioned snapshot of market, account, active strategy, authority, and pending events. The runtime assembles the bounded context; the harness supplies the judgment.

Platform · T3 server

TradingHarnessWakeup

type TradingHarnessWakeup = {
  missionId: string;
  harnessRunId: string;
  cause:
    | "market_watch_triggered"
    | "scheduled_reassessment"
    | "order_updated"
    | "position_updated"
    | "user_message"
    | "mission_resumed";

  occurredAt: number;
  triggeringWatch?: PersistedWatch;
  userMessage?: string;

  marketSnapshot: AgentMarketSnapshot;
  accountSnapshot: AgentAccountSnapshot;
  activeStrategy: MomentumStrategyState;
  authority: TradingAuthority;
  pendingEvents: TradingDomainEventSummary[];
  instruction: string;
};

Authoritative snapshot

The payload is an authoritative mission snapshot, not a prompt assembled from unbounded raw market streams. Market and account state are bounded, freshness-stamped views produced by the gateway and reconciler.

Versioned context

activeStrategy and authority carry their current versions, and pendingEvents is the coalesced set the run was started with — so a stale wake-up cannot silently overwrite a newer published conclusion.

§12.3 · Turn coordinator

One lease, seven pre-run checks, and no direct provider CLIs

The TradingTurnCoordinator is the single gate between an observed event and a started run. It verifies mission, provider, account, lease, and version state before doing anything, and it returns one of three outcomes so a caller can tell whether a run actually started, was queued behind an active one, or was blocked.

Platform · T3 server

TradingTurnCoordinator

interface TradingTurnCoordinator {
  requestRun(input: HarnessRunRequest): Promise<
    | { status: "started"; harnessRunId: string }
    | { status: "queued_behind_active_run" }
    | { status: "blocked"; reason: string }
  >;
}

Platform · T3 server

Before starting a run, verify

  1. Mission is active.
  2. Provider binding is unchanged.
  3. Provider instance is available.
  4. No other run owns the lease.
  5. Account is available.
  6. The event has not been superseded.
  7. The latest strategy and authority versions are loaded.
Use existing provider APIs. The coordinator must call T3's existing provider-management APIs to resume the session. It must not spawn provider CLIs directly. Trading code supplies the persisted mission identity, provider binding, fresh snapshot, and tool surface; ProviderService owns session start and resume.

§12.4 · What happens between turns

An observed fact becomes a wake-up, never an authorization

Between harness turns, T3 may observe and route facts. It may not generate new discretionary intent. A watch firing is routed through the evaluator, the inbox, the coordinator, and ProviderService before the harness sees it — and the harness may still decline.

  1. Five-minute candle closes above threshold. The gateway finalizes the candle once, after its close time.
  2. WatchEvaluator marks the watch triggered. The simple, deterministic predicate matches on the finalized close.
  3. TradingEventInbox persists the event. It is keyed for deduplication and coalesced with any pending peers.
  4. TradingTurnCoordinator acquires the lease. The seven pre-run checks pass; no other run owns the lease.
  5. ProviderService resumes the bound session. The same provider, instance, and resume cursor as the original binding.
  6. The harness receives a fresh snapshot. A bounded TradingHarnessWakeup, not a raw market stream.
  7. The harness decides. To enter, decline, or revise strategy — and registers the next watches before ending the turn.
A watch is not an order. The watch firing does not itself authorize a new position. The harness may decline, revise the strategy, or publish new watches; only a validated, leased, authority-checked execution record can change exposure.

§12.5 · Exchange-native activity while sleeping

Orders the harness already placed keep working without a turn

Because immediate downside protection lives at the exchange, the mission is not idle merely because the harness is asleep. The following continue without an active harness turn, because they were already placed or authorized, and T3 reconciles them and wakes the harness when interpretation is required.

Exchange · Hyperliquid

Resting and protective orders

  • Resting GTC entry orders requested by the harness
  • Reduce-only stop orders
  • Reduce-only take-profit orders

Exchange · Hyperliquid

Exchange-side and account events

  • Exchange-side cancellation or fill processing
  • Funding accrual
  • Position changes caused by existing orders

T3 subscribes to order, fill, and clearinghouse updates and reconciles each against canonical Hyperliquid state. When interpretation is required — a partial fill that needs independent protection, a triggered stop, a funding swing — T3 wakes the bound harness with a fresh snapshot. Routine exchange-native bookkeeping does not require a turn.

§13 · Market data

Bounded ETH candles, BBO, asset context, and account state under freshness thresholds

The market-data gateway subscribes directly to every POC ETH candle interval plus BBO, active asset context, and the account streams that drive execution and reconciliation. Hyperliquid documents direct candle snapshot and WebSocket support for every POC interval — 1m, 3m, 5m, 15m, and 1h — so the gateway subscribes to those intervals directly rather than synthesizing longer candles locally.

Exchange · Hyperliquid

Market subscriptions

  • ETH 1m candles
  • ETH 3m candles
  • ETH 5m candles
  • ETH 15m candles
  • ETH 1h candles
  • ETH BBO
  • ETH active asset context

Platform · T3 server

Account subscriptions

  • Account order updates
  • Account fills
  • Clearinghouse state
  • Open orders

§13 · Gateway requirements

Reconnect, resubscribe, snapshot versus incremental, dedupe, refresh, freshness

The gateway is the only market-data path into trading state. It must distinguish snapshots from incremental updates, survive disconnects without duplicating records, and refresh canonical state through the Info API after reconnect rather than trusting replayed updates.

Exchange · Hyperliquid

The market gateway must

  • Reconnect with bounded exponential backoff.
  • Re-subscribe after reconnect.
  • Distinguish snapshots from incremental updates.
  • De-duplicate replayed records.
  • Refresh canonical state through the Info API after reconnect.
  • Attach freshness metadata to tool results.
StreamFreshness thresholdStale handling
BBOstale after 2 secondsblocks marketable IOC pricing
Asset contextstale after 5 secondsrefresh via Info API
Account state during executionstale after 5 secondsexecution-critical stale data blocks submission
Closed candlesvalid only after final close time and single processinga finalized candle triggers a watch at most once
Stale blocks submission. Execution-critical stale data blocks submission. A marketable IOC limit must be derived from a fresh best bid or ask, not a stale mark price; an execution-record validation refuses to advance while BBO or account state is past its threshold.

§13 · Bounded context and computed features

Direct intervals, 500-bar tool responses, and computed features — no raw streams to the harness

Hyperliquid supports all five POC intervals directly, so the gateway subscribes to each rather than deriving 3m or 1h candles locally. One tool response is bounded to 500 bars; the gateway may paginate historical requests within Hyperliquid's documented recent-candle availability, but it must not place an unbounded history into a harness turn. Computed features are derived from the bounded view, not shipped as raw streams.

Exchange · Hyperliquid

Direct interval subscriptions

Subscribe directly to 1m, 3m, 5m, 15m, and 1h. Hyperliquid supports them; the POC does not synthesize 3m or 1h candles locally.

Platform · T3 server

Bounded tool responses

One tool response is bounded to 500 bars. Historical requests may paginate within Hyperliquid's documented recent-candle availability, but no unbounded history enters a harness turn.

Computed features may include

  • Returns
  • VWAP
  • ATR
  • Realized volatility
  • Average volume
  • Relative volume
  • Recent high and low (swing points)
  • Open-interest change when available
No raw streams into context. Do not place massive raw streams into the harness context. The wake-up payload carries bounded snapshots and computed features; the harness reads additional history through paginated tools when it needs more, always within the 500-bar response bound.