agent-proxy: validate a real docker build boots + serves (fix container had no daemon) #24

Closed
opened 2026-07-02 08:23:42 +00:00 by coilysiren · 3 comments
Owner

Context

Follow-up flagged in the agent-proxy#22 retrospective. That fix corrected the dependency metadata and validated the exact uv sync --frozen --no-dev install path the Dockerfile uses, but it could not run an actual docker build (no docker daemon in the fix container). The image build was the thing that was broken, so it should be exercised end to end at least once.

Ask

Run a real docker build of the agent-proxy image from main and confirm it boots and serves, not just that the deps resolve:

  1. docker build -t agent-proxy-test . completes clean.
  2. docker run the image and confirm python -m app.main binds without a ModuleNotFoundError and stays up (no restart loop).
  3. Hit /v1/healthz, /v1/models, and /metrics against the running container and confirm they respond.

If any of that fails, fix the Dockerfile/entrypoint until it passes. test-container.sh already stubs the build check - extend it (or add a small script) so this is repeatable, and wire it into the repo's test verb if that fits the house style.

Acceptance

  • A documented, repeatable command builds the image and confirms a live boot + healthz/models/metrics response.
  • test-container.sh (or its replacement) actually asserts a bootable container, not just a successful build.

Refs

  • agent-proxy#22 (the dependency + surface fix this validates).
  • infrastructure#431 already made the ansible role --build on converge, so a broken build would now surface on Kai's next ward exec ansible-sync - this makes it catchable in the repo instead.
