Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,21 @@ Admins can **View as** a sub-user from the top-right of the home layout (`Profil
`hermes_requires <0.20.0` on a "verified" 401 that came from a hand-rolled
`hermes serve` run rather than `hermes webui`. 0.20.0 works. Verify against the
real start path before writing a version constraint.
5. **The agent image build clones two third-party repos over the public internet,
unauthenticated.** `agent/Dockerfile` fetches `NousResearch/hermes-agent` and
`nesquena/hermes-webui` at build time. GitHub rate-limits unauthenticated
requests *per source IP*, and Actions runners share pooled egress addresses, so
`docker compose build` intermittently died on `fatal: unable to access ...: The
requested URL returned error: 429` (exit 128) — 2 of 15 runs, always on branches
whose diff had nothing to do with the agent. Both clones now retry 5x with
backoff, and still print FATAL and exit 1 on exhaustion so a genuinely dead
upstream cannot yield an image with no runtime in it. Two lessons worth keeping:
a CI failure that is *intermittent and unrelated to the diff* is a network or
rate-limit signature, not a code defect — read the log before bisecting the
branch; and the webui clone's pre-existing `|| git clone` fallback looked like
resilience but only ever handled a *moved ref*, re-issuing the identical refused
request against a 429. A fallback that fails the same way as the thing it backs
up is not a fallback.

### Verification Anti-Patterns (do not repeat)

Expand Down
34 changes: 30 additions & 4 deletions agent/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,24 @@ ARG AGENT_RUNTIME_REF=v2026.8.18
ARG AGENT_API_REPO=https://github.com/nesquena/hermes-webui.git
ARG AGENT_API_REF=v0.52.76

# Runtime engine (Python agent)
RUN git clone --depth 1 --branch "${AGENT_RUNTIME_REF}" "${AGENT_RUNTIME_REPO}" runtime \
# Runtime engine (Python agent).
#
# The clone retries with backoff. github.com rate-limits *unauthenticated*
# requests per source IP, and CI runners share pooled egress addresses, so a
# cold clone here intermittently returns HTTP 429 and took the whole image
# build down with it (`fatal: unable to access ...: error: 429`, exit 128 --
# 2 of 15 `docker compose build` runs). This is not CI-only defensiveness: a
# self-host user behind a shared NAT hits the identical refusal. Retry is the
# right layer precisely because the condition is transient; the loop still
# exits non-zero and loudly on the last attempt rather than continuing with no
# runtime cloned.
RUN n=0; rm -rf runtime; \
until git clone --depth 1 --branch "${AGENT_RUNTIME_REF}" "${AGENT_RUNTIME_REPO}" runtime; do \
n=$((n+1)); \
if [ "$n" -ge 5 ]; then echo "FATAL: clone of ${AGENT_RUNTIME_REPO} failed after 5 attempts" >&2; exit 1; fi; \
echo "clone attempt $n failed (HTTP 429 / transient network); retrying in $((n*15))s" >&2; \
rm -rf runtime; sleep $((n*15)); \
done \
&& cd runtime \
&& UV_NO_CONFIG=1 uv sync \
&& if [ -d .venv ] && [ ! -d venv ]; then ln -sfn .venv venv; fi \
Expand All @@ -64,8 +80,18 @@ RUN git clone --depth 1 --branch "${AGENT_RUNTIME_REF}" "${AGENT_RUNTIME_REPO}"

# HTTP API surface the frontend / backend talk to (:8787).
# Default branch is master (not main). Fall back to HEAD if the ref moves.
RUN (git clone --depth 1 --branch "${AGENT_API_REF}" "${AGENT_API_REPO}" api \
|| git clone --depth 1 "${AGENT_API_REPO}" api) \
#
# Same 429 retry as the runtime clone above. Note the inner `||` fallback
# handles only a *moved ref*; on a rate-limit it just re-issues the same
# refused request, so it needed the outer retry to be any use at all.
RUN n=0; rm -rf api; \
until (git clone --depth 1 --branch "${AGENT_API_REF}" "${AGENT_API_REPO}" api \
|| git clone --depth 1 "${AGENT_API_REPO}" api); do \
n=$((n+1)); \
if [ "$n" -ge 5 ]; then echo "FATAL: clone of ${AGENT_API_REPO} failed after 5 attempts" >&2; exit 1; fi; \
echo "clone attempt $n failed (HTTP 429 / transient network); retrying in $((n*15))s" >&2; \
rm -rf api; sleep $((n*15)); \
done \
&& cd api \
&& if [ -f requirements.txt ]; then \
UV_NO_CONFIG=1 uv pip install --python /opt/deepsql-agent/runtime/venv/bin/python -r requirements.txt; \
Expand Down
Loading