# T10.9 — CLI: runs list/episode/transcript | Field | Value | |---|---| | Phase | P10 — Orchestration | | Size | S — under 1 day | | Status | Not started | | Flags | — | | Spec | inlined below | | Blocks | — | ## Goal Three thin CLI wrappers over the HTTP API — `poimen runs list --goal `, `poimen runs episode `, `poimen runs transcript ` — the whole troubleshooting UX, no dashboard. ## Facts (inlined — no spec read needed) - Thin HTTP clients, not a second implementation of the query logic already in T10.1/T1.7/T1.6 — the CLI calls the same routes T10.4 exposes. - Rides the eventual shared `poimen` binary (T9.3); workable as a standalone binary if T9.3 hasn't landed yet — soft dependency, not a hard block. - `episode` output must include failed attempts by default — T1.7's own rule. A CLI that hides them defeats the purpose of the command. - `transcript` dereferences the `BlobRef` server-side, via the HTTP route (T10.4) — the CLI never talks to `BlobStore` directly. ## Steps 1. `poimen runs list [--goal ] [--status ]` → `GET /v1/runs`, table output (`run_id`, `status`, `goal_id`, age). 2. `poimen runs episode ` → `GET /v1/runs/{id}/episode`, human-readable rendering of `EpisodeView` including failed attempts, clearly marked. 3. `poimen runs transcript ` → `GET .../transcript`, prints prompt/output text, dereferenced server-side. 4. Exit codes: non-zero on API error, distinct code for "run not found" vs "transport error" — operator scripting depends on this distinction. 5. Test against a live embedded API instance (T10.4's test harness) with a run that has a retry and a rewind. ## Acceptance - `episode` on a run with two failed attempts and one success shows all three, visually distinguishable. - `transcript` returns the same text content as reading the `BlobRef` directly through `BlobStore` in a test assertion. ## Verify **Harness:** T10.4's `axum` test instance, one run with a retry (two attempts, one failed) and a rewind. **Integration test** — `tests/it_cli_runs_ux.rs`: 1. `runs list --goal G`; assert output contains exactly the runs tagged G, none untagged. 2. `runs episode `; assert both the failed and successful attempt appear — count them explicitly, not just non-empty output. 3. `runs transcript 1` (the failed attempt) and `... 2` (the retry); assert distinct content for each, matching their respective `BlobRef`s byte-for-byte. 4. Run-not-found case; assert a distinct exit code from a transport-down case. **Command:** `cargo test -p cli runs_ux` **False pass:** - Step 2 checking only that output is non-empty — a CLI silently filtering to the winning attempt (T1.7's own named trap) passes a non-empty check while hiding exactly the debugging signal requested. ## Traps - Filtering episode output to "just the successful attempt for clarity" — T1.7's trap restated at the CLI layer; the failed attempts are the reason this command exists. - Fetching transcripts client-side by resolving `BlobRef` directly instead of through the HTTP route. Bypasses the tenant auth checks the API enforces. --- Background (not required to do this task): [T10.4-http-api-surface.md](T10.4-http-api-surface.md) · [T1.7-episode-query-surface.md](T1.7-episode-query-surface.md) · [T1.6-prompt-and-output-blob-capture.md](T1.6-prompt-and-output-blob-capture.md) · [T8.2-metrics.md](T8.2-metrics.md)