TaifoonTAIFOON
Taifoon · Docs
Concept

Receipts & causality

Every order in the venue belongs to a 3-actor receipt: the decider (who reasoned), the matcher (what crossed), and the trader (who attributed). The causality_ok bit says whether the chain reconstructs without holes — a single boolean that catches most retry bugs and a lot of slippage discrepancies.

The 3-actor schema

{
  "session_id": "warp-2026-05-15-04h-abc123",
  "causality_ok": true,
  "decider": {
    "kind": "llm" | "cubicle" | "manual",
    "decisions": [
      {
        "decision_id": "uuid",
        "t_logical": 1779047520,
        "prompt_hash": "sha256:...",
        "response": { "side": "buy", "qty": "0.01", "price": "78400", "reason": "..." }
      }
    ]
  },
  "matcher": {
    "orders": [
      { "order_id": "uuid", "decision_id": "uuid", "t_placed": 1779047521, "fill_ids": ["..."] }
    ],
    "fills": [
      { "fill_id": "uuid", "order_id": "uuid", "qty": "0.01", "price": "78401.2",
        "was_maker": false, "speed_x": 100.0 }
    ]
  },
  "trader": {
    "attribution": "llm-warp-abc123",
    "wallet": null,
    "execution_mode": "PAPER" | "SHADOW" | "LIVE"
  }
}

What causality_ok means

For every matcher.order_id, there must exist exactly one decider.decision_id upstream. For every matcher.fill_id, the parent order_id must resolve. If both invariants hold, causality_ok = true. If any matcher row has no upstream decision, the bit goes false and the receipt enters a debuggable state — see the diagnosis section below.

Common failure modes

  • Retry without idempotency. Your bot re-posted the same decision twice; the second matcher row has no fresh upstream decision_id.
  • Stale session. You finalised the warp session but a late HTTP response landed an order anyway. Matcher rejected; receipt shows orphaned attempt.
  • Cross-session leak. Two warp sessions share the same attribution and one placed an order without a session header. Phase A.2 hardens this.

Diagnosing a false causality_ok

# Pull the receipt
curl -s https://api.taifoon.dev/tape/v1/sessions/<sid>/receipt > receipt.json

# Find orphaned orders
jq '
  .matcher.orders[]
  | select(.decision_id as $d | $d == null or
           ([$d] | inside([.decider.decisions[].decision_id])) | not)
' receipt.json

The output is the list of orders the matcher accepted but the decider never claimed. Usually one of the failure modes above; occasionally a manual override placed via the UI mid-session (which is fine and produces a kind: "manual" decider row).

Note
Receipts are the unit of billing for LLM-as-trader and the unit of attribution for tournament leaderboards. Treat causality_ok = false as a release-blocker bug, not a warning.