# Hotato 0.4.0

Released 2026-07-08.

0.4.0 turns Hotato from a one-call scorer into a monitoring loop: connect a stack once, pull recent recordings, rank the candidate turn-taking moments across them, then carry a labeled fix to a paste-ready patch and a before/after proof.

## Highlights

- **`hotato connect` / `pull` / `sweep`.** `connect <stack>` runs a live auth check and stores credentials locally at `~/.hotato/connections.json` (mode 0600), sent nowhere but the vendor's own API. `pull` bulk-fetches recent recordings through each platform's verified list endpoint. `sweep` runs `pull` and then the zero-config `analyze` over the pulled folder, writing one self-contained offline dashboard of ranked candidate moments. Only verified endpoints are used; Retell pulls by explicit `--call-id`, and that limitation is documented rather than papered over.
- **`hotato analyze <folder>`, zero-config folder analysis.** Point it at a folder of dual-channel recordings and it ranks candidate turn-taking moments across all of them by measured salience, no scenarios, labels, onsets, or flags. Top moments embed the real audio inline with a playhead synced to the timeline, so you press play and hear the bug land where the chart marks it. `--format json` emits the ranked candidates for an agent to drive. Everything is framed as measured candidates you review and label; never a pass/fail, a failure count, or an intent claim.
- **`hotato patch` / `verify` / `loop`, the closed loop.** `patch <fixplan.json>` renders a fix plan into a literal, paste-ready artifact per platform: a JSON merge-patch body plus a ready curl for the platform's real config endpoint, or the exact constructor-kwarg source edit where config lives in agent code. It produces the change and never applies it. `verify --before --after` reports battery-scale movement ("N of M fixtures that used to fail now pass, K of L hold fixtures still pass"), refuses the headline claim under low n, and reports coincidence, never causation. `loop` orchestrates the whole cycle with state across runs and never auto-labels or auto-applies.
- **`hotato ingest`, the passive on-ramp.** Wire a platform webhook to invoke it and each completed call is fetched and scanned for candidate moments automatically. Discovery only: webhook payloads are treated strictly as untrusted data, exit 0 means it ran, and nothing is auto-labeled, auto-fixtured, or auto-tuned. Per-stack recipes in `docs/INGEST.md`.
- **`hotato describe`, the generated capability manifest.** One call (json or text) built from the live parser: every subcommand, argument, and exit code, so an agent learns the whole CLI without scraping `--help` across 18 subparsers. Exit-code epilogs are now uniform across every subcommand, and `doctor --format json` emits the machine envelope.
- **Five more stacks behind `--allow-mono`.** Bland, ElevenLabs, Synthflow, Millis, and Cartesia capture and pull with exact verified endpoints. These platforms record one combined track, so scoring is degraded, gated behind the flag, and labeled indicative only.

## Changed

- **`hotato demo` scores two real recorded calls.** The packaged battery replaces synthetic audio with two operator-recorded probe calls against a scripted assistant on a provider's default interruption settings: a missed interruption (0.25 s talk-over) and a false stop on a soft backchannel. Full provenance and attestation ride in the scenario metadata, the only human voice is the recording operator's own, and two runs remain byte-identical.

## Fixed

- Atomic writes for recording downloads, the sweep dashboard, and every `--out`, so an interrupted run cannot truncate a previously good artifact.
- Every JSON emitter refuses NaN and Infinity; a non-finite value surfaces as a clean exit 2, never invalid JSON.
- `.hotato/loop-state.json` is validated on load; an incomplete or hand-edited state file is a clean error, never a KeyError.
- Malformed WAV headers, identical caller/agent channels, invalid onsets, and bad flags are refused up front with plain reasons, and one bad file never aborts an `analyze` or `loop` batch.
- Wording in `docs/WHY.md` and `README.md` softened where a frequency claim had no source.

## Security

- Default-deny SSRF blocking on every vendor download URL: loopback, private, link-local, and reserved hosts are refused, re-checked on every redirect, and credential headers are stripped on cross-host redirects. `HOTATO_ALLOW_PRIVATE_URLS` is the explicit opt-out.
- MCP server input paths are sandboxed, mirroring the existing report-path sandbox.
- `ingest` local recording paths require `HOTATO_INGEST_DIR` and fail closed, so a forged payload cannot point the scanner at an arbitrary file.
- Scenario ids are validated as safe path segments, closing a crafted-pack path that could read or exfiltrate an arbitrary local WAV.

## CI and docs

- A new sdist guard job builds the sdist, installs only the extracted tree into a fresh venv, and runs the full suite from inside it, catching the green-wheel-broken-sdist failure mode that shipped unguarded in 0.2.3 and 0.3.0.
- `server.json` MCP registry manifest, `docs/MCP.md` with copy-paste client configs, `docs/CONNECT.md`, an honest rewrite of `docs/ADAPTER-STATUS.md`, `llms.txt` reconciled to the real command surface, the new concatenated `llms-full.txt`, and `CITATION.cff`.
