You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
conductor run --web-bg aborts at launch when the workflow contains a human_gate (unless --skip-gates), yet the web dashboard has complete gate support: the gate modal, WebSocket gate_response, POST /api/gate-respond, and the conductor gate-respond CLI (#234). Background runs are the mode where web-based gate resolution is most valuable — the guard forces users to choose between the dashboard and gates.
Root cause
The guard (cli/app.py::_abort_web_bg_if_human_gate) papers over a gap in the engine: _handle_gate_with_web (engine/workflow.py) races the terminal prompt against the dashboard unconditionally. With the bg child's stdin=DEVNULL, Prompt.ask raises EOFError instantly, race-wins, cancels the web arm, and crashes the workflow before a dashboard user could respond. Verified by reproducing the race with stdin at /dev/null:
winner: cli_arm
raised: EOFError: EOF when reading a line
Precedent
This is the same failure #198 described for the max-iterations gate, fixed in #202 by _resolve_max_iterations_gate's tiered policy (dashboard + bg/non-TTY → web-only wait, CLI arm deliberately skipped; also racing wait_for_stop() so /api/stop can end the wait). The human-gate path never got that tier — cli_usable = not self._bg_mode and sys.stdin.isatty() already exists one function away.
conductor run --web-bgaborts at launch when the workflow contains ahuman_gate(unless--skip-gates), yet the web dashboard has complete gate support: the gate modal, WebSocketgate_response,POST /api/gate-respond, and theconductor gate-respondCLI (#234). Background runs are the mode where web-based gate resolution is most valuable — the guard forces users to choose between the dashboard and gates.Root cause
The guard (
cli/app.py::_abort_web_bg_if_human_gate) papers over a gap in the engine:_handle_gate_with_web(engine/workflow.py) races the terminal prompt against the dashboard unconditionally. With the bg child'sstdin=DEVNULL,Prompt.askraisesEOFErrorinstantly, race-wins, cancels the web arm, and crashes the workflow before a dashboard user could respond. Verified by reproducing the race with stdin at/dev/null:Precedent
This is the same failure #198 described for the max-iterations gate, fixed in #202 by
_resolve_max_iterations_gate's tiered policy (dashboard + bg/non-TTY → web-only wait, CLI arm deliberately skipped; also racingwait_for_stop()so/api/stopcan end the wait). The human-gate path never got that tier —cli_usable = not self._bg_mode and sys.stdin.isatty()already exists one function away.Proposal
_handle_gate_with_web: when a dashboard is attached andcli_usableis false, wait on the web arm only (racingwait_for_stop()); keep the CLI/web race for foreground TTY._abort_web_bg_if_human_gate, or downgrade it to a launch-time notice pointing at the dashboard URL andconductor gate-respond.--web-bg/human_gatesections indocs/cli-reference.mdand the skill references.Happy to submit a PR for this if the approach sounds right.