Polytraders Dev Guide
internal
v3 spine Phase 1 · Shared contracts 9 demo-wired · 0 shadow-ready · 0 production-live · 100 pending · 109 total 15/33 infra tasks the plan status board

Typed schemas

Every object that crosses a bot boundary is one of these. If your bot consumes or emits an object that isn't here, that object isn't real yet.

contracts package: v0.1.0 · frozen for v3.0.0 · loaded from packages/polytraders-contracts/schemas/manifest.json

Index

Source of truth for every contract is packages/polytraders-contracts/. Both the TypeScript declarations below and the JSON schemas alongside are read from disk by the build — the dev guide does not invent schemas.

SchemaCategoryPurposeSpec
BotinterfaceThe interface every bot implements: init, decide, explain, health. The single most important contract in the system.jump
BotContextinterfaceThe injected runtime context every decide() receives. Bots may not reach outside it — no direct network, filesystem or wall-clock.jump
BotConfigconfigVersioned configuration shape — defaults, warnings, hard limits, feature flags. Every emitted envelope carries the configVersion that produced it.jump
MarketSnapshotdataNormalised Polymarket V2 market state at a point in time. The single object every Strategy and Risk bot reads from.jump
OrderBookSnapshotdataBest bid, best ask, depth ladder and timestamp. Carries a freshness stamp and source hash.jump
OrderIntentdecisionStrategy output, before risk approval. Risk bots evaluate OrderIntents — not raw market observations.jump
RiskVotedecisionRisk-bot decision on an OrderIntent: APPROVE, RESHAPE, REJECT, QUARANTINE — with reason code.jump
ExecutionPlandecisionOutput of an Execution Utility — shape, timing and sizing of orders to submit.jump
ReportEnvelopetelemetryUniversal reporting wrapper — every bot, every decision. The single emission shape from which audit, dashboards and replay are built.jump
ReasonCodetelemetryRegistry-aligned reason code. Free-text errors are forbidden — every emit carries one of these.jump

Per-contract specifications

Every contract below shows the TypeScript declaration and the matching JSON Schema, both read directly from packages/polytraders-contracts/. The verification script in tests/verify-contracts.js ensures every TS file pairs with a schema and every schema declares the canonical header fields.

Bot

interface src: src/Bot.ts · schema: schemas/Bot.schema.json

The interface every bot implements: init, decide, explain, health. The single most important contract in the system.

TypeScript declaration
/**
 * Bot — the interface every bot in the system implements.
 *
 * Four methods: init, decide, explain, health. Anything else lives in the
 * runtime, not in the bot. The single most important contract in the system.
 *
 * Frozen for v3.0.0.
 */
import type { BotContext } from "./BotContext.js";
import type { BotConfig } from "./BotConfig.js";

export interface BotHealthStatus {
  schemaVersion: "1.0.0";
  botId: string;
  heartbeatAgeMs: number;
  errorRatePct: number;
  decisionsLastMinute: number;
  mode: "stub" | "shadow" | "advisory" | "enforced" | "quarantine";
  observedAt: number;
}

export interface BotExplanation {
  schemaVersion: "1.0.0";
  botId: string;
  /** Human-readable explanation of the most recent decision. */
  summary: string;
  /** Reason code that drove the decision. */
  reasonCode: string;
  /** Optional structured detail — model inputs, thresholds breached, etc. */
  detail?: Record<string, unknown>;
}

/**
 * Generic Bot<I, O> — input type I, output type O.
 *
 * - Risk bots: I = OrderIntent, O = RiskVote
 * - Strategy bots: I = MarketSnapshot, O = OrderIntent | null
 * - Execution utilities: I = OrderIntent, O = ExecutionPlan
 * - Intelligence bots: I = raw payload, O = a typed signal
 */
export interface Bot<I, O> {
  /** Stable id, e.g. "risk.kill_switch". */
  readonly botId: string;

  /** Semantic version of the bot implementation. */
  readonly version: string;

  /** Called once per process. Validates config, opens any local state. */
  init(config: BotConfig, ctx: BotContext): Promise<void>;

