# Research API v1

The research API never enters official ratings or calls a provider. Environments
are ephemeral and owned by the authenticated session (loopback dev bypass is
available through `bun run dev`). HTTP POST bodies are JSON; errors are non-2xx
`{error: string}`. Do not retry a step after a transport timeout without resetting:
steps are intentionally not idempotent. Local mode avoids HTTP entirely.

| Operation | Endpoint | Body / result |
|---|---|---|
| Create | POST `/api/research/envs` | `{scenario?,seed?,maxTurns?,turnTicks?,singleAgent?,record?}` → `{id,possibleAgents,...transition}` |
| Reset | POST `/api/research/envs/:id/reset` | `{seed?,scenario?}` → transition at turn zero |
| Step | POST `/api/research/envs/:id/step` | `{actions:{agent:Action}}` → transition |
| Parallel batch | POST `/api/research/batch` | `{steps:[{id,actions}]}` → `{results:[{id,...transition}]}` |
| Replay | GET `/api/research/envs/:id/replay` | `{replay,stateHash}`; recording must be enabled |
| Close | DELETE `/api/research/envs/:id` | `{ok:true}` |
| Protocol | GET `/api/research/schema` | version, scenario inventory, complete JSON Schemas |
| Public suites | GET `/api/proof/suites` | latest 100 public suite manifests, date and status |
| Raw match I/O | GET `/api/matches/:id/io` | recorded result, all calls/decisions and accounting |
| Export | GET `/api/research/data?ids=a,b&format=jsonl` | up to 20 accessible recorded matches; also `parquet` |

Defaults: duel-01, seed 1, maxTurns 240, turnTicks 45, all agents, recording on.
Seed is uint32. Turn ticks 1–60, episode limit 1–10,000. Reset can switch scenarios
only when spawn count stays fixed; single-agent mode requires Duel. Missing reset
fields use creation defaults. HTTP max 32 environments per owner, 256 per process,
30-minute idle expiration (pruned on access), 64 KiB requests, normal server rate
limits apply. Batch IDs must be unique, max 32. Use local mode for large jobs.
Environments do not survive restart; export before closing or idling out.

A transition contains `agents` (next live roster), plus dictionaries keyed by the
agents that acted: `observations`, `rewards`, `terminations`, `truncations`, `infos`.
On reset these dictionaries cover all agents with zero rewards and false flags.
Infos contain a canonical SHA-256 state hash and winners. Each action is validated
against the protocol Action schema; missing, invalid or extra agents reject the
whole step without advancing. Actions resolve simultaneously from the prior state.
No timeout/noop substitution is used for research requests. The optional house bot
chooses from the same prior state with its own explicit seed.

KO/ring-out: terminated, reward -1, last observation included. Natural survivor:
terminated, reward +1. All other turns: zero. Episode cap: surviving agents truncate
with zero reward; engine HP-tiebreak winners are diagnostic only. A terminal step
has `agents:[]`; reset before another step. In FFA, removed agents never reappear.

Python setup and encoding: packages/sdk-python/README.md. Local TypeScript fast
path and CLI JSONL: packages/research/README.md. PPO is an attributed training
example; no learning-quality or benchmark-strength claim is made.
