explore dispatch is dead: no docker client binary in the explore image (socket mounted, CLI absent) - distinct from ward#319 #321

Closed
opened 2026-06-25 08:26:40 +00:00 by coilysiren · 2 comments
Owner

Symptom

Dispatching a sibling headless run from inside a warded explore session fails before it reaches the socket. From an explore container on agentic-os:

$ warded coilyco-flight-deck/ward#320
ward agent headless --driver claude: carrying coilyco-flight-deck/ward#320 - "..."
ward agent headless --driver claude: image pull failed (shell: docker not found on PATH: exec: "docker": executable file not found in $PATH); trying the local image
ward: shell: docker not found on PATH: exec: "docker": executable file not found in $PATH

Root cause

The explore container has no docker client binary at all. Probed from inside:

  • PATH=/usr/local/go/bin:/usr/local/bin:/usr/bin:/bin
  • command -v docker / nerdctl / podman - all empty
  • find / -name docker -type f - nothing
  • /var/run/docker.sock is mounted (srw-rw---- root root)

So the host socket is bind-mounted, but ward agent headless / warded shells out to the docker CLI to pull the image and run the sibling, and that client binary is not installed in the explore image. Dispatch dies at the exec: "docker" lookup, never reaching the socket.

Why this matters

This is the core promise of explore mode. docs/agent-explore.md and the composed read-only block both state that the docker socket is mounted "so warded #N works from inside" and that "Dispatching commissioned work is the point of explore." With no docker client in the image, the entire file-and-dispatch loop is dead in the water: an explore session can file issues but cannot commission any of them.

Distinct from ward#319

ward#319 is about the dropped non-root agent lacking group access to a mounted socket (a root:root 0600 socket with no usable group). That presupposes a docker client exists and is reaching the socket. This is upstream of that: there is no client binary to invoke in the first place, so dispatch fails identically whether the socket is reachable or not. ward#319's root-socat-bridge fix does not address a missing client.

Fix direction

The explore (and sandbox, if it shares the image) container image needs a docker client on PATH - the CLI binary only, talking to the mounted socket, not a full daemon. Confirm whether ward agent sandbox hits the same wall (it dispatches too), and whether the --with-repo/sibling path in headless mode is meant to use the docker CLI or could use an API client against the socket directly. Add a --print-time or bring-up-time preflight that fails loudly ("explore can file but cannot dispatch: no docker client in image") instead of only surfacing at dispatch.

Blocks

ward#320 (explore doctrine sharpening) was filed from this same session but could not be dispatched because of this. Any explore-commissioned work is blocked on this until the explore image carries a docker client.

Filed from the same warded explore session on agentic-os that hit the failure.