  /** The single decision point. Must be deterministic given (input, config, ctx). */
  decide(input: I, ctx: BotContext): Promise<O>;

  /** Returns the explanation for the most recent decision. Used by /explain UI and audit. */
  explain(): BotExplanation;

  /** Returns current health. Polled by the runtime; never blocking. */
  health(): BotHealthStatus;
}
JSON Schema (draft 2020-12)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "polytraders/Bot",
  "title": "Bot",
  "description": "Structural description of the Bot interface. Not directly validatable at runtime — bots are TS classes — but documents the four mandatory methods for the dev-guide and code review.",
  "type": "object",
  "required": ["botId", "version", "methods"],
  "additionalProperties": false,
  "properties": {
    "botId": { "type": "string", "pattern": "^[a-z]+\\.[a-z0-9_]+$" },
    "version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
    "methods": {
      "type": "object",
      "required": ["init", "decide", "explain", "health"],
      "additionalProperties": false,
      "properties": {
        "init": { "const": "(config, ctx) => Promise<void>" },
        "decide": { "const": "(input, ctx) => Promise<output>" },
        "explain": { "const": "() => BotExplanation" },
        "health": { "const": "() => BotHealthStatus" }
      }
    }
  }
}

BotContext

interface src: src/BotContext.ts · schema: schemas/BotContext.schema.json

The injected runtime context every decide() receives. Bots may not reach outside it — no direct network, filesystem or wall-clock.

TypeScript declaration
/**
 * BotContext — injected runtime context.
 *
 * Every decide() call receives a BotContext. The bot uses it to read
 * normalised data, emit envelopes, and query its own config. Bots MUST NOT
 * reach outside the context (no direct network, no filesystem, no clock
 * other than ctx.now).
 *
 * Frozen for v3.0.0.
 */
import type { MarketSnapshot } from "./MarketSnapshot.js";
import type { OrderBookSnapshot } from "./OrderBookSnapshot.js";
import type { BotConfig } from "./BotConfig.js";
import type { ReportEnvelope } from "./ReportEnvelope.js";

export interface BotContext {
  /** Stable id of this bot instance, e.g. "risk.kill_switch". */
  botId: string;

  /** Loaded configuration. Frozen for the duration of decide(). */
  config: BotConfig;

  /** Correlation id for the current decision; carried through every emit. */
  correlationId: string;

  /** Monotonic clock — bots MUST NOT call Date.now() directly. */
  now(): number;

  /** Look up the latest MarketSnapshot for a market id. */
  getMarketSnapshot(marketId: string): MarketSnapshot | null;

  /** Look up the latest OrderBookSnapshot for a market id and side. */
  getOrderBookSnapshot(marketId: string, side: "yes" | "no"): OrderBookSnapshot | null;

  /** Emit a ReportEnvelope. The envelope is timestamped and signed by the runtime. */
  emit(envelope: Omit<ReportEnvelope, "envelopeId" | "emittedAt" | "botId" | "botVersion" | "configVersion" | "correlationId" | "mode">): void;

  /** Current run mode — bots adjust behaviour based on this. */
  mode: "stub" | "shadow" | "advisory" | "enforced" | "quarantine";

  /** Logging surface — structured only. Free-text logging is rejected at lint time. */
  log(level: "debug" | "info" | "warn" | "error", reasonCode: string, fields?: Record<string, unknown>): void;
}
JSON Schema (draft 2020-12)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "polytraders/BotContext",
  "title": "BotContext",
  "description": "Structural description of the BotContext shape injected into decide(). Not directly validatable at runtime — methods are functions — but documents the surface bots are allowed to touch.",
  "type": "object",
  "required": ["botId", "config", "correlationId", "mode", "methods"],
  "additionalProperties": false,
  "properties": {
    "botId": { "type": "string", "pattern": "^[a-z]+\\.[a-z0-9_]+$" },
    "config": { "$ref": "polytraders/BotConfig" },
    "correlationId": { "type": "string", "minLength": 1 },
    "mode": { "enum": ["stub", "shadow", "advisory", "enforced", "quarantine"] },
    "methods": {
      "type": "object",
      "required": ["now", "getMarketSnapshot", "getOrderBookSnapshot", "emit", "log"],
      "additionalProperties": false,
      "properties": {
        "now": { "const": "() => number" },
        "getMarketSnapshot": { "const": "(marketId) => MarketSnapshot | null" },
        "getOrderBookSnapshot": { "const": "(marketId, side) => OrderBookSnapshot | null" },
        "emit": { "const": "(envelope) => void" },
        "log": { "const": "(level, reasonCode, fields?) => void" }
      }
    }
  }
}

