* fix(review): give the review retry the remaining time budget
A transient API failure on the first review attempt was retried with a
hardcoded 5-minute timeout while the first attempt got the whole
180-minute budget. The retry does not resume the failed review, it
re-runs it from scratch, so on a large PR it spends those five minutes
re-fetching the PR worktree, re-chunking the diff and re-launching
review agents and is then killed on the clock before producing a single
finding. Every retry after a transient failure therefore reported a
timeout, and the resulting comment advised raising --timeout, which does
not affect the retry cap at all.
All attempts already share the single-review budget, so that budget is
the only bound the retry needs. Give every attempt the remaining budget
and gate the retry on having enough of it left to plausibly finish;
below that, report the transient failure so the next run starts over
with a full budget.
* test(review): pin the per-attempt review timeout budget
The retry-loop harness stubbed timeout with `shift; shift; exec "$@"`,
which discarded the duration argument, so no test could observe how much
budget each attempt was given. That is the invariant the retry fix turns
on: a reintroduced per-attempt cap, or a miscalculated remaining budget,
would have left the suite green while making every retry unusable.
Record the duration the stub receives and assert on it. Cover the retry
gate from both sides as well, since every existing scenario runs with a
180-minute budget and none of them exercise the threshold: a budget just
under it must not start a retry that cannot finish, and one just over it
must still retry.