Summary
Cards created with --goal never exercise the Ralph-style goal loop. The dispatcher sets HERMES_KANBAN_GOAL_MODE=1 in the worker env, but the spawned worker command (hermes -p <profile> chat -q "work kanban task <id>") takes the single-query branch of main(), which never reads that env var. The goal hook is only wired in the fully-quiet -Q/--quiet machine-readable branch. Result: goal_mode is stored on the card and silently ignored; any apparent progress comes from the dispatcher's ordinary retry loop, and a worker that leaves the task open (expecting the judge to continue it) is reaped as protocol_violation.
Where the wires cross (0.16.0 line numbers; same structure in 0.18.2)
hermes_cli/kanban_db.py:6673-6676 (_default_spawn) — sets HERMES_KANBAN_GOAL_MODE=1 and spawns [hermes, -p, <profile>, --accept-hooks, chat, -q, <prompt>]. The comment says "the worker reads these and wraps its run in the Ralph-style /goal judge loop (see cli.py quiet-mode path)" — but the spawned form never reaches that path.
cli.py:16010-16014 — the only reader of HERMES_KANBAN_GOAL_MODE, calling _run_kanban_goal_loop_q(...). It lives in the -Q/--quiet machine-readable branch (the same block that owns the kanban EX_TEMPFAIL exit-code contract).
cli.py:16048-16070 — the else branch, explicitly documented "Single-query mode (hermes chat -q "…")": cli.chat(query) + _print_exit_summary(). No goal hook.
Evidence from a live board
Worker logs of a goal_mode card end with the _print_exit_summary() block ("Resume this session with… / Duration / Messages") and never contain the session_id: stderr line of the quiet branch — although _default_spawn merges stderr into the log (stderr=subprocess.STDOUT, kanban_db.py:6768). Three consecutive runs of one goal card each produced exactly one turn in a fresh session, no judge verdict, no continuation; goals.run_kanban_goal_loop itself works when invoked directly (unit-style, mocked cli).
Still present in 0.18.2 (latest)
- spawn unchanged:
kanban_db.py:7794-7796 still ["chat", "-q", prompt]
- hook unchanged:
cli.py:16113 (quiet branch) vs single-query branch 16152-16171
Suggested fix (either side works)
- Wire the
HERMES_KANBAN_GOAL_MODE check into the single-query branch too (after cli.chat(...) returns the first response), or
- Spawn goal-mode workers through the
-Q/--quiet path the hook already lives in.
Summary
Cards created with
--goalnever exercise the Ralph-style goal loop. The dispatcher setsHERMES_KANBAN_GOAL_MODE=1in the worker env, but the spawned worker command (hermes -p <profile> chat -q "work kanban task <id>") takes the single-query branch ofmain(), which never reads that env var. The goal hook is only wired in the fully-quiet-Q/--quietmachine-readable branch. Result:goal_modeis stored on the card and silently ignored; any apparent progress comes from the dispatcher's ordinary retry loop, and a worker that leaves the task open (expecting the judge to continue it) is reaped asprotocol_violation.Where the wires cross (0.16.0 line numbers; same structure in 0.18.2)
hermes_cli/kanban_db.py:6673-6676(_default_spawn) — setsHERMES_KANBAN_GOAL_MODE=1and spawns[hermes, -p, <profile>, --accept-hooks, chat, -q, <prompt>]. The comment says "the worker reads these and wraps its run in the Ralph-style /goal judge loop (see cli.py quiet-mode path)" — but the spawned form never reaches that path.cli.py:16010-16014— the only reader ofHERMES_KANBAN_GOAL_MODE, calling_run_kanban_goal_loop_q(...). It lives in the-Q/--quietmachine-readable branch (the same block that owns the kanban EX_TEMPFAIL exit-code contract).cli.py:16048-16070— theelsebranch, explicitly documented "Single-query mode (hermes chat -q "…")":cli.chat(query)+_print_exit_summary(). No goal hook.Evidence from a live board
Worker logs of a
goal_modecard end with the_print_exit_summary()block ("Resume this session with… / Duration / Messages") and never contain thesession_id:stderr line of the quiet branch — although_default_spawnmerges stderr into the log (stderr=subprocess.STDOUT, kanban_db.py:6768). Three consecutive runs of one goal card each produced exactly one turn in a fresh session, no judge verdict, no continuation;goals.run_kanban_goal_loopitself works when invoked directly (unit-style, mockedcli).Still present in 0.18.2 (latest)
kanban_db.py:7794-7796still["chat", "-q", prompt]cli.py:16113(quiet branch) vs single-query branch16152-16171Suggested fix (either side works)
HERMES_KANBAN_GOAL_MODEcheck into the single-query branch too (aftercli.chat(...)returns the first response), or-Q/--quietpath the hook already lives in.