BotConfig

config src: src/BotConfig.ts · schema: schemas/BotConfig.schema.json

Versioned configuration shape — defaults, warnings, hard limits, feature flags. Every emitted envelope carries the configVersion that produced it.

TypeScript declaration
/**
 * BotConfig — versioned configuration for a single bot.
 *
 * Parameter defaults, warnings, hard limits, feature flags. Every emitted
 * envelope carries the configVersion that produced it; replay is impossible
 * without this guarantee.
 *
 * Frozen for v3.0.0.
 */
export interface BotConfigParameter {
  name: string;
  type: "number" | "integer" | "boolean" | "string" | "duration_ms";
  default: number | boolean | string;
  warning?: number | string;     // soft threshold — emit warning, do not block
  hard?: number | string;        // hard threshold — block / fall back
  description?: string;
}

export interface BotConfig {
  schemaVersion: "1.0.0";
  botId: string;
  configVersion: string;        // semver, bumped on every change
  signedBy: string;             // approver wallet / id
  signedAt: number;             // ms epoch
  parameters: BotConfigParameter[];
  featureFlags: Record<string, boolean>;
  modeOverride?: "stub" | "shadow" | "advisory" | "enforced" | "quarantine";
}
JSON Schema (draft 2020-12)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "polytraders/BotConfig",
  "title": "BotConfig",
  "description": "Versioned configuration for a single bot.",
  "type": "object",
  "required": ["schemaVersion", "botId", "configVersion", "signedBy", "signedAt", "parameters", "featureFlags"],
  "additionalProperties": false,
  "properties": {
    "schemaVersion": { "const": "1.0.0" },
    "botId": { "type": "string", "pattern": "^[a-z]+\\.[a-z0-9_]+$" },
    "configVersion": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
    "signedBy": { "type": "string", "minLength": 1 },
    "signedAt": { "type": "number" },
    "parameters": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["name", "type", "default"],
        "additionalProperties": false,
        "properties": {
          "name": { "type": "string", "minLength": 1 },
          "type": { "enum": ["number", "integer", "boolean", "string", "duration_ms"] },
          "default": {},
          "warning": {},
          "hard": {},
          "description": { "type": "string" }
        }
      }
    },
    "featureFlags": { "type": "object", "additionalProperties": { "type": "boolean" } },
    "modeOverride": { "enum": ["stub", "shadow", "advisory", "enforced", "quarantine"] }
  }
}

MarketSnapshot

data src: src/MarketSnapshot.ts · schema: schemas/MarketSnapshot.schema.json

Normalised Polymarket V2 market state at a point in time. The single object every Strategy and Risk bot reads from.

TypeScript declaration
/**
 * MarketSnapshot — normalised Polymarket V2 market state at a point in time.
 *
 * The single object every Strategy and Risk bot reads from. Bots MUST NOT
 * consume raw Polymarket payloads. The data-flow layer produces this object;
 * downstream consumers trust it.
 *
 * Frozen for v3.0.0.
 */
