v4.8.7 (PATCH). Fixes left by the PR #51 review, plus one gap herdr 0.9.3 exposed in #45 B. The normal path's exit codes, output, registry writes and events are unchanged.
Changes
A decision the wrapper cannot make ends it with 1 (multi-agent-mux-delegate-job, N2). exit "$(_decide exit-code …)" became exit "" (255 on bash 3.2) when wrapper.py or $PY was broken; the other printed decisions failed in their own ways. Printed decisions now go through _decided, exits through _exit_for, failures through _decision_failed (one ERROR: line, exit 1).
Yes/no decisions are printed (review codex R1). subscriber-up and stall-watch answered by rc only, so a call that could not run read as "no" (a subscriber that ended 0 made the submit end 0). They now take --print (yes/no, rc 0); anything else ends the wrapper with 1. Without --print the rc contract is unchanged.
resume closes herdr 0.9.3's placeholder (lib_py/herdr_restore.py). herdr 0.9.3 restores a pane whose directory is gone as a placeholder (restore_error, no process, pane process-info → pane_not_found); close_restored_shells raised on it and left the removed worktree's pane. Only restore_error AND pane_not_found counts as "nothing running"; any other failure still exits 1.
Test premise (tests/test_issue45_restore.py): the restored cwd of a removed directory is $HOME on herdr 0.9.1 and the original path on 0.9.3.
Docs: wrapper.py header (stdlib-only module, but -m loads the package and paho; N1); lib_int.sh uses python3 from PATH (N3).
Verification
Review loop in the merge workspace, all four reviewers: round 1 (@8241bd5) claude/grok PASS, agy NOT PASS (two static wrapper-literal tests), codex NOT PASS (R1 above); fixed in 541c3fe, 65b78b6. Round 2 (@65b78b6) 4/4 PASS. Reports in .agents/reports/<session>/.
Full suite at 65b78b6 (macOS, Python 3.9, herdr 0.9.3): 1730 passed / 0 failed / 56 skipped; 18 skips were the uninitialized nats-docker submodule, and with it tests/test_deploy_freshness.py is 31 passed. The herdr 0.9.3 failure noted in v4.8.6 now passes.
Refs #17 (follow-ups to item 4 / PR #51; #17 stays open)
v4.8.7 (PATCH). Fixes left by the PR #51 review, plus one gap herdr 0.9.3 exposed in #45 B. The normal path's exit codes, output, registry writes and events are unchanged.
## Changes
- **A decision the wrapper cannot make ends it with 1** (`multi-agent-mux-delegate-job`, N2). `exit "$(_decide exit-code …)"` became `exit ""` (255 on bash 3.2) when `wrapper.py` or `$PY` was broken; the other printed decisions failed in their own ways. Printed decisions now go through `_decided`, exits through `_exit_for`, failures through `_decision_failed` (one `ERROR:` line, exit 1).
- **Yes/no decisions are printed** (review codex R1). `subscriber-up` and `stall-watch` answered by rc only, so a call that could not run read as "no" (a subscriber that ended 0 made the submit end 0). They now take `--print` (`yes`/`no`, rc 0); anything else ends the wrapper with 1. Without `--print` the rc contract is unchanged.
- **resume closes herdr 0.9.3's placeholder** (`lib_py/herdr_restore.py`). herdr 0.9.3 restores a pane whose directory is gone as a placeholder (`restore_error`, no process, `pane process-info` → `pane_not_found`); `close_restored_shells` raised on it and left the removed worktree's pane. Only `restore_error` AND `pane_not_found` counts as "nothing running"; any other failure still exits 1.
- **Test premise** (`tests/test_issue45_restore.py`): the restored cwd of a removed directory is `$HOME` on herdr 0.9.1 and the original path on 0.9.3.
- **Docs**: `wrapper.py` header (stdlib-only module, but `-m` loads the package and paho; N1); `lib_int.sh` uses `python3` from PATH (N3).
## Verification
- Review loop in the merge workspace, all four reviewers: round 1 (@8241bd5) claude/grok PASS, agy NOT PASS (two static wrapper-literal tests), codex NOT PASS (R1 above); fixed in 541c3fe, 65b78b6. Round 2 (@65b78b6) **4/4 PASS**. Reports in `.agents/reports/<session>/`.
- Full suite at 65b78b6 (macOS, Python 3.9, herdr 0.9.3): **1730 passed / 0 failed / 56 skipped**; 18 skips were the uninitialized nats-docker submodule, and with it `tests/test_deploy_freshness.py` is 31 passed. The herdr 0.9.3 failure noted in v4.8.6 now passes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
test_b_only_the_resumed_sessions_restored_shell_is_closed asserted that a
pane whose directory was removed comes back in $HOME. That is herdr 0.9.1;
herdr 0.9.3 keeps the old path. The cwd check now takes either, with a note
on which version does what; closing never looked at the cwd.
herdr_restore.py's docstring said the same "$HOME when that directory is
gone" and now names both versions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
With the cwd premise fixed, test_b still failed on herdr 0.9.3: a pane whose
saved directory is gone is restored as a placeholder - `restore_error`
"Saved directory is unavailable...", no process - and `pane process-info`
answers pane_not_found. close_restored_shells raised on it and exited 1
before looking at the other panes, so the resumed session's removed-worktree
pane (the case #45 closes on herdr 0.9.1, where it is a shell in $HOME) was
left behind.
Such a pane has nothing running, so it now meets condition 4. Only that
exact answer counts - restore_error set AND pane_not_found; any other
process-info failure still stops the run with exit 1 as before.
Tests: test_issue45_restore.py passes on herdr 0.9.3 (5/5). Unit tests with a
stub herdr: the placeholder is closed (fails at 56d0bfd); pane_not_found
without restore_error, or restore_error with another error, still raises.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
`exit "$(_decide exit-code ...)"` became `exit ""` when wrapper.py could
not answer (its module or $PY broken): rc 255 on bash 3.2. The other printed
decisions failed open in their own ways: a failed wait-rc returned "" (255),
a failed finalize-begin left finalize-end with an empty --final (argparse
rc 2), and a failed sub-status or finalize-end ended under set -e with the
interpreter's own code (1 for a traceback, 127 for one that cannot run).
Printed decisions now go through _decided <var> <subcommand>, and the
exits through _exit_for <status>: a failed call, an empty answer or a
non-numeric exit code / wait rc prints one ERROR line and exits 1. finalize-
end may legitimately answer nothing (its wrapper gone), so only its rc is
checked. The normal path is unchanged: same answers, exit codes and output.
Left as they are: subscriber-up and stall-watch are answered by rc, so a
failure reads as "no" - the subscriber is then treated as dead (the error
path, whose sub-status/exit-code are now fail-closed) and the notify-only
stall watcher is not started.
Tests (F): the real submit (human job, unreachable broker, 3 s budget) with
a $PY that fails one decision - exit-code, sub-status, finalize-begin,
finalize-end; traceback, rc 127 or no answer - ends with 1 and the ERROR
line; the same run without a failure still ends with 3. cmd_wait with a
failing wait-rc ends with 1. A static guard keeps printed decisions off
raw $(...). At 56d0bfd: exit-code 255/255, finalize-begin 2/2, wait-rc
255/255, sub-status and finalize-end 127 for "gone"; the traceback and
no-answer cases there already ended with 1 (by set -e or by the empty
status mapping to 1) and fail only on the missing ERROR line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
"Stdlib only" is true of the module, not of how the wrapper runs it:
`python -m lib_py.delegation.wrapper` imports lib_py/delegation/__init__.py
first, which loads the service and paho-mqtt. The docstring now says so; the
wrapper's $PY already needs paho. The run path is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
mam_int / mam_int_knob call `python3 -I` because the file is also sourced
without lib.sh and so without $PY. The header now says so, and that without
a python3 on PATH they print nothing (rc 127). The code is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
Sourcing the wrapper sources lib.sh, which writes $WORKSPACE_ROOT/.mam/shim/
herdr; without WORKSPACE_ROOT that is the checkout. The new N2 wait-rc test
now runs with its tmp dir as WORKSPACE_ROOT, so the N2 tests leave the
checkout untouched. (The existing C tests in this file still write the
checkout's .mam/shim/herdr, as at 56d0bfd - not changed here.)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
a670ef5 routed the wrapper's sub-status and exit-code decisions through
_decided / _exit_for, but test_tier1_unit G-10 and test_issue5's
test_wrapper_exit_code_mapping_literals_are_frozen still looked for the raw
`_decide sub-status "$sub_rc"` and `_decide exit-code "$job_status"`. They now
pin `_decided job_status sub-status "$sub_rc"` and `_exit_for "$job_status"`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
subscriber-up and stall-watch answered only by exit code, so a call that
could not run (module error rc 1, missing interpreter 127) read as "no":
with a subscriber that had ended 0, the error path mapped 0 to completed
and the submit ended with 0; a failed stall-watch silently skipped the
watcher.
Both now take --print: the answer is printed as yes/no and the exit code
is 0 for either. The wrapper asks with --print through _decided_yes, so a
nonzero exit, no answer or anything else is _decision_failed (ERROR line,
exit 1). Without --print the rc contract is unchanged (yes 0, no 1).
Tests: subscriber-up (direct and loop/discuss call sites) and stall-watch,
each failing by traceback, rc 127 or no answer with a subscriber that
ended 0, end the submit with 1 and the ERROR line - at 8241bd5 eight of the
nine ended with 0 (the loop/no-answer one ended with 1 through the loop's
own result and failed only on the ERROR line); yes and no keep their
branches in both paths; --print unit table; the static guard forbids an
rc-read yes/no decision.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
Merge-workspace review loop (--all-reviewer): round 1 (@8241bd5) claude
grok PASS, agy NOT PASS (two static wrapper-literal tests did not follow
_decided/_exit_for), codex NOT PASS (R1: a yes/no decision that could not
run read as "no"); fixed in 541c3fe and 65b78b6. Round 2 (@65b78b6) 4/4
PASS. Full suite at 65b78b6 on macOS / Python 3.9 / herdr 0.9.3: 1730
passed / 0 failed / 56 skipped; 18 of the skips were the uninitialized
nats-docker submodule in the fresh worktree, and with it
tests/test_deploy_freshness.py is 31 passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
Godopu
merged commit 6b00b6e06f into main2026-10-08 02:32:57 +00:00
리뷰: 1회차(@8241bd5) claude·grok PASS, agy NOT PASS(정적 테스트 2건이 _decided/_exit_for 를 못 따라감), codex NOT PASS(R1: 실행 못 한 예/아니오 판정이 "아니오"로 읽혀 제출이 0 으로 끝남). 541c3fe·65b78b6 에서 수정, 2회차(@65b78b6) 4/4 PASS.
전체 스위트(65b78b6, macOS·Python 3.9·herdr 0.9.3): 1730 passed / 0 failed / 56 skipped. skip 중 18 건은 새 워크트리의 nats-docker 서브모듈 미초기화 — 초기화 후 test_deploy_freshness.py 31 passed. v4.8.6 때의 herdr 0.9.3 실패도 통과.
creator 가 계획보다 하나 더 고쳤습니다: herdr 0.9.3 은 디렉터리가 사라진 pane 을 자리표시(restore_error, pane_not_found)로 복원하고, resume 의 빈 셸 닫기가 거기서 예외로 멈춰 #45 B 가 실제로 깨져 있었습니다(f19f940).
동작 변화 하나: stall-watch 판정 호출 자체가 실패하면 예전엔 감시만 조용히 껐지만 이제 래퍼가 1 로 끝납니다(정상의 "아니오"는 그대로 감시 안 함).
후속(비차단): tests/test_issue17_item4_decisions.py 의 _env 가 상속된 WORKSPACE_ROOT 를 비우지 않음.
병합했습니다 (6b00b6e, v4.8.7).
- 리뷰: 1회차(@8241bd5) claude·grok PASS, agy NOT PASS(정적 테스트 2건이 `_decided`/`_exit_for` 를 못 따라감), codex NOT PASS(R1: 실행 못 한 예/아니오 판정이 "아니오"로 읽혀 제출이 0 으로 끝남). 541c3fe·65b78b6 에서 수정, 2회차(@65b78b6) 4/4 PASS.
- 전체 스위트(65b78b6, macOS·Python 3.9·herdr 0.9.3): 1730 passed / 0 failed / 56 skipped. skip 중 18 건은 새 워크트리의 nats-docker 서브모듈 미초기화 — 초기화 후 test_deploy_freshness.py 31 passed. v4.8.6 때의 herdr 0.9.3 실패도 통과.
- creator 가 계획보다 하나 더 고쳤습니다: herdr 0.9.3 은 디렉터리가 사라진 pane 을 자리표시(`restore_error`, `pane_not_found`)로 복원하고, resume 의 빈 셸 닫기가 거기서 예외로 멈춰 #45 B 가 실제로 깨져 있었습니다(f19f940).
- 동작 변화 하나: stall-watch 판정 호출 자체가 실패하면 예전엔 감시만 조용히 껐지만 이제 래퍼가 1 로 끝납니다(정상의 "아니오"는 그대로 감시 안 함).
- 후속(비차단): `tests/test_issue17_item4_decisions.py` 의 `_env` 가 상속된 WORKSPACE_ROOT 를 비우지 않음.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Refs #17 (follow-ups to item 4 / PR #51; #17 stays open)
v4.8.7 (PATCH). Fixes left by the PR #51 review, plus one gap herdr 0.9.3 exposed in #45 B. The normal path's exit codes, output, registry writes and events are unchanged.
Changes
multi-agent-mux-delegate-job, N2).exit "$(_decide exit-code …)"becameexit ""(255 on bash 3.2) whenwrapper.pyor$PYwas broken; the other printed decisions failed in their own ways. Printed decisions now go through_decided, exits through_exit_for, failures through_decision_failed(oneERROR:line, exit 1).subscriber-upandstall-watchanswered by rc only, so a call that could not run read as "no" (a subscriber that ended 0 made the submit end 0). They now take--print(yes/no, rc 0); anything else ends the wrapper with 1. Without--printthe rc contract is unchanged.lib_py/herdr_restore.py). herdr 0.9.3 restores a pane whose directory is gone as a placeholder (restore_error, no process,pane process-info→pane_not_found);close_restored_shellsraised on it and left the removed worktree's pane. Onlyrestore_errorANDpane_not_foundcounts as "nothing running"; any other failure still exits 1.tests/test_issue45_restore.py): the restored cwd of a removed directory is$HOMEon herdr 0.9.1 and the original path on 0.9.3.wrapper.pyheader (stdlib-only module, but-mloads the package and paho; N1);lib_int.shusespython3from PATH (N3).Verification
541c3fe,65b78b6. Round 2 (@65b78b6) 4/4 PASS. Reports in.agents/reports/<session>/.65b78b6(macOS, Python 3.9, herdr 0.9.3): 1730 passed / 0 failed / 56 skipped; 18 skips were the uninitialized nats-docker submodule, and with ittests/test_deploy_freshness.pyis 31 passed. The herdr 0.9.3 failure noted in v4.8.6 now passes.🤖 Generated with Claude Code
https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
병합했습니다 (
6b00b6e, v4.8.7)._decided/_exit_for를 못 따라감), codex NOT PASS(R1: 실행 못 한 예/아니오 판정이 "아니오"로 읽혀 제출이 0 으로 끝남). 541c3fe·65b78b6 에서 수정, 2회차(@65b78b6) 4/4 PASS.65b78b6, macOS·Python 3.9·herdr 0.9.3): 1730 passed / 0 failed / 56 skipped. skip 중 18 건은 새 워크트리의 nats-docker 서브모듈 미초기화 — 초기화 후 test_deploy_freshness.py 31 passed. v4.8.6 때의 herdr 0.9.3 실패도 통과.restore_error,pane_not_found)로 복원하고, resume 의 빈 셸 닫기가 거기서 예외로 멈춰 #45 B 가 실제로 깨져 있었습니다(f19f940).tests/test_issue17_item4_decisions.py의_env가 상속된 WORKSPACE_ROOT 를 비우지 않음.