v1.16.0
One flag. --timeout bounds a whole run and, crucially, raises the hard cap each provider used to enforce invisibly — the CLI tools' 5 minutes, ollama's 5, the Anthropic SDK's 10 — because a deadline alone can only cut a run short, never lengthen it.
Added
-
--timeout— bound a run, and raise the ceilings that used to end it early (ADR-0038). Every provider shipped a hard, invisible cap. The CLI-tool providers killed their subprocess after 5 minutes, ollama’s HTTP client after 5, and the Anthropic SDK refuses a non-streaming request that could exceed 10 — it does not wait, it errors with “streaming is required for operations that may take longer than 10 minutes”. A large diff or a slow local model hit those, and there was nothing to do about it but shrink the diff.commitbrief --staged --cli claude --timeout 20m # let the host CLI think commitbrief --staged --timeout 600 # bare integer = seconds commitbrief config set review.timeout 15m # make it the default commitbrief --staged --timeout 0 # ignore that default onceThe interesting part is what
--timeoutis not. Wrapping the run in a context deadline — the obvious implementation — would have been wrong on its own, because a deadline can only ever cut a run short. The CLI backend derives its own child context from the caller’s, somin(5m, 20m)still dies at five; ollama’shttp.Clienttimeout fires independently of the context entirely; and the Anthropic SDK’s ceiling is a pre-flight refusal, not a wait. A user who typed--timeout 20mand still got killed at 5 would have been actively misled. So the value is also handed down to the provider, through a new optionalTimeoutSetterinterface implemented by exactly the three backends that impose a cap of their own. Everyone else — the OpenAI-compatible providers, Gemini — purely follows the context, where the deadline already is the whole story.The value is a Go duration (
90s,10m,1h30m) or a bare whole number of seconds, because--timeout 600is what a CI author types and rejecting it for a missing unit is a papercut with no upside. Resolution is--timeout→review.timeout→ the provider’s built-in, so--timeout 0restores the built-ins for a single run. An invalid or negative value fails before the diff is read, let alone sent.It bounds the whole run — diff acquisition, the provider call, render, and the time you spend at a confirmation prompt. That last part is deliberate: a review parked on a prompt nobody is there to answer is exactly as stuck as one parked on a provider. It applies to every command that can spend real time, including
doctor— whose provider probes otherwise fast-fail at 5 seconds, reporting a link that is merely slow as unreachable — andproviders test. Oncommitbrief mcpit becomes a per-tool-call budget rather than a lifetime for the long-lived server.Expiring is a normal failure: exit code 1, with a message that names the duration and points back at the flag, so a self-inflicted deadline is never mistaken for a provider outage.
-
review.timeoutconfig key — the persistent half of--timeout. Stored as a string (timeout: "10m") so the YAML stays readable, and validated on write:commitbrief config set review.timeout ten-minutesis rejected there rather than breaking every later run.
Fixed
- The smoke-test script asserted a
dry-runlabel the CLI stopped printing several releases ago, somake smokehad been failing on a clean tree. It sits outsidemake checkand the release workflow, which is why nothing caught it. Developer tooling only — no user-facing behavior changed.