advisor ref-mode returns docker exit 125 through the dispatch broker (TTY/attach in non-interactive host-forward context) #606

Closed
opened 2026-07-04 09:12:00 +00:00 by coilysiren · 2 comments
Owner

Symptom

ward agent advisor <owner/repo#N> "<prompt>" in ref mode, launched through the dispatch broker from a warded director (read-only) surface, fails every time with:

ward: dispatch broker: ward agent advisor --driver claude: research coilyco-flight-deck/agentic-os#<N>: exit status 125

Docker exit 125 is a pre-start container-create/run rejection (the daemon refused the container before the entrypoint ran), classically "the input device is not a TTY" when a docker run -it/attach is issued in a non-interactive context.

Reproduced 2026-07-04

Firing 16 advisor ref-mode runs across the aos backlog from a warded director surface session (broker at $WARD_DISPATCH_BROKER_ADDR), every one returned exit status 125. Constant across issues (#298 #291 #282 #268 #237 #235 #231 #228 #225 #300 #269 #252 ...). Not issue-specific.

What works vs what fails (the tell)

  • engineer ref runs from the same session, same broker, same moment: all forwarded and detached cleanly (ward dispatch broker: forwarded ... to host ward). The detached-launch path is healthy.
  • advisor --print (renders plan, launches nothing): forwards fine. So ref resolution + prompt render + broker forward are all healthy.
  • advisor real run: 125 at container launch, every time.
  • --no-tailnet does not help (rules out the live-observe tailnet/~/.aws mount as the cause).

The discriminator: engineer forwards-and-detaches (host ward launches the container natively, later, no TTY expectation). Advisor ref-mode runs its research container synchronously through the broker and waits on it, and that synchronous launch is what 125s in the non-interactive, TTY-less host-forward context.

Likely cause

The advisor research container is started with an interactive/TTY attach (-it or equivalent) even in ref mode. Ref mode is documented as "researches the issue and posts the answer as a comment" - non-interactive by nature - and the help even advertises a no-TTY one-shot (--oneshot) for the freeform path. Ref mode should launch detached / non-TTY the same way the engineer surface does, or at minimum drop -t when no terminal is attached ([ -t 0 ]).

Impact

The advisor surface is unusable through the dispatch broker from a director surface - exactly the seat the doctrine steers design/research work toward ("prefer a sibling dispatch ... warded advisor #N to design or research"). Every consult/design issue routed to advisor from a director session silently fails to launch, so the research never happens and no comment is posted. 16 intended advisor dispatches were lost to this in one session.

Fix direction

  • In the advisor ref-mode launch path (ward cmd/ward/ agent/advisor container assembly), do not request a TTY/attach when stdin/stdout is not a terminal - gate the -t/-i on [ -t 0 ], or make ref mode forward-and-detach like the engineer path and post its comment on completion.
  • Confirm against the host-side dispatch log (C:\Users\firem\.ward\agent-logs\dispatch\*-advisor-*.log) which docker invocation returns 125; that log carries the exact docker: Error response from daemon / the input device is not a TTY line this container cannot see.

Repro one-liner

ward agent advisor coilyco-flight-deck/agentic-os#269 "research this and comment" --no-tailnet
# -> ward: dispatch broker: ... research ...#269: exit status 125

Filed from a warded director read-only surface (capture-and-dispatch).

## Symptom `ward agent advisor <owner/repo#N> "<prompt>"` in **ref mode**, launched through the dispatch broker from a warded director (read-only) surface, fails **every time** with: ``` ward: dispatch broker: ward agent advisor --driver claude: research coilyco-flight-deck/agentic-os#<N>: exit status 125 ``` Docker exit **125** is a pre-start container-create/run rejection (the daemon refused the container before the entrypoint ran), classically "the input device is not a TTY" when a `docker run -it`/attach is issued in a non-interactive context. ## Reproduced 2026-07-04 Firing 16 advisor ref-mode runs across the aos backlog from a `warded director` surface session (broker at `$WARD_DISPATCH_BROKER_ADDR`), every one returned `exit status 125`. Constant across issues (#298 #291 #282 #268 #237 #235 #231 #228 #225 #300 #269 #252 ...). Not issue-specific. ## What works vs what fails (the tell) - **engineer** ref runs from the same session, same broker, same moment: **all forwarded and detached cleanly** (`ward dispatch broker: forwarded ... to host ward`). The detached-launch path is healthy. - **advisor `--print`** (renders plan, launches nothing): **forwards fine**. So ref resolution + prompt render + broker forward are all healthy. - **advisor real run**: **125 at container launch**, every time. - `--no-tailnet` does **not** help (rules out the live-observe tailnet/`~/.aws` mount as the cause). The discriminator: engineer **forwards-and-detaches** (host ward launches the container natively, later, no TTY expectation). Advisor ref-mode runs its research container **synchronously** through the broker and waits on it, and that synchronous launch is what 125s in the non-interactive, TTY-less host-forward context. ## Likely cause The advisor research container is started with an interactive/TTY attach (`-it` or equivalent) even in ref mode. Ref mode is documented as "researches the issue and posts the answer as a comment" - non-interactive by nature - and the help even advertises a no-TTY one-shot (`--oneshot`) for the freeform path. Ref mode should launch **detached / non-TTY** the same way the engineer surface does, or at minimum drop `-t` when no terminal is attached (`[ -t 0 ]`). ## Impact The advisor surface is unusable through the dispatch broker from a director surface - exactly the seat the doctrine steers design/research work toward ("prefer a sibling dispatch ... `warded advisor #N` to design or research"). Every consult/design issue routed to advisor from a director session silently fails to launch, so the research never happens and no comment is posted. 16 intended advisor dispatches were lost to this in one session. ## Fix direction - In the advisor ref-mode launch path (ward `cmd/ward/` agent/advisor container assembly), do not request a TTY/attach when stdin/stdout is not a terminal - gate the `-t`/`-i` on `[ -t 0 ]`, or make ref mode forward-and-detach like the engineer path and post its comment on completion. - Confirm against the host-side dispatch log (`C:\Users\firem\.ward\agent-logs\dispatch\*-advisor-*.log`) which docker invocation returns 125; that log carries the exact `docker: Error response from daemon` / `the input device is not a TTY` line this container cannot see. ## Repro one-liner ``` ward agent advisor coilyco-flight-deck/agentic-os#269 "research this and comment" --no-tailnet # -> ward: dispatch broker: ... research ...#269: exit status 125 ``` Filed from a warded director read-only surface (capture-and-dispatch).
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-ward-606 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-04T09:12:10Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-ward-606` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-04T09:12:10Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - advisor ref-mode research now captures via a plain foreground docker run (no stdin attach), so a broker forward from a director surface no longer hits docker exit 125.

The diagnosis took longer than the fix. The plan already set TTY=false, so -it wasn't the culprit the way the title first reads - the real offender was the lone -i. Tracing it end to end (broker goroutine -> in-process agentAdvisorCommand().Run -> newRunner() capturing os.Stdin -> Capture wiring that stdin into docker run -i) is what made it click: in the surface's host process os.Stdin is the operator's TTY, already hijacked by the foreground surface docker run -it, and docker rejects the second attach pre-start. The redirectStdioToLog swap only moves stdout/stderr, never stdin, which is why the freeform/engineer paths dodge it.

The fix is small and, I think, the right shape: a one-shot claude -p reads no stdin, so it never needed -i at all - -i was there on the mistaken belief it was required for capturable stdout, when a plain foreground docker run streams stdout fine. Added an upPlan.Capture mode that emits none of -d/-i/-t. Fairly confident: build, vet, full tests, and the pre-commit suite are green, and the existing TestAdvisorResearchPlan was tightened to forbid all three flags.

Rough edges / follow-ups worth noting: I couldn't reproduce the live 125 from inside this container (no host broker + TTY-owning surface here), so the confirmation is by construction and reasoning, not an observed green run - worth a real director-surface smoke test. And the freeform advisor --oneshot path under no-TTY still uses -i; it isn't broker-dispatchable so it's out of scope here, but if it ever becomes forwardable it'd want the same Capture treatment. Forgejo Actions also looked idle in this env (task list frozen weeks back), so I verified the landing by confirming origin/main advanced to the fix commit rather than by a CI run.

WARD-OUTCOME: done - advisor ref-mode research now captures via a plain foreground `docker run` (no stdin attach), so a broker forward from a director surface no longer hits docker exit 125. The diagnosis took longer than the fix. The plan already set `TTY=false`, so `-it` wasn't the culprit the way the title first reads - the real offender was the lone `-i`. Tracing it end to end (broker goroutine -> in-process `agentAdvisorCommand().Run` -> `newRunner()` capturing `os.Stdin` -> `Capture` wiring that stdin into `docker run -i`) is what made it click: in the surface's host process `os.Stdin` is the operator's TTY, already hijacked by the foreground surface `docker run -it`, and docker rejects the second attach pre-start. The `redirectStdioToLog` swap only moves stdout/stderr, never stdin, which is why the freeform/engineer paths dodge it. The fix is small and, I think, the right shape: a one-shot `claude -p` reads no stdin, so it never needed `-i` at all - `-i` was there on the mistaken belief it was required for capturable stdout, when a plain foreground `docker run` streams stdout fine. Added an `upPlan.Capture` mode that emits none of `-d/-i/-t`. Fairly confident: build, vet, full tests, and the pre-commit suite are green, and the existing `TestAdvisorResearchPlan` was tightened to forbid all three flags. Rough edges / follow-ups worth noting: I couldn't reproduce the live 125 from inside this container (no host broker + TTY-owning surface here), so the confirmation is by construction and reasoning, not an observed green run - worth a real director-surface smoke test. And the freeform `advisor --oneshot` path under no-TTY still uses `-i`; it isn't broker-dispatchable so it's out of scope here, but if it ever becomes forwardable it'd want the same `Capture` treatment. Forgejo Actions also looked idle in this env (task list frozen weeks back), so I verified the landing by confirming origin/main advanced to the fix commit rather than by a CI run.
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#606
No description provided.