# Usage — Drain Execution Records

This directory contains per-session usage ledgers.
Each file tracks token counts, cost, tool call metrics, and runner-side local performance snapshots across every `drainMailboxOnce()` invocation.

## Layout

One JSONL file per session key (colons preserved, slashes escaped with `_`):

```
usage/
  stdio:alice.jsonl       ← drain records for session "stdio:alice"
  lark:mybot.jsonl        ← drain records for session "lark:mybot"
```

## Record Format

Each line is a JSON `DrainRecord`:

```jsonc
{
  "id": "uuid",
  "session_key": "stdio:alice",
  "sdk_session_id": "sess_abc",
  "drain_started_at": "2026-03-02T03:00:00.000Z",
  "drain_duration_ms": 1820,
  "sdk_duration_ms": 1600,
  "events_processed": 2,
  "events_skipped": 0,
  "tool_calls": 5,
  "tool_errors": 0,
  "output_chars": 342,
  "cancelled": false,
  "usage": {
    "total_cost_usd": 0.00438, // USD cost from Claude API
    "input_tokens": 2800,
    "output_tokens": 410,
    "cache_read_input_tokens": 1200,
    "cache_creation_input_tokens": 0
  },
  "perf": {
    "mailbox_merge_ms": 6.1,
    "mailbox_parse_ms": 1.8,
    "session_snapshot_ms": 2.2,
    "session_state_ms": 0.9,
    "outbox_lookup_ms": 0.4,
    "event_read_ms": 1.6,
    "effective_config_ms": 0.7,
    "outbox_emit_ms": 4.3,
    "session_upsert_ms": 1.5,
    "mailbox_finalize_ms": 1.1,
    "sdk_ttft_ms_total": 842,
    "sdk_ttft_samples": 1
  }
}
```

`usage` is absent when the drain was cancelled before the SDK returned a result.
`perf` is optional and records best-effort local timings for the runner-side stages observed during that drain.

## Summary Semantics

`usage.get` returns both per-record detail and an aggregated summary.

- `usage` fields are summed across records in the summary.
- `perf` fields are also summed across records in the summary.
- `sdk_ttft_ms_total / sdk_ttft_samples` are emitted separately so callers can compute average TTFT without losing sample count.

These timings are scoped to one `drainMailboxOnce()` execution. Transport-side or consumer-side costs such as ingress snapshots, replay scans, websocket delivery, and CLI rendering are not part of this ledger yet.

## Querying via RPC

```bash
# All sessions — aggregated summaries
curl -X POST http://localhost:20233/rpc \
  -d '{"jsonrpc":"2.0","id":1,"method":"usage.get","params":{}}'

# One session — full record list + summary
curl -X POST http://localhost:20233/rpc \
  -d '{"jsonrpc":"2.0","id":2,"method":"usage.get","params":{"session_key":"stdio:alice"}}'
```

## Guarantees

- **Append-only**: records are never deleted or modified.
- **Best-effort**: a failed write never disrupts the drain itself.
- **One file per session**: safe for concurrent reads; single-process appends are atomic at the OS level.
