fix(install): give the proxy uv install the same retry window - #907
fix(install): give the proxy uv install the same retry window#907tend-agent wants to merge 1 commit into
Conversation
tend-agent
left a comment
There was a problem hiding this comment.
The retry loop is now byte-identical in install-claude-binary.sh and install-proxy-uv.sh with no shared source of truth, so the next adjustment to the window — or a bug in the jitter expression — has to be found and applied twice, and the two rationale comments already differ in wording while describing the same policy. The repo has the mechanism for this: shared/steps/lib/run-issue.sh is sourced by both rate-limit-preflight.sh and report-failure.sh via . "${SCRIPT_DIR}/lib/run-issue.sh". Extraction isn't free here, though — the claude copy runs inside sudo -u "$SANDBOX" env … bash <<'EOF' as the sandbox user, so a sourced helper would need the lib path threaded through that env and the file readable by the sandbox UID, which I haven't verified. A follow-up rather than a change to this PR, but worth deciding deliberately: the duplication is what lets the two windows drift apart later.
Nothing else — the loop tracks #906 line for line, shellcheck -S warning is clean on the modified script, and the four new tests fail against the base branch's script in exactly the three places the PR body claims.
|
Extracted, in #908, stacked on this branch so the diff there is only the extraction. The sandbox boundary you flagged as unverified is real, and it rules out the obvious approach: Concatenating the lib onto the front of the stdin stream sidesteps it — the file is read on the runner side of the privilege drop, and cat "${SCRIPT_DIR}/lib/retry.sh" - <<'EOF' \
| sudo -u "$SANDBOX" env HOME="$AGENT_HOME" … bash
set -euo pipefail
retry_install "claude $CLAUDE_VERSION" \
"curl -fsSL https://claude.ai/install.sh | bash -s -- '$CLAUDE_VERSION'"
EOFOne correction to the review: the two loops aren't byte-identical — the fetched command and both echo strings differ, so what's shared is the five lines of window mechanics ( The eight tests from #906 and #907 pass unmodified against the lib, which is the useful signal: the window is now asserted from both sides of the Left this PR alone rather than folding the extraction in — but if three PRs for one window is worse than a slightly wider one, squashing #908 into this branch loses nothing. |
|
First production occurrence of this exact path failing, from a Run 31422711935, leg The failure shape is a stall, not a fast-403 burst. Every attempt hit the Siblings on the same run installed fine seconds either side, which argues against the shared-egress rate-limit rationale given for jitter in #908. Nothing in the log says why. Sources
|
…will convert (#914) ## Problem Filing an issue in this repo is not a note to a maintainer — `tend-triage` fires on `issues`. Twice in 66 minutes this morning a run filed an issue describing a fix it had already fully scoped, and triage then re-derived that fix from the issue body and opened the PR: | Issue filed | Triage boots | Turns / cost | PR opened | |---|---|---|---| | [#909](#909) 07:18:53Z | [31300864191](https://github.com/max-sixty/tend/actions/runs/31300864191) 07:18:56Z | 64 / $4.08 | [#910](#910) `fix/issue-909` 07:25:17Z | | [#911](#911) 07:39:37Z | [31301666194](https://github.com/max-sixty/tend/actions/runs/31301666194) 07:39:40Z | 58 / $4.20 | [#912](#912) `fix/issue-911` 07:49:37Z | Three seconds from issue to triage boot in both cases. Neither issue was under-specified: #909 named `codex/action.yaml:120-124` and proposed extracting to `shared/steps/install-codex-cli.sh` sourcing `lib/retry.sh`; #911 named `proxy/setup-sandbox.sh:319` and proposed the same treatment. Both PRs implemented exactly the proposal. Triage's own session log confirms the conversion — run 31300864191's artifact is `claude-session-logs-n909`, and it ran `gh pr create --head fix/issue-909`, classifying the reporter as "the bot's own review of #908". So the issue bought nothing and cost $8.28 of agent time plus two threads. The filing run could have opened both PRs itself. <details><summary>Wider context — the cascade this sat inside</summary> A single transient CDN 403 lost one matrix leg of `review-reviewers` run [31297986524](https://github.com/max-sixty/tend/actions/runs/31297986524) at 06:03Z (`curl: (22) ... 403`, three attempts inside 15.7 s, the other four legs installing the same version from the same runner at the same moment succeeded). Between 06:29Z and 07:56Z that produced #906, #907, #908, #909, #910, #911 and #912 — five stacked PRs and two issues, none merged, across ~14 agent runs and roughly $30 at list prices. The engineering is sound and correctly atomic; this PR does not argue with any of it. Only the two issue hops are pure overhead, and they are the part with a mechanical fix. </details> ## Solution A short section in `running-in-ci` — the skill every workflow loads — stating that an issue in this repo is picked up by triage, so a fix you can already scope should be opened as a PR in the same run. The escape hatches stay: too large or ambiguous to fix, needs a maintainer decision, or verification is out of reach from CI. This generalizes a rule that already exists but only in two skills: `review-runs` ("Issue (fallback): Only for problems too large or ambiguous to fix directly") and `review-reviewers` carry it, so the runs that have it don't do this. The runs that did — a `tend-review` session and a `tend-mention` session — load neither. ## Gate assessment - **Evidence level**: High — 2 occurrences this window, and the self-amplifying-output family has been recorded in prior windows (2026-08-04: review→fix ping-pong over five rounds; two bot PRs seven minutes apart on one root cause). - **Structural**: yes for the costly half. The bot's choice to file rather than fix is stochastic, but once an issue exists the triage conversion has no decision point — it fires on the `issues` event every time. - **Change type**: targeted fix (one short section), normal evidence bar. - **Both gates**: pass. Found by `/tend-ci-runner:review-runs` on run [31302531474](https://github.com/max-sixty/tend/actions/runs/31302531474). --------- Co-authored-by: tend-agent <270458913+tend-agent@users.noreply.github.com>
Stacked on #906 — based on its branch, so the diff here is only the
install-proxy-uv.shhalf. GitHub retargets this tomainwhen #906 merges.Problem
#906 widens the retry window on
install-claude-binary.shafter a CDN 403 burst costreview-reviewersa matrix leg. Reviewing it surfaced thatshared/steps/install-proxy-uv.shcarries the identical loop —for i in 1 2 3,sleep $((i * 5)),after 3 attempts— and shares every property the rationale rests on:claude/action.yaml), ~80 lines ahead of the claude install, so it is equally ahead of the agent step and equally invisible toReport failure, which is gated onsteps.claude.outcome == 'failure'. A run lost here files notend-outagerow.review-reviewerslegs at once, so a rate limit hits them together and an unjittered backoff has them retry together.A 403 from
astral.shinstead ofclaude.ailoses the leg identically. Fixing only the claude half leaves the exposure roughly halved rather than closed.Change
Same shape as #906: five attempts, exponential backoff, jitter — 5, 10, 20, 40 seconds plus 0–9s each. Happy path unchanged (one fetch, no sleep).
Worth stating the same ceiling caveat: when
curlfails fast the added cost to a doomed install is ~95s; when the CDN hangs, each attempt burns the fulltimeout 60and the worst case goes from ~195s to ~411s.Tests
install-proxy-uv.shhad no test coverage at all. Four tests added, reusing the fixture pattern #906 establishes (_fake_bin,FAKE_SLEEP_RECORDING, the attempt/sleep readers) with acurlfake that emits astral's installer shape — read on stdin bysh -s --, so it stays POSIX rather than bash. Reusing those helpers is why this is stacked rather than parallel; the alternative was a second copy of each fake and a conflict at the end of the file.Three of the four fail against the pre-fix script:
[5, 10]Full generator suite: 387 passed.
shellcheck -S warningclean on the modified script;ruff checkandruff format --checkclean.