Files
multi-agent-mux/.agents/reports/reviewer-grok-01/report-7b602181.md
T

11 KiB
Raw Blame History

Review 7b602181 — PR #70 / Issue #66, Mac pass

PASS. Head bb496fd calls herdr with sanitize_herdr_agent_name of the session. agent get, agent read, agent prompt, and agent wait all use that name. The agent kind is still taken from --agent or from the original session suffix. Log lines keep the original session name. A name that is already legal is left as it is.

Reviewed only the snapshot /var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261025T023502Z-d6f876/1 (loop-20261025T023502Z-d6f876). Head bb496fde785133ad1dbec288aa7431766efb6eb5, tree 15a72c9a1933c64018a2f2d9aa9707348905bb4d. Base 0461a632eb667c7988fd33be92e9a4b36151f22a (main, v4.10.0) is an ancestor and is the control-plane HEAD. One commit, 3 files, +150/−5. No uncommitted changes. git diff --check is clean. Snapshot porcelain is empty and the snapshot has no .mam. Control-plane porcelain is empty. No product file was edited. The source worktree mam-wt-pr70 was not run.

The commit is bb496fd fix(stall_watch): call herdr with the sanitized agent name.

Host tools: control-plane venv Python 3.9.6, /bin/bash 3.2.57(1)-release. The real agent databases were not opened.

Herdr name and kind

HerdrSource keeps the original string on self.target and stores self.name = sanitize_herdr_agent_name(target) (stall_watch.py:158-165). agent_get, agent read, and agent prompt pass self.name (182-184, 206, 210-211). StallWatch.herdr_name is the same function of args.target (229). watcher.wait_once receives that name, and args.herdr_session stays the server (331).

build_argv puts a non-default server on herdr --session and the target on agent wait <target> (loop/watcher.py:131-139). HerdrSource._herdr does the same split (stall_watch.py:167-168). The server argument is not run through the agent-name sanitizer.

resolve_agent still reads --agent, then its first hyphen piece, then agent_of_row on the original session name (stall_watch.py:140-146, registry.py:30-49, base.py:326-330). Measured on this snapshot: resolve_agent("", "mam-wt-issue-66-very-long-creator-codex") is codex. The sanitized form mam-wt-issue-66-very-lo-d55dfa83 does not end in -codex, and resolve_agent of that string with an empty --agent is None. resolve_agent("claude", <39-character name>) is claude. resolve_agent("claude-code", ...) is claude. The long-name test passes --agent "" and still nudges, which is the codex input-box path (test_issue66_stall_watch_sanitized_name.py:111-127).

No remaining herdr agent-name argument in stall_watch.py uses self.target or args.target. Those strings appear in the log lines (373, 397, 406). Pane calls use the pane id (186-193).

_notify publishes progress with the detail it is given (237-244). Idle publishes stalled:idle (408). Blocked publishes stalled:blocked plus the dialog line (374). Those details contain the stall reason. The original session name is in the matching log line (agent <original> idle for, agent <original> is blocked). The tests lock both (79-85, 101-106).

Sanitizer

sanitize_herdr_agent_name (lib_py/agents/sanitize.py:4-30) lowercases, replaces characters outside [a-z0-9_-] with -, prefixes x- when the result does not start with a letter, and when the result is longer than 32 characters keeps 23 characters, a hyphen, and 8 hex digits of SHA-1.

The function lib.sh runs is the definition at lib.sh:293-316 (an earlier copy at 36-61 is replaced when the file is sourced; every call site is below 316). It was executed on its own, without sourcing lib.sh. On this Mac, with shasum present, it returned the same strings as the Python function:

input result
creator-claude-fix-issue-99-long-branch (39) creator-claude-fix-issu-e6fc18ae
Creator-Claude-T creator-claude-t
creator-claude-t creator-claude-t
mam-wt-issue-66-very-long-creator-codex mam-wt-issue-66-very-lo-d55dfa83
empty agent
1abc x-1abc
A B a-b
33 x xxxxxxxxxxxxxxxxxxxxxxx-60bbb3c8
planner-agy planner-agy

The earlier shell copy produced the same three long, codex, and 1abc results. Python 3.9.6 imports stall_watch. The new code adds no match statement and no evaluated X | Y annotation. The module already has from __future__ import annotations.

Mock

strict_agent_names is off unless a test sets it. _match_agent then keeps the previous comparison (conftest.py:470-476). With the flag set, the mock answers only an exact registered name. agent get and agent read then print agent_not_found JSON (1057-1062, 1152-1158). agent wait and agent prompt already did (1089-1096, 1187-1194). Existing tests that leave the flag unset keep the old not-found text on get and read.

Tests

test_the_names_change_and_the_strict_mock_answers_only_the_sanitized_one registers the sanitized name and checks that get, read, wait, and prompt reject the original name with agent_not_found and accept the sanitized name (51-65).

test_an_idle_agent_is_nudged_through_the_sanitized_name covers the 39-character name, Creator-Claude-T, and creator-claude-t. Every get, read, prompt, and wait uses the sanitized name, one nudge is sent, the progress detail is stalled:idle, and the log contains agent <original> idle for (68-85).

