trade_notional.py — scenario setup

1 / 5
USER_MESSAGE = "Sell about $1,000 of ACME from my portfolio to rebalance."

INTENDED_NOTIONAL_CENTS = 100_000   # $1,000 — what user meant
BAD_QUANTITY = 1000                 # shares — agent mistake (190× too large)
SHARE_PRICE_CENTS = 19_000          # $190.00 per share
MAX_ORDER_NOTIONAL_CENTS = 500_000  # $5,000 — the gated fix
Incident: User asks for ~$1k of ACME. Agent will pass quantity=1000 (shares) → ~$190k notional.
Fix under test: Tool blocks any order above $5,000 notional.

trade_notional.py — agent bug (@boundary agent@1)

2 / 5
@boundary("agent", kind="llm", extract_input=agent_input)
def agent_plan(state: dict) -> dict:
    """Simulated LLM — treats '$1,000' as 1,000 shares."""
    tool_call = ToolCall(
        name="place_order",
        arguments={"quantity": BAD_QUANTITY},  # 1000 shares, not $1,000
    )
    return {..., "tool_calls": [tool_call], "finish_reason": "tool_calls"}
Chronicle: @boundary records this LLM step as an envelope. Same code is stubbed during cut-point test.
Bug: Confuses dollar notional with share count. Frozen in the incident fixture.

trade_notional.py — max-amount gate (@boundary place_order@1)

3 / 5
@boundary("place_order", kind="tool", extract_input=_order_input)
def place_order(symbol: str, quantity: int, *, side: str = "sell") -> dict:
    notional_cents = quantity * SHARE_PRICE_CENTS
    if _mode == "gated" and notional_cents > MAX_ORDER_NOTIONAL_CENTS:
        return {"status": "blocked", "blocked": True, ...}

    return {"status": "filled", ...}  # ungated: $190k goes through
Cut-point boundary: This is the only code you change. set_mode("gated") activates the max-amount check.
Record: ungated → incident captured. Test: gated → $190k blocked (> $5k cap).

run.py — Chronicle cut-point test

4 / 5
agent@1 STUB place_order@1 LIVE ★ agent@2 LIVE
scenario.set_mode("gated")
session.load_trace("fixtures/traces/trade-notional/")
session.enable_replay(
    ReplayPlan()
        .stub("agent", 1)
        .live("place_order", 1)   # run gated tool live
        .live("agent", 2)
)
result = scenario.run_agent()
Chronicle: Agent plan replayed from fixture (unchanged). Only place_order runs new gated code.

Terminal — record incident

5 / 5
$ python examples/financial_incidents/run.py trade record

  RECORD  trade-notional
  User request     Sell about $1,000 of ACME from my portfolio to rebalance.

  Node               Kind  Mode   Input                    Output
  agent@1            llm   LIVE   Sell about $1,000...     → place_order(quantity=1000)
  place_order@1      tool  LIVE   quantity=1000              filled: $190,000.00 total
  agent@2            llm   LIVE   tool_result: filled        Done. Sold 1000 ACME...

  Trace exported   fixtures/traces/trade-notional/
Incident recorded: ~$190k sell committed to fixture. Agent behavior preserved for regression.

Terminal — cut-point test

6 / 5
$ python examples/financial_incidents/run.py trade test

  TEST  trade-notional  (cut-point)

  Node               Kind  Mode   Input                    Output
  agent@1            llm   STUB   Sell about $1,000...     → place_order(quantity=1000)
  place_order@1      tool  LIVE   quantity=1000              blocked: exceeds maximum $5,000.00
  agent@2            llm   LIVE   blocked...               Order blocked — $190,000...

  [PASS] order blocked
  [PASS] no shares sold
  [PASS] agent@1 stubbed
  [PASS] place_order ran live
Fix verified: Same agent plan, gated tool blocks $190k. No LLM API calls.