PR #48 already carries the 4.8.5 lockstep bump. Merge-workspace review: round 1 (@35935f9) claude agy grok NOT PASS (test portability: tomllib on Python 3.9, /proc and /tmp on macOS), codex no verdict (model at capacity, budget spent); fixed in36f0b8dande8d9151. Round 2 (@e8d9151) codex NOT PASS (unrunnable config check passed an unchecked merge, odd pids closed a pane, nan dropped the user's settings); fixed inab93776. Round 3 (@ab93776) 4/4 PASS. Full suite atab93776on macOS / Python 3.9: 1456 passed / 0 failed / 38 skipped. Reports and the round-3 identity sidecars go to .agents/reports/<session>/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
8.5 KiB
Review: PR #48 (fix/issue-45) @ 35935f9
- Role: Reviewer (grok)
- Identity: loop
loop-20261005T064728Z-a4e9c8, basec5c7eff6f758c76ac37a138dbc3c301f3f7e10cf(main, v4.8.4), head35935f9e8a052ca9e9d9b806a62d17eb72afea78, treedff9db13f3e018528d04a024acda942f501513ff, 10 commits, clean snapshot - Verified read-only at
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261005T064728Z-a4e9c8/1. Main stayedc5c7eff. No product edit, no commit, no push. - Diff vs base: 25 files,
tests/test_issue10_13_purge.pyabsent. Interpreter for the required pytest:pr-48/.venvPython 3.9.6. The shim'spython3is/usr/bin/python3, also 3.9.6. CI (deploy/gitea-ci.yml:94) is Python 3.10.tomllibis stdlib only from 3.11.requirements-dev.txtispytest>=8.0./usr/bin/python3has notomli. The venv hastomliand notomllib.
The required command exits at collection. With --continue-on-collection-errors added only so the rest of the list could run: 5 failed, 192 passed, 1 skipped, 1 error in 254.58s (--basetemp=/tmp/667follow.YrMavO). The first command, as written in the brief, was 1 collection error in 0.22s, exit 2, and ran no test.
What fails
1. The merge tests do not load on the Python this repo runs
tests/test_issue45_herdr_config.py:16 is a top-level import tomllib. Collection raises ModuleNotFoundError. tests/test_issue9_server_env.py:157 imports it inside test_bootstrap_hands_the_server_a_no_restore_config and fails the same way.
lib_py/herdr_config.py:54-57 catches that import and sets tomllib = None. server_config_text then returns MINIMAL plus a warning (:146-147). A throwaway HOME=/tmp/m45probe-min run of /usr/bin/python3 -m lib_py.herdr_config server-config printed that warning and wrote only:
# generated by multi-agent-mux (Issue #45) - do not edit
# source: /tmp/m45probe-min/home/.config/herdr/config.toml
[session]
resume_agents_on_restore = false
The throwaway user file's checksum was unchanged. That is the accepted "no parser, restore still off" path. It is also the path the shim takes on this host and on CI 3.10.
VERSIONS.md:49 and multi-agent-mux-create/SKILL.md:120 describe the generated file as the user's config with that one key forced false. test_issue9_server_env.py:171 asserts the merged document (ui.confirm_close kept). On this python3 the file is MINIMAL, so fixing only the test import still fails :171.
Alternative: in herdr_config.py:54-57 and in both test imports, use tomli when tomllib is missing (tomli.loads / tomli.TOMLDecodeError match). Add tomli to requirements-dev.txt, and install it for the python3 that lib.sh:241 actually runs (/usr/bin/python3 here, not the venv). Keep the MINIMAL warning only when neither library imports. With tomli assigned onto the module, the merge kept inline tables, dotted keys, quoted keys, arrays, and comments semantically, and a broken document or a non-table session fell back to MINIMAL. herdr config check on confirm_close = "no" in a throwaway file exited 1 with parse error and using defaults, and herdr_rejects (:169-180) reported it.
2. The #45 positive control never runs on macOS
tests/test_issue45_restore.py:227-228 reads /proc/<pid>/environ. Both parametrizations of test_a_mam_server_does_not_relaunch_saved_agents raised FileNotFoundError there. The asserts above that line had already passed: the fake claude did not log --resume, the restored pane had no agent, and the foreground pid was the shell. The positive control at :230-236 (plain server must relaunch) did not execute.
Alternative: build the HERDR_CONFIG_PATH=... needle, read /proc/<pid>/environ when that file exists, and otherwise use ps eww -p <pid> -o command= and search the raw output. Run :230-236 even when the environ read fails, so a missing /proc cannot skip the control that shows restore-on still relaunches.
3. Restored cwd is the resolved path
tests/test_issue45_restore.py:263:
AssertionError: assert '/private/tmp/m45swco8dxx' == '/tmp/m45swco8dxx'
mkdtemp(dir="/tmp") returns /tmp/.... On this Mac /tmp is /private/tmp, and herdr's pane cwd is the resolved path. The close calls start at :267, so the selective-close asserts (live agent Z, busy sleep X2, user pane U, close only X and Y) did not run. test_b_resume_leaves_no_restored_shell_behind got create rc 0 and status == running, then StopIteration at :325 on p.get("cwd") == str(ws) with that same unresolved /tmp path.
test_b_default_server_is_never_touched passed: HERDR_SESSION_NAME=default returns before invoking --herdr /nonexistent/herdr (herdr_restore.py:54-55).
Alternative: compare Path(cwd).resolve() with Path(rig.root).resolve() at :263, and Path(p["cwd"]).resolve() == Path(ws).resolve() at :325.
What holds
-
User file bytes are not the output.
output_path(herdr_config.py:192-205) skips the source (includingsamefile) and a file whose marker source is not this source. Both names taken raisesOSError; the fallback directory goes through the same helper (:270-276). Writes aremkstemp+os.replace(:208-216). A generatedHERDR_CONFIG_PATHis followed one hop via the# source:line (:79-87). The trace beside the user config ismam-server-config.toml(ormam-server-config-<sha256(realpath)[:12]>.toml), marker plus# source: <path>. Ananvalue makestomllib.loads(text) == expectedfalse (:159,nan != nan) and falls back toMINIMALwith a warning. The user file stays. -
The only server spawn is the shim heredoc
lib.sh:212-261($REAL_HERDR --session <name> server). create (create_session.sh:27sourceslib.sh,:264_herdr new-session) and resume (resume_session.sh:8,:245) go through that shim. The bootstrap runs only when the name is notdefault,MAM_NO_AUTOSTARTis not1, andsession listsays the server is down (lib.sh:193-212).status.sh:13setsMAM_NO_AUTOSTART=1before sourcing.reconcile.sh:28sets it before herdr calls; the bootstrap is in the shim, not at source time. An already-running server is left as it was.create/SKILL.md:120-121andVERSIONS.md:50say the file is rebuilt the next time MAM starts the server, and a server the user starts withherdr session attachdoes not get it. -
A pane is closed only when the session name is set and not
default, the pane has no live agent,agent_session.valueequals the id being resumed, andshell_pidequalsforeground_process_group_id(herdr_restore.py:54-65). Empty id anddefaultreturn[]before herdr.mainmapsOSError,SubprocessError, andValueErrorto exit 1 (:81-83).resume_session.sh:240-242calls that and on failure only prints a warning; dry-run has already exited at:225-228. Bash does not decide which pane. There is a gap betweenprocess-infoandpane closein which a command can start; a failed query still closes nothing further. -
MAM_VERSIONandFRAMEWORK_VERSIONare4.8.5. The eightSKILL.mdfrontmatters match.tests/data/skill_code_blocks.jsonchanges line numbers and the resume script anchors (:237/:241/:247-250→:245/:249/:255-258). No class, kind, or sha field changes.test_version_consistencyandtest_issue42_skill_blocksare in the 192 passes. S11's base moved to811b080(tests/test_issue23_24_startup_path.py) because the heredoc change is intentional. -
tests/test_issue10_13_purge.pyis not in the diff. Those claimed full-suite failures are not produced by this PR. This review did not run that file. -
New judgment is in
herdr_config.pyandherdr_restore.py. The new bash is "config path empty → warning, start anyway" (lib.sh:243-245) and the resume warning above. The spawn snippet copiesMAM_SERVER_CFGinto the child asHERDR_CONFIG_PATHand dropsMAM_SERVER_CFGfrom the child (lib.sh:253-256).
Isolation
Restore tests use mkdtemp(prefix="m45", dir="/tmp"), session mamt45-<pid>-<uuid4>, and strip HERDR_WORKSPACE_ID, HERDR_PANE_ID, HERDR_TAB_ID, HERDR_SOCKET_PATH, HERDR_SESSION, HERDR_ENV, HERDR_CONFIG_PATH, and XDG_CONFIG_HOME (test_issue45_restore.py:41-42, :160-164). After the run, no mamt45 process remained. ~/.config/herdr/config.toml mtime stayed 1784456394, the directory mtime stayed 1791183576, and no mam-server-config.toml appeared there. The live multi-agent-mux server log grew on its own during the window. Snapshot git status was empty afterward. Throwaway pytest dirs were removed.
[VERDICT: NOT PASS]