lib/retry.sh's header (as of #910) enumerates the pre-agent third-party reaches as three — install-claude-binary.sh (claude.ai), install-proxy-uv.sh (astral.sh), install-codex-cli.sh (registry.npmjs.org). There is a fourth, and it is not on the window.
proxy/setup-sandbox.sh:319 warms the uvx cache before backgrounding the proxy:
MITMPROXY="mitmproxy==${MITMPROXY_VERSION}"
UVX="${TEND_UV_DIR}/uvx"
"$UVX" --from "$MITMPROXY" mitmdump --version >/dev/null
On a cold UV_CACHE_DIR that resolves and downloads mitmproxy from PyPI. The script runs under set -euo pipefail (line 33), so a failed resolve exits non-zero, and the calling step — Set up credential-isolation sandbox, claude/action.yaml:205 — runs three steps ahead of Run Claude at line 273. A PyPI blip there costs the whole run having done none of the work the trigger asked for: exactly the failure #906/#907/#908/#910 exist to close, on the one remaining reach.
Why the cache doesn't cover it
The actions/cache key is version-scoped — tend-mitmproxy-${{ runner.os }}-${{ inputs.mitmproxy_version }} (claude/action.yaml:203) — so a miss isn't a rare event, it's scheduled. Every mitmproxy_version bump misses on the first run for every workflow, and the weekly bump PR is the moment a PyPI blip is most expensive: it would redden the PR that carries the security-relevant pin. Evicted entries (7-day idle, or the repo's 10 GB cap) miss the same way.
Proposed fix
retry_install is sourceable from proxy/setup-sandbox.sh the way the installer scripts source it — the lib is at shared/steps/lib/retry.sh and the script already resolves ${ACTION_PATH}. So:
. "${ACTION_PATH}/shared/steps/lib/retry.sh"
retry_install "mitmproxy ${MITMPROXY_VERSION}" \
"'$UVX' --from '$MITMPROXY' mitmdump --version >/dev/null"
Two things to check when picking it up, neither settled here:
Also update lib/retry.sh's header enumeration from three reaches to four.
Raised in the review of #910.
lib/retry.sh's header (as of #910) enumerates the pre-agent third-party reaches as three —install-claude-binary.sh(claude.ai),install-proxy-uv.sh(astral.sh),install-codex-cli.sh(registry.npmjs.org). There is a fourth, and it is not on the window.proxy/setup-sandbox.sh:319warms the uvx cache before backgrounding the proxy:On a cold
UV_CACHE_DIRthat resolves and downloads mitmproxy from PyPI. The script runs underset -euo pipefail(line 33), so a failed resolve exits non-zero, and the calling step —Set up credential-isolation sandbox,claude/action.yaml:205— runs three steps ahead ofRun Claudeat line 273. A PyPI blip there costs the whole run having done none of the work the trigger asked for: exactly the failure #906/#907/#908/#910 exist to close, on the one remaining reach.Why the cache doesn't cover it
The
actions/cachekey is version-scoped —tend-mitmproxy-${{ runner.os }}-${{ inputs.mitmproxy_version }}(claude/action.yaml:203) — so a miss isn't a rare event, it's scheduled. Everymitmproxy_versionbump misses on the first run for every workflow, and the weekly bump PR is the moment a PyPI blip is most expensive: it would redden the PR that carries the security-relevant pin. Evicted entries (7-day idle, or the repo's 10 GB cap) miss the same way.Proposed fix
retry_installis sourceable fromproxy/setup-sandbox.shthe way the installer scripts source it — the lib is atshared/steps/lib/retry.shand the script already resolves${ACTION_PATH}. So:Two things to check when picking it up, neither settled here:
claude/action.yaml:196-197calls it a "~20s resolve" but that figure isn't dated and predates several version bumps. Time it on a runner with a cleared cache before assuming 60s is generous. If it isn't, that's the case for the optional third parameter (retry_install LABEL COMMAND [TIMEOUT]) that codex: the CLI install is the one pre-agent installer not on the shared retry window #909 raised and fix(install): give the codex CLI install the same retry window #910 declined — npm didn't need it; this might.mitmdumpon the next line is a separate question. It's the same PyPI reach on a cache miss, but it'snohup'd with a readiness wait after it, so a retry there isn't the same shape and shouldn't be folded in without thinking about the wait.Also update
lib/retry.sh's header enumeration from three reaches to four.Raised in the review of #910.