export interface MarketSnapshot {
  schemaVersion: "1.0.0";
  marketId: string;             // Polymarket V2 condition id
  conditionTokens: string[];    // outcome token ids
  resolution: {
    status: "open" | "paused" | "resolving" | "resolved" | "invalid";
    expectedAt?: number;        // ms epoch
  };
  prices: {
    yes: number;                // in pUSD, 0..1
    no: number;
  };
  liquidityPusd: number;        // total order-book liquidity in pUSD
  source: "clob_v2" | "subgraph" | "snapshot_archive";
  observedAt: number;           // monotonic ms
  freshnessMs: number;          // age at observation time
  provenance: string;           // hash of source response
}
JSON Schema (draft 2020-12)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "polytraders/MarketSnapshot",
  "title": "MarketSnapshot",
  "description": "Normalised Polymarket V2 market state at a point in time.",
  "type": "object",
  "required": ["schemaVersion", "marketId", "conditionTokens", "resolution", "prices", "liquidityPusd", "source", "observedAt", "freshnessMs", "provenance"],
  "additionalProperties": false,
  "properties": {
    "schemaVersion": { "const": "1.0.0" },
    "marketId": { "type": "string", "minLength": 1 },
    "conditionTokens": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
    "resolution": {
      "type": "object",
      "required": ["status"],
      "additionalProperties": false,
      "properties": {
        "status": { "enum": ["open", "paused", "resolving", "resolved", "invalid"] },
        "expectedAt": { "type": "number" }
      }
    },
    "prices": {
      "type": "object",
      "required": ["yes", "no"],
      "additionalProperties": false,
      "properties": {
        "yes": { "type": "number", "minimum": 0, "maximum": 1 },
        "no":  { "type": "number", "minimum": 0, "maximum": 1 }
      }
    },
    "liquidityPusd": { "type": "number", "minimum": 0 },
    "source": { "enum": ["clob_v2", "subgraph", "snapshot_archive"] },
    "observedAt": { "type": "number" },
    "freshnessMs": { "type": "number", "minimum": 0 },
    "provenance": { "type": "string", "minLength": 1 }
  }
}

OrderBookSnapshot

data src: src/OrderBookSnapshot.ts · schema: schemas/OrderBookSnapshot.schema.json

Best bid, best ask, depth ladder and timestamp. Carries a freshness stamp and source hash.

TypeScript declaration
/**
 * OrderBookSnapshot — best bid, best ask, depth ladder and timestamp.
 * Carries a freshness stamp and source hash. Default budget 500 ms.
 *
 * Frozen for v3.0.0.
 */
export interface OrderBookLevel {
  pricePusd: number;
  sizePusd: number;
}

export interface OrderBookSnapshot {
  schemaVersion: "1.0.0";
  marketId: string;
  side: "yes" | "no";
  bestBidPusd: number;
  bestAskPusd: number;
  spreadBps: number;
  depth: OrderBookLevel[];      // sorted by price
  observedAt: number;
  freshnessMs: number;
  source: "clob_v2";
}
JSON Schema (draft 2020-12)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "polytraders/OrderBookSnapshot",
  "title": "OrderBookSnapshot",
  "description": "Best bid, best ask, depth ladder and timestamp.",
  "type": "object",
  "required": ["schemaVersion", "marketId", "side", "bestBidPusd", "bestAskPusd", "spreadBps", "depth", "observedAt", "freshnessMs", "source"],
  "additionalProperties": false,
  "properties": {
    "schemaVersion": { "const": "1.0.0" },
    "marketId": { "type": "string", "minLength": 1 },
    "side": { "enum": ["yes", "no"] },
    "bestBidPusd": { "type": "number", "minimum": 0, "maximum": 1 },
    "bestAskPusd": { "type": "number", "minimum": 0, "maximum": 1 },
    "spreadBps": { "type": "number", "minimum": 0 },
    "depth": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["pricePusd", "sizePusd"],
        "additionalProperties": false,
        "properties": {
          "pricePusd": { "type": "number", "minimum": 0, "maximum": 1 },
          "sizePusd": { "type": "number", "minimum": 0 }
        }
      }
    },
    "observedAt": { "type": "number" },
    "freshnessMs": { "type": "number", "minimum": 0 },
    "source": { "const": "clob_v2" }
  }
}

OrderIntent

decision src: src/OrderIntent.ts · schema: schemas/OrderIntent.schema.json

Strategy output, before risk approval. Risk bots evaluate OrderIntents — not raw market observations.

