{"name":"thunderdome","routes":{"POST /spike/seed":"Create a repo and push a demo app (once): { repo?, app? }, default thunderdome-sample with sample-app. Apps are the folders in demo/.","POST /spike/day1":"Seed, fork, push from a sandbox, and read the commit back.","GET /tasks":"The race list, newest first (at most 50). No auth.","POST /tasks":"Create a task: { repo | template, prompt, agents: 3..5 }. A template is forked into a fresh source repo <template>-<id> first. Returns 1 fork and 1 write token per agent.","GET /tasks/:id":"Task state (no tokens). Once judged, verdict holds the winner, the why and the merge result. No auth.","POST /tasks/:id/run":"Start every agent at once, each in its own sandbox on its own fork. When every agent ends, the judge and the merge start on their own.","GET /tasks/:id/steps?after=<seq>":"The agents' step log, oldest first. No auth.","GET /tasks/:id/claims":"The claim board: active claims and history. No auth.","POST /tasks/:id/claims":"Claim files for an agent: { agent, files, shared? }. Files others hold are claimed as shared and listed in clashes.","POST /tasks/:id/release":"Release an agent's files: { agent, files? }.","GET /tasks/:id/live":"WebSocket of live task events: a snapshot, then every change. No auth.","POST /tasks/:id/judge":"Start the judge by hand on a finished task if it did not start on its own. 409 when it exists.","GET /tasks/:id/judge":"The judge's status and, when done, its output: scores, why and the ship result. No auth.","GET /tasks/:id/forks/:agent/diff":"The diff the judge scored for an agent's fork: { agent, diff, clipped }. 404 before the judge saved it. No auth.","GET /tasks/:id/shots/:agent/desktop.jpg":"A screenshot the look step kept of the agent's preview (also phone.jpg; agent `before` is the source). 404 when there is none. No auth.","POST /play":"Start a 5-agent race on a demo template: { template, prompt, invite? }. Daily quota; 202 { id, page, remaining }. No auth.","GET /play/quota":"Today's play quota: { day, used, limit, remaining, invite }. No auth.","GET /play":"The run-your-own-race page. No auth.","GET /race/:id":"The live race page for a task. No auth; the read routes it uses are public too.","GET /race/:id/card.png":"The race's result card (1200x630), for link previews. Drawn on the first request after the verdict; 404 before it. No auth.","GET /races":"The race gallery page. No auth.","POST /admin/races":"Add races from before the index: { ids }.","POST /admin/purge":"Delete races for good (repos, state, race list entry): { ids } or {} for every listed race. Running races are skipped.","GET /admin/model-check":"Test the Worker's model key against the model API. Shows the key type and length, never the key.","POST /admin/retain":"Run retention now: judged races older than RACE_RETENTION_DAYS (outside the gallery's newest 50) lose their repos; replays stay. The cron runs this daily."}}