test_a_blocked_agent_is_reported_with_the_original_name uses the sanitized name on the herdr calls, sends no prompt, publishes stalled:blocked plus the dialog line, and logs agent <original> is blocked (90-106).

test_the_kind_of_a_long_name_comes_from_its_original_suffix passes an empty --agent. The sanitized name does not end in -codex. The watcher still nudges and every herdr call uses the sanitized name (111-127).

Results

Pytest used the control-plane venv, PYTHONDONTWRITEBYTECODE=1, -p no:cacheprovider, and --basetemp /tmp/mam7b6-7b602181-pytest (head) and /tmp/mam7b6-7b602181-base-pytest (base). Neither path contains mam-review or shim. Inherited HERDR_*, MAM_*, MQTT_*, SKS_*, DELEGATE_JOB_LOGS_DIR, HOME_DIR, CLAUDE_*, CODEX_HOME, PYTHONPATH, WORKSPACE_ROOT, MAM_ENV_FILE, MAM_JOBS_DIR, AGENT_SESSIONS_YAML, the four XDG_* homes, and AGENT_PYTHON_BIN were dropped.

Head, cwd the snapshot: tests/test_issue66_stall_watch_sanitized_name.py, test_issue30_stall_watch.py, test_issue30_stall_watch_stop.py. 66 passed, 1 skipped in 356.72 s. Exit 0. The log closed at 2026-10-25 12:31:50. The skip is test_s6_the_heartbeat_timer_is_not_a_background_child, marked for Linux because it reads /proc (test_issue30_stall_watch.py:39-40, 340-341). That test is outside this delta. The seven Issue #66 tests passed inside the 66.

Base, this turn: a throwaway git clone --shared checked out at 0461a632eb667c7988fd33be92e9a4b36151f22a, with only tests/test_issue66_stall_watch_sanitized_name.py copied from head. Base stall_watch.py has no sanitize_herdr use. 6 failed, 1 passed in 37.05 s. Exit 1. The pass is test_an_idle_agent_is_nudged_through_the_sanitized_name[creator-claude-t], the name that needs no conversion. The six failures are the long name, the uppercase name, and the long codex name. On that tree get, read, and wait are invoked with creator-claude-fix-issue-99-long-branch and Creator-Claude-T. The snapshot was not checked out. Snapshot porcelain stayed empty.

Issue #17 timing failure

tests/test_issue17_item4_decisions.py::test_c_finalize_ends_with_an_unreaped_wrapper[broker_unavailable-3-0] is outside this diff. The test sends SIGTERM to a delegate wrapper during finalize and expects exit -15 within 4.5 seconds, an empty call log, and an unchanged job record (583-601). The diff names only stall_watch.py, tests/conftest.py, and the new Issue #66 test. It does not touch the wrapper, finalize_unfinished, or that test. A miss on that timing assertion is a cleanup race in the wrapper test, separate from the herdr agent name.

Blocking defects

None.

Non-blocking

  • lib.sh contains two copies of _sanitize_herdr_agent_name (36-61 and 293-316). Sourcing the file uses the second. On this host, with shasum present, both copies matched Python for the names checked above. The first copy's missing-hasher path truncates to 32 characters with no hash. This commit does not change lib.sh.
  • Progress details stay stalled:idle and stalled:blocked <dialog>. They carry the stall reason. The original session name is on the log line beside that notice.

Host check

No process has a first command token of opencode or a path ending in /opencode. lsof on ~/.local/share/opencode/opencode.db exited 1. That database was not opened. Its mtime is 1792641027 and its size is 36712448. opencode.log is absent. ~/.config/opencode/service.json mtime is 1792246838 and its size is 64. ~/.local/state/opencode/service.json is absent. The herdr config mtime is 1784456394. ~/.local/state/mam mtime is 1792846843. ~/.local/state/mam/id-order is a directory, mtime 1792846886. These stats match the check taken before the suite.

Five herdr processes were running immediately before the suite. After the suite log closed at 2026-10-25 12:31:50, the 12:31:59 check still found five. One pid from the before-set was gone. The replacement is /opt/homebrew/bin/herdr with --session multi-agent-mux, elapsed time about 15 seconds at 12:31:59, so it started near 12:31:44, during the last seconds of the suite. No herdr command line contained the review basetemp, the snapshot path, or d6f876. This review did not start or stop a herdr server.

After this report file was written, the same check was repeated at 2026-10-25 12:33:25. Snapshot porcelain is empty and the snapshot has no .mam. Control-plane porcelain is empty at 0461a632eb667c7988fd33be92e9a4b36151f22a. No process has a first command token of opencode or a path ending in /opencode, and lsof still reports no database holder. The database mtime is still 1792641027 and its size is still 36712448. opencode.log is still absent. The config service file mtime is still 1792246838. The state service file is still absent. The herdr config mtime is still 1784456394. ~/.local/state/mam mtime is still 1792846843, and id-order is still the same directory (mtime 1792846886). Five herdr processes were present. The pid set differs from the 12:31:59 set by one homebrew herdr process whose arguments include --session multi-agent-mux; its elapsed time was about 41 seconds, so that process had turned over again. No herdr command line contained the review basetemp, the snapshot path, or d6f876. This review did not start or stop it.

[VERDICT: PASS]