TypeScript declaration
/**
 * OrderIntent — Strategy output, before risk approval.
 *
 * Strategy emits an OrderIntent. Risk evaluates it. Risk MUST NOT evaluate
 * anything that is not an OrderIntent. There is no other path from a market
 * observation to a signed order.
 *
 * Frozen for v3.0.0.
 */
export interface OrderIntent {
  schemaVersion: "1.0.0";
  intentId: string;             // ULID
  emittedBy: string;            // bot_id of the Strategy bot
  marketId: string;
  side: "buy" | "sell";
  outcome: "yes" | "no";
  sizePusd: number;
  limitPricePusd?: number;       // omitted for IOC/market intents
  timeInForce: "GTC" | "IOC" | "FOK";
  reason: string;               // reason_code from registry
  modelEdgeBps?: number;
  emittedAt: number;
  expiresAt: number;            // hard expiry; risk re-checks against this
  builderCode: string;          // V2 attribution
}
JSON Schema (draft 2020-12)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "polytraders/OrderIntent",
  "title": "OrderIntent",
  "description": "Strategy output, before risk approval.",
  "type": "object",
  "required": ["schemaVersion", "intentId", "emittedBy", "marketId", "side", "outcome", "sizePusd", "timeInForce", "reason", "emittedAt", "expiresAt", "builderCode"],
  "additionalProperties": false,
  "properties": {
    "schemaVersion": { "const": "1.0.0" },
    "intentId": { "type": "string", "minLength": 1 },
    "emittedBy": { "type": "string", "pattern": "^[a-z]+\\.[a-z0-9_]+$" },
    "marketId": { "type": "string", "minLength": 1 },
    "side": { "enum": ["buy", "sell"] },
    "outcome": { "enum": ["yes", "no"] },
    "sizePusd": { "type": "number", "exclusiveMinimum": 0 },
    "limitPricePusd": { "type": "number", "minimum": 0, "maximum": 1 },
    "timeInForce": { "enum": ["GTC", "IOC", "FOK"] },
    "reason": { "type": "string", "pattern": "^(INTEL|DISC|STRAT|RISK|EXEC|SEC|GOV)_[A-Z0-9_]+$" },
    "modelEdgeBps": { "type": "number" },
    "emittedAt": { "type": "number" },
    "expiresAt": { "type": "number" },
    "builderCode": { "type": "string", "minLength": 1 }
  }
}

RiskVote

decision src: src/RiskVote.ts · schema: schemas/RiskVote.schema.json

Risk-bot decision on an OrderIntent: APPROVE, RESHAPE, REJECT, QUARANTINE — with reason code.

TypeScript declaration
/**
 * RiskVote — Risk-bot decision on an OrderIntent.
 *
 * Every Risk bot in the configured order emits one RiskVote per OrderIntent.
 * Any single REJECT halts the pipeline. RESHAPE is allowed but the proposed
 * intent must pass every Risk bot again.
 *
 * Frozen for v3.0.0.
 */
import type { OrderIntent } from "./OrderIntent.js";

export type RiskVerdict = "APPROVE" | "RESHAPE" | "REJECT" | "QUARANTINE";

export interface RiskVote {
  schemaVersion: "1.0.0";
  voteId: string;
  intentId: string;             // ties to the OrderIntent
  byBot: string;
  verdict: RiskVerdict;
  reasonCode: string;           // from reason-code registry
  reshape?: Partial<OrderIntent>;
  evaluatedAt: number;
  evaluationMs: number;
  killSwitchActive: boolean;
}
JSON Schema (draft 2020-12)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "polytraders/RiskVote",
  "title": "RiskVote",
  "description": "Risk-bot decision on an OrderIntent.",
  "type": "object",
  "required": ["schemaVersion", "voteId", "intentId", "byBot", "verdict", "reasonCode", "evaluatedAt", "evaluationMs", "killSwitchActive"],
  "additionalProperties": false,
  "properties": {
    "schemaVersion": { "const": "1.0.0" },
    "voteId": { "type": "string", "minLength": 1 },
    "intentId": { "type": "string", "minLength": 1 },
    "byBot": { "type": "string", "pattern": "^[a-z]+\\.[a-z0-9_]+$" },
    "verdict": { "enum": ["APPROVE", "RESHAPE", "REJECT", "QUARANTINE"] },
    "reasonCode": { "type": "string", "pattern": "^(INTEL|DISC|STRAT|RISK|EXEC|SEC|GOV)_[A-Z0-9_]+$" },
    "reshape": { "type": "object", "additionalProperties": true },
    "evaluatedAt": { "type": "number" },
    "evaluationMs": { "type": "number", "minimum": 0 },
    "killSwitchActive": { "type": "boolean" }
  }
}

