# Review: Issue #17 follow-ups @ 65b78b6 - Role: Reviewer (grok) - Identity: loop `loop-20261007T224638Z-cf1efb`, snapshot `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261007T224638Z-cf1efb/2`, base `56d0bfda54e4f4fe1ccc138cc262edfb14cfb4a3` (main still `56d0bfd`), head `65b78b6ec44e44904afe34915563522d4631767f`, tree `b805d9f9e6639c7359115ea9336eac95d7ab85bc`. `8241bd5` is an ancestor. Snapshot porcelain empty. `git diff --check 56d0bfd..HEAD` clean. - Delta since the PASS at `8241bd5`: `541c3fe` and `65b78b6`, five files, +80/−16. Cumulative base..head: nine files, +267/−33. No version bump and no `VERSIONS.md` edit. - Verified read-only at the snapshot. Python 3.9.6 from the follow-up worktree `.venv`, bash 3.2.57, herdr 0.9.3. Inherited `HERDR_*`, `MAM_*`, `MQTT_*`, and `WORKSPACE_ROOT` were unset. `HOME` and `WORKSPACE_ROOT` were the basetemp. `.mam.env` was not exported. `started` for this dispatch is `dispatch_verified` with attempt-id 1. ## A failed yes/no decision exits 1 `wrapper.py` `_yes_no` (`:335-339`) prints `yes` or `no` and returns 0 when `--print` is set. Without `--print` it returns 0 for yes and 1 for no. `subscriber-up` and `stall-watch` both take `--print` (`:301-309`) and return through `_yes_no` (`:346-353`). The module head (`:15-17`) says the wrapper uses `--print`, so a failure to decide cannot pass for no. `_decided_yes` (`multi-agent-mux-delegate-job:123-131`) calls `_decided` with `--print`. `yes` returns 0, `no` returns 1, and any other text calls `_decision_failed` (`:106-109`), which `exit 1`. `_decided` (`:112-118`) already exits 1 when the command status is non-zero or stdout is empty, before that case. The handshake uses `if _decided_yes subscriber-up "$sub_exit"` at `:396` and `:568`. `start_stall_watch` (`:730`) is `_decided_yes stall-watch "$session" "$agent" || return 0`. A real `no` still returns 1, so the watcher is skipped and the subscriber "died early" branch still runs. A failed decision executes `exit 1` inside `_decision_failed`, so that `|| return 0` and the `if` leave the wrapper at 1. `test_f_a_failed_yes_no_decision_ends_the_submit_with_1` (`tests/test_issue17_item4_decisions.py:693-699`) covers modes error, gone, and empty for subscriber-up on direct and loop submits, and for stall-watch on a direct submit. The stand-in subscriber exits 0 before `SUBSCRIBED`. Each run expects rc 1 and `could not decide ''`. `test_f_yes_and_no_keep_their_branches` (`:702-710`) keeps the live branches. `SUB_RC` 0 stays on the yes path: "subscriber died early" is absent, and a direct submit exits 0. `SUB_RC` 1 takes the died-early path, and a direct submit exits 1. The direct rc 0 also shows that the later `wait "$sub_pid"` (`:437-438`), after the handshake already waited that pid, still finishes the submit on this bash 3.2.57. `test_u_yes_no_print` (`:713-721`) checks `--print` stdout `yes`/`no` with rc 0, the rc form of subscriber-up still 0 or 1, stall-watch `herdr:c`/`claude` printing yes, and `remote:r`/`claude` printing no. `test_s_no_printed_decision_is_read_raw` (`:734-743`) drops comment lines, then requires `_decided_yes subscriber-up` twice and `_decided_yes stall-watch` once, and rejects a raw `_decide subscriber-up` or `_decide stall-watch`. The comment at `:725` still names `_decide stall-watch`. The call that runs is `:730`. ## Frozen literals `541c3fe` points `tests/test_issue5_phase1_delegation.py:390` and `tests/test_tier1_unit.py:847` at `_decided job_status sub-status "$sub_rc"`. The issue5 pin also requires `_exit_for "$job_status"`. `EXIT_CODES` and `sub_status()` stay asserted in Python (`test_issue5_phase1_delegation.py:393-395`). ## Items already passed at 8241bd5 The herdr 0.9.3 placeholder close, the N1 docstring, the N3 PATH comment, and the printed-decision `_decided` / `_exit_for` path are absent from `git diff 8241bd5 HEAD`. That review recorded 305 passed (`test_issue17_item4_decisions.py`, `test_issue33_budget.py`, `test_issue45_restore_unit.py`) and 5 passed (`tests/test_issue45_restore.py`). After this review, `~/.config/herdr/config.toml` mtime is still `1784456394` and the directory mtime is still `1791382452`. ## Tests this round `--basetemp=/tmp/518efollow.hbS1ua`. `tests/test_issue17_item4_decisions.py`, `tests/test_issue33_budget.py`, `tests/test_issue5_phase1_delegation.py`, `tests/test_tier1_unit.py`: **442 passed** in 472.25 s. Snapshot porcelain stayed empty. Main stayed `56d0bfd`. [VERDICT: PASS]