## Context Follow-up flagged in the agent-proxy#22 retrospective. That fix corrected the dependency metadata and validated the exact `uv sync --frozen --no-dev` install path the Dockerfile uses, but it could not run an actual `docker build` (no docker daemon in the fix container). The image build was the thing that was broken, so it should be exercised end to end at least once. ## Ask Run a real `docker build` of the agent-proxy image from `main` and confirm it boots and serves, not just that the deps resolve: 1. `docker build -t agent-proxy-test .` completes clean. 2. `docker run` the image and confirm `python -m app.main` binds without a `ModuleNotFoundError` and stays up (no restart loop). 3. Hit `/v1/healthz`, `/v1/models`, and `/metrics` against the running container and confirm they respond. If any of that fails, fix the Dockerfile/entrypoint until it passes. `test-container.sh` already stubs the build check - extend it (or add a small script) so this is repeatable, and wire it into the repo's test verb if that fits the house style. ## Acceptance - A documented, repeatable command builds the image and confirms a live boot + healthz/models/metrics response. - `test-container.sh` (or its replacement) actually asserts a bootable container, not just a successful build. ## Refs - agent-proxy#22 (the dependency + surface fix this validates). - infrastructure#431 already made the ansible role `--build` on converge, so a broken build would now surface on Kai's next `ward exec ansible-sync` - this makes it catchable in the repo instead.
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-agent-proxy-24 on host kais-macbook-pro-2.local is carrying this issue (reserved 2026-07-02T08:24:04Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-agent-proxy-24` on host `kais-macbook-pro-2.local` is carrying this issue (reserved 2026-07-02T08:24:04Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - test-container.sh now builds+boots+serves the image (daemon path), plus a daemonless boot_probe.sh, both landed on main and #24 closed.

Honest retro: the ironic part is this fix container also had no Docker daemon - and no way to get one (no root, no uidmap/iptables, rootless setup refused). So I couldn't press the actual docker build button myself, which is the one thing the issue most wanted. What I could do was validate the thing that actually matters: I reproduced the Dockerfile's exact install path (uv sync --frozen --no-dev) and CMD (.venv/bin/python -m app.main), booted it, and confirmed /healthz + /v1/models + /metrics all respond and the process stays up. That runs green here and is now ward boot-probe. The daemon-requiring ward test-container is written and bash-checked but has NOT been executed against a real daemon from this box - infrastructure#431's --build-on-converge is what will exercise it for real on Kai's next ansible-sync.

One surprise worth flagging: the issue says probe /v1/healthz, but app.main registers healthz at /healthz (unprefixed), so that's what I assert - noted inline in the probe script. Confidence is high on the boot logic and the daemonless proof; medium on the docker path purely because I couldn't run it end to end here. A mid-run git fetch also caught issue-25 landing pytest-asyncio, which I merged cleanly (async tests now run: 27 passed).

Follow-up worth filing: give the engineer container a real (or rootless-capable) Docker daemon, or wire ward test-container into a CI leg that has one, so the daemon path gets asserted in-repo rather than only on ansible converge.

WARD-OUTCOME: done - test-container.sh now builds+boots+serves the image (daemon path), plus a daemonless boot_probe.sh, both landed on main and #24 closed. Honest retro: the ironic part is this fix container also had no Docker daemon - and no way to get one (no root, no `uidmap`/`iptables`, rootless setup refused). So I couldn't press the actual `docker build` button myself, which is the one thing the issue most wanted. What I *could* do was validate the thing that actually matters: I reproduced the Dockerfile's exact install path (`uv sync --frozen --no-dev`) and CMD (`.venv/bin/python -m app.main`), booted it, and confirmed `/healthz` + `/v1/models` + `/metrics` all respond and the process stays up. That runs green here and is now `ward boot-probe`. The daemon-requiring `ward test-container` is written and bash-checked but has NOT been executed against a real daemon from this box - infrastructure#431's `--build`-on-converge is what will exercise it for real on Kai's next ansible-sync. One surprise worth flagging: the issue says probe `/v1/healthz`, but `app.main` registers healthz at `/healthz` (unprefixed), so that's what I assert - noted inline in the probe script. Confidence is high on the boot logic and the daemonless proof; medium on the docker path purely because I couldn't run it end to end here. A mid-run `git fetch` also caught issue-25 landing pytest-asyncio, which I merged cleanly (async tests now run: 27 passed). Follow-up worth filing: give the engineer container a real (or rootless-capable) Docker daemon, or wire `ward test-container` into a CI leg that has one, so the daemon path gets asserted in-repo rather than only on ansible converge.
Author
Owner

Scoping note: the live docker build + boot half of this is blocked on ward#522 - engineer containers currently have no usable docker daemon (the host socket is mounted only for the dispatch path, not detached engineer runs, and dev-base ships no docker CLI). ward#522 is designing that capability (docker-out-of-docker vs true DinD vs rootless/sysbox, reconciled with SECURITY.md's socket-as-escape-vector note).

So scope this run to the daemon-free part, which stands alone and is useful now:

  • Make test-container.sh (or a replacement) a real, repeatable build+boot+healthz/models/metrics check, ready to run wherever a daemon exists.
  • Validate the uv sync --frozen --no-dev install path resolves cleanly.
  • Wire the check into the repo's test verb so it runs the moment ward#522 gives engineers a daemon.

Leave the actual live docker build execution as the acceptance step that unblocks once ward#522 lands - do not block or salvage this run waiting on a daemon. Note in the outcome that live-build verification is deferred to ward#522.

Scoping note: the **live `docker build` + boot** half of this is blocked on ward#522 - engineer containers currently have no usable docker daemon (the host socket is mounted only for the dispatch path, not detached engineer runs, and dev-base ships no docker CLI). ward#522 is designing that capability (docker-out-of-docker vs true DinD vs rootless/sysbox, reconciled with SECURITY.md's socket-as-escape-vector note). So scope this run to the **daemon-free** part, which stands alone and is useful now: - Make `test-container.sh` (or a replacement) a real, repeatable build+boot+healthz/models/metrics check, ready to run wherever a daemon exists. - Validate the `uv sync --frozen --no-dev` install path resolves cleanly. - Wire the check into the repo's test verb so it runs the moment ward#522 gives engineers a daemon. Leave the actual live `docker build` execution as the acceptance step that unblocks once ward#522 lands - do not block or salvage this run waiting on a daemon. Note in the outcome that live-build verification is deferred to ward#522.
Sign in to join this conversation.
No description provided.