ExecutionPlan

decision src: src/ExecutionPlan.ts · schema: schemas/ExecutionPlan.schema.json

Output of an Execution Utility — shape, timing and sizing of orders to submit.

TypeScript declaration
/**
 * ExecutionPlan — output of an Execution Utility.
 *
 * Describes the shape, timing and sizing of orders to submit. Each step is
 * a discrete on-chain action. The plan expires; re-quote if execution
 * cannot complete before expiresAt.
 *
 * Frozen for v3.0.0.
 */
export interface ExecutionStep {
  stepNo: number;
  action: "submit" | "amend" | "cancel";
  pricePusd: number;
  sizePusd: number;
  submitAt: number;             // ms epoch — for paced execution
}

export interface ExecutionPlan {
  schemaVersion: "1.0.0";
  planId: string;
  intentId: string;
  steps: ExecutionStep[];
  estimatedFeesPusd: number;
  estimatedSlippageBps: number;
  expiresAt: number;
}
JSON Schema (draft 2020-12)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "polytraders/ExecutionPlan",
  "title": "ExecutionPlan",
  "description": "Output of an Execution Utility — shape, timing and sizing of orders to submit.",
  "type": "object",
  "required": ["schemaVersion", "planId", "intentId", "steps", "estimatedFeesPusd", "estimatedSlippageBps", "expiresAt"],
  "additionalProperties": false,
  "properties": {
    "schemaVersion": { "const": "1.0.0" },
    "planId": { "type": "string", "minLength": 1 },
    "intentId": { "type": "string", "minLength": 1 },
    "steps": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": ["stepNo", "action", "pricePusd", "sizePusd", "submitAt"],
        "additionalProperties": false,
        "properties": {
          "stepNo": { "type": "integer", "minimum": 1 },
          "action": { "enum": ["submit", "amend", "cancel"] },
          "pricePusd": { "type": "number", "minimum": 0, "maximum": 1 },
          "sizePusd": { "type": "number", "minimum": 0 },
          "submitAt": { "type": "number" }
        }
      }
    },
    "estimatedFeesPusd": { "type": "number", "minimum": 0 },
    "estimatedSlippageBps": { "type": "number" },
    "expiresAt": { "type": "number" }
  }
}

ReportEnvelope

telemetry src: src/ReportEnvelope.ts · schema: schemas/ReportEnvelope.schema.json

Universal reporting wrapper — every bot, every decision. The single emission shape from which audit, dashboards and replay are built.

TypeScript declaration
/**
 * ReportEnvelope — universal reporting wrapper.
 *
 * Every bot, every decision. The single emission shape from which the audit
 * log, dashboards, replay simulator and reconcile feed are built.
 *
 * Frozen for v3.0.0.
 */
export type EnvelopeKind =
  | "decision"
  | "signal"
  | "event"
  | "metric"
  | "audit"
  | "error";