## Symptom Dispatching a sibling headless run from inside a `warded explore` session fails before it reaches the socket. From an explore container on agentic-os: ``` $ warded coilyco-flight-deck/ward#320 ward agent headless --driver claude: carrying coilyco-flight-deck/ward#320 - "..." ward agent headless --driver claude: image pull failed (shell: docker not found on PATH: exec: "docker": executable file not found in $PATH); trying the local image ward: shell: docker not found on PATH: exec: "docker": executable file not found in $PATH ``` ## Root cause The explore container has **no docker client binary at all**. Probed from inside: - `PATH=/usr/local/go/bin:/usr/local/bin:/usr/bin:/bin` - `command -v docker` / `nerdctl` / `podman` - all empty - `find / -name docker -type f` - nothing - `/var/run/docker.sock` **is** mounted (`srw-rw---- root root`) So the host socket is bind-mounted, but `ward agent headless` / `warded` shells out to the `docker` CLI to pull the image and run the sibling, and that client binary is not installed in the explore image. Dispatch dies at the `exec: "docker"` lookup, never reaching the socket. ## Why this matters This is the **core promise** of explore mode. `docs/agent-explore.md` and the composed read-only block both state that the docker socket is mounted "so `warded #N` works from inside" and that "Dispatching commissioned work is the **point** of explore." With no docker client in the image, the entire file-and-dispatch loop is dead in the water: an explore session can file issues but cannot commission any of them. ## Distinct from ward#319 ward#319 is about the dropped non-root agent lacking **group access** to a mounted socket (a `root:root 0600` socket with no usable group). That presupposes a docker client exists and is reaching the socket. This is upstream of that: there is no client binary to invoke in the first place, so dispatch fails identically whether the socket is reachable or not. ward#319's root-socat-bridge fix does not address a missing client. ## Fix direction The explore (and sandbox, if it shares the image) container image needs a docker **client** on PATH - the CLI binary only, talking to the mounted socket, not a full daemon. Confirm whether `ward agent sandbox` hits the same wall (it dispatches too), and whether the `--with-repo`/sibling path in headless mode is meant to use the docker CLI or could use an API client against the socket directly. Add a `--print`-time or bring-up-time preflight that fails loudly ("explore can file but cannot dispatch: no docker client in image") instead of only surfacing at dispatch. ## Blocks ward#320 (explore doctrine sharpening) was filed from this same session but **could not be dispatched** because of this. Any explore-commissioned work is blocked on this until the explore image carries a docker client. Filed from the same `warded explore` session on agentic-os that hit the failure.
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-ward-321 on host kais-macbook-pro-2.local is carrying this issue (reserved 2026-07-01T04:37:45Z). 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-ward-321` on host `kais-macbook-pro-2.local` is carrying this issue (reserved 2026-07-01T04:37:45Z). 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 - bring-up preflight fails loudly when an in-container dispatch has no docker client, instead of dying at the raw exec: "docker" lookup.

Honest retrospective: the biggest surprise was that the issue's framing had partly aged out from under it. Filed June 25, it asks to put a docker client in the explore image, but since then explore folded into the director surface (ward#353) and in-container dispatch moved to a TCP host broker (ward#391) - so the intended path no longer shells docker in the container at all, and the Dockerfile that would carry a client lives in agentic-os, not here. That left the genuinely ward-side, still-live piece: the preflight the issue explicitly asked for. Any dispatch that misses the broker path (no broker addr, WARD_READONLY unset, ref won't resolve) still falls through to the docker shell-out and used to die at the bare exec error, so launchAgentContainer now catches that early with a named, broker-aware reason.

What fought back was small stuff: every doc in this repo is pinned just under the 4000-char cap, so I couldn't add a doc note without splitting a file - reverted it and leaned on the descriptive error string plus the code comment. I also hit a pre-existing lint debt (~33 golangci findings on main) and an unformatted kdl guardfile, both untouched by me and confirmed present on baseline.

Confidence is high on the logic itself - the decision is a small pure state machine with unit coverage - and moderate on whether this fully honours the issue's spirit, since "should the surface image carry a docker client as a fallback" is really an agentic-os call. Worth a follow-up: confirm the sandbox dispatch path, and decide whether the broker is now the sole sanctioned in-container dispatch route (which would make the docker-shell fall-through dead code worth removing) rather than a path we keep hardening.

WARD-OUTCOME: done - bring-up preflight fails loudly when an in-container dispatch has no docker client, instead of dying at the raw `exec: "docker"` lookup. Honest retrospective: the biggest surprise was that the issue's framing had partly aged out from under it. Filed June 25, it asks to put a docker client in the explore image, but since then `explore` folded into the director surface (ward#353) and in-container dispatch moved to a TCP host broker (ward#391) - so the intended path no longer shells `docker` in the container at all, and the Dockerfile that would carry a client lives in agentic-os, not here. That left the genuinely ward-side, still-live piece: the preflight the issue explicitly asked for. Any dispatch that misses the broker path (no broker addr, WARD_READONLY unset, ref won't resolve) still falls through to the docker shell-out and used to die at the bare `exec` error, so `launchAgentContainer` now catches that early with a named, broker-aware reason. What fought back was small stuff: every doc in this repo is pinned just under the 4000-char cap, so I couldn't add a doc note without splitting a file - reverted it and leaned on the descriptive error string plus the code comment. I also hit a pre-existing lint debt (~33 golangci findings on main) and an unformatted kdl guardfile, both untouched by me and confirmed present on baseline. Confidence is high on the logic itself - the decision is a small pure state machine with unit coverage - and moderate on whether this fully honours the issue's spirit, since "should the surface image carry a docker client as a fallback" is really an agentic-os call. Worth a follow-up: confirm the sandbox dispatch path, and decide whether the broker is now the sole sanctioned in-container dispatch route (which would make the docker-shell fall-through dead code worth removing) rather than a path we keep hardening.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/ward#321
No description provided.