export interface ReportEnvelope {
  schemaVersion: "1.0.0";
  envelopeId: string;
  kind: EnvelopeKind;
  botId: string;
  botVersion: string;
  configVersion: string;
  correlationId: string;
  parentDecisionId?: string;
  marketId?: string;
  intentId?: string;
  reasonCode?: string;
  mode: "stub" | "shadow" | "advisory" | "enforced" | "quarantine";
  emittedAt: number;
  payload: Record<string, unknown>;
}
JSON Schema (draft 2020-12)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "polytraders/ReportEnvelope",
  "title": "ReportEnvelope",
  "description": "Universal reporting wrapper — every bot, every decision.",
  "type": "object",
  "required": ["schemaVersion", "envelopeId", "kind", "botId", "botVersion", "configVersion", "correlationId", "mode", "emittedAt", "payload"],
  "additionalProperties": false,
  "properties": {
    "schemaVersion": { "const": "1.0.0" },
    "envelopeId": { "type": "string", "minLength": 1 },
    "kind": { "enum": ["decision", "signal", "event", "metric", "audit", "error"] },
    "botId": { "type": "string", "pattern": "^[a-z]+\\.[a-z0-9_]+$" },
    "botVersion": { "type": "string", "minLength": 1 },
    "configVersion": { "type": "string", "minLength": 1 },
    "correlationId": { "type": "string", "minLength": 1 },
    "parentDecisionId": { "type": "string" },
    "marketId": { "type": "string" },
    "intentId": { "type": "string" },
    "reasonCode": { "type": "string", "pattern": "^(INTEL|DISC|STRAT|RISK|EXEC|SEC|GOV)_[A-Z0-9_]+$" },
    "mode": { "enum": ["stub", "shadow", "advisory", "enforced", "quarantine"] },
    "emittedAt": { "type": "number" },
    "payload": { "type": "object", "additionalProperties": true }
  }
}

ReasonCode

telemetry src: src/ReasonCode.ts · schema: schemas/ReasonCode.schema.json

Registry-aligned reason code. Free-text errors are forbidden — every emit carries one of these.

TypeScript declaration
/**
 * ReasonCode — registry-aligned reason codes.
 *
 * Every emit, every reject, every halt carries one of these. Free-text
 * error messages are forbidden. The full registry is rendered on the
 * dev-guide /standards/reason-codes page; this file is the typed view.
 *
 * Frozen for v3.0.0. New codes are additive only.
 */
export type ReasonCodePrefix =
  | "INTEL"
  | "DISC"
  | "STRAT"
  | "RISK"
  | "EXEC"
  | "SEC"
  | "GOV";

export type ReasonCode = `${ReasonCodePrefix}_${string}`;

/**
 * Sentinel codes guaranteed to exist in every release. Bots MAY reference
 * these by name; the full registry is loaded at runtime.
 */
export const SENTINEL_CODES = {
  INTEL_FEED_STALE: "INTEL_FEED_STALE",
  STRAT_NO_EDGE: "STRAT_NO_EDGE",
  RISK_KILL_SWITCH_ACTIVE: "RISK_KILL_SWITCH_ACTIVE",
  RISK_STALE_BOOK: "RISK_STALE_BOOK",
  RISK_MARKET_HALTED: "RISK_MARKET_HALTED",
  EXEC_BUDGET_BREACH: "EXEC_BUDGET_BREACH",
  SEC_WALLET_FUNDING_SHORT: "SEC_WALLET_FUNDING_SHORT",
  GOV_RECONCILE_MISMATCH: "GOV_RECONCILE_MISMATCH",
  GOV_API_DEGRADED: "GOV_API_DEGRADED",
  GOV_CONFIG_DRIFT: "GOV_CONFIG_DRIFT",
} as const;

export type SentinelCode = (typeof SENTINEL_CODES)[keyof typeof SENTINEL_CODES];
JSON Schema (draft 2020-12)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "polytraders/ReasonCode",
  "title": "ReasonCode",
  "description": "Registry-aligned reason code. Free-text errors are forbidden.",
  "type": "string",
  "pattern": "^(INTEL|DISC|STRAT|RISK|EXEC|SEC|GOV)_[A-Z0-9_]+$",
  "examples": [
    "INTEL_FEED_STALE",
    "STRAT_NO_EDGE",
    "RISK_KILL_SWITCH_ACTIVE",
    "RISK_STALE_BOOK",
    "EXEC_BUDGET_BREACH",
    "SEC_WALLET_FUNDING_SHORT",
    "GOV_RECONCILE_MISMATCH"
  ]
}

Versioning

Every schema carries a schemaVersion field. Bumping a schema is a coordinated rollout: every producer and consumer must support both old and new versions for the duration of the rollout. The migration runbook for each schema lives next to it in the implementation repo.