Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
19 KiB
19 KiB
PR #73 (fix/issue-71) Review Report — agy (herdr:reviewer-agy-01)
- Job ID:
0a1bb7e7 - Loop ID:
loop-20261026T133440Z-c8961b(iteration 2) - Issue / PR: Issue #71 / PR #73 (
fix/issue-71, 14 commits) - Base Commit:
29cc4fd90478936f15387a7665e2c95d13a3f14c(main,v4.10.1) - Head Commit:
5f6065344f800c2c5ad6f802e95e9e8ff5cc7b26(tree98d57bdc60920238c115ece90c90cc694f2744bc) - Review Worktree:
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261026T133440Z-c8961b/2 - Host Environment: macOS Darwin arm64 (
Darwin 25.3.0),/bin/bash 3.2.57(1)-release,/usr/bin/python3 3.9.6
1. Executive Summary
PR #73 resolves Issue #71 across all 10 scope items and review refinements (A–F):
- Unifies
attach_commandconstruction inlib_py/agents/sanitize.py::attach_command(herdr --session <server> agent attach <sanitized_name>) acrosscreate_session.sh,update_yaml_resumed.sh(both branches), andreconcile.sh, and teaches thelib.shshim'sherdr agent attachbranch to resolve its target via_resolve_herdr_target. - Removes
kill_commandandstart_commandfrom newly written rows,status.sh --json, andSKILL.mdexamples, and replaces directherdr kill-sessioncleanup hints increate_session.sh(exit 43) andid_order.pywithstop_session.sh --session <name> --herdr-session <srv>. - Adds row-less session closing (
stop_rowless) tostop_session.shwith strict registry readability gating (load_state_json strict), workspace boundary enforcement (os.path.realpathprefix-safe check, exit 5 on foreign workspace), server selection parity withcreate_session.sh, and registry immutability. - Consolidates
_sanitize_herdr_agent_nameinto a single definition inlib.shinjected into the generated shim viadeclare -f, achieving exact parity with Python'ssanitize_herdr_agent_nameacross ASCII, non-ASCII/multibyte, option-like (-n,-e), and newline-containing names, while failing closed (nonzero, stderr) when no hash tool orpython3is available instead of emitting00000000or truncated names. - Introduces registry format version 2 (
registry_format: 2) inlib_py/registry_format.pyandlib_py/atomic_yaml.py, migrating under the writer lock (BEGIN IMMEDIATE) and repairing stale YAML copies on retry. - Computes
herdr_nameonce inStallWatch.__init__and passes it as a required keyword-only argumentnametoHerdrSource.
All 10 review checklist items, macOS /bin/bash 3.2.57 and /usr/bin/python3 3.9.6 compatibility checks, negative regression on base (68 failed, 2 passed), and 461 functional/integration tests on head (461 passed, 1 skipped, 0 failed) pass on macOS Darwin arm64.
2. Checklist Verification (Items 1–10)
1. Completion Criteria & Scope Discipline (Issue #71 & Scope Comment Items 1–10)
- Verified (
PASS):- Every requirement in Issue #71 and the "범위 정리" comment (items 1–10) is implemented and covered by regression tests.
- Out-of-scope Issue #72 (
multi-agent-mux-updateskill) is not pulled into this PR; only thepython3 -m lib_py.registry_format --yaml <path>entry point specified in item 9 is provided for #72 to call.MAM_VERSION(4.10.1) andVERSIONS.mdare untouched as intended.
2. Regression Suite tests/test_issue71_herdr_names.py (Base vs Head & Reconcile Pane Precondition)
- Verified (
PASS):- Base (
29cc4fd90478936f15387a7665e2c95d13a3f14c): Checked out into an isolated temporary directory outside the review worktree and rantests/test_issue71_herdr_names.py(70 tests): 68 failed, 2 passed in 104.17s (the only 2 passing tests onbasearetest_the_names_need_converting_and_the_plain_one_does_notandtest_wrapper_attach_calls_herdr_with_the_herdr_name[lab-plain-creator-claude], matching the PR description). - Head (
5f6065344f800c2c5ad6f802e95e9e8ff5cc7b26): All 70 passed (including all 4hashersparameterizations:shasum,sha1sum,openssl,python3). - Reconcile test precondition:
test_reconcile_registers_with_the_attach_commandseedsmock_herdrwithagents={name: {"agent": "claude", "status": "running", "cwd": str(mam_sandbox), "workspace_id": "w1", "pid": 9999, "pane_id": "w1:p71", "command": "claude", "buffer": "Ready"}}and assertslen(rows) == 1androws[0]["last_visible_note"] == "auto-registered by monitor"before checking_assert_new_form(rows[0], rows[0]["herdr_session"]).
- Base (
3. attach_command (lib_py/agents/sanitize.py) & lib.sh Wrapper agent attach
- Verified (
PASS):lib_py/agents/sanitize.py:34-42defines:def attach_command(server, name): return "herdr --session %s agent attach %s" % ( shlex.quote(server or "default"), shlex.quote(sanitize_herdr_agent_name(name)))- Always writes
--session defaultwhenserveris"default"or empty/falsy, and appliesshlex.quoteto both the server and sanitized agent name. - Used by all three writers:
create:create_session.sh:512, 566resume(new row and existing row):update_yaml_resumed.sh:119, 150, 160reconcile:reconcile.sh:569, 676
- In
lib.sh:605-613, the shim'sagent attachbranch shifts<target>(t="$1"; shift), resolvestgt=$(_resolve_herdr_target "$t" || true), fails with exit 1 if empty, and invokes_real_herdr agent attach "$tgt" "$@", preserving trailing flags such as--takeover.
4. Removal of kill_command & start_command and Updated Cleanup Hints
- Verified (
PASS):git grep -E 'kill_command|start_command'across the repository confirms no production code, skill doc, or active schema assertion reads or writeskill_commandorstart_command(the only remaining mentions are inregistry_format.pyto drop them during v1 -> v2 migration and intest_issue71_herdr_names.py/test_tier2_component.pyasserting their absence).status.sh:221-231andmulti-agent-mux-status/SKILL.md:94-101no longer includestart_command, and nothing in the codebase readsstart_commandfromstatus.sh --json.- The shim's
new-sessionandkill-sessiontranslation branches inlib.share properly retained becausecreate_session.sh,resume_session.sh,stop_session.sh, andreconcile.shcall them internally. - Both user-facing cleanup hints now point to
stop_session.sh:create_session.sh:364:bash .agents/skills/multi-agent-mux-stop/scripts/stop_session.sh --session $SESSION_NAME --herdr-session ${HERDR_SESSION_NAME:-default}lib_py/agents/id_order.py:359-360:bash .agents/skills/multi-agent-mux-stop/scripts/stop_session.sh --session {m['session']} --herdr-session {srv}
5. Row-less stop (stop_session.sh) & Strict Registry Read (load_state_json strict)
- Verified (
PASS):- Strict registry read gate:
stop_session.sh:170-193callsSTATE_JSON=$(load_state_json strict)first. Inlib.sh:1597-1631,load_state_json strictre-raisessqlite3.OperationalErroron missingstateorsessionstables as well as corrupt DB / malformed YAML, printing tostderrand exiting1. Furthermore,MAPPED_DATAexits7only whend.get('herdr_sessions', [])was validly iterated and had no matching dict row; non-dict rows (herdr_sessions: [- 42]) exit1(cannot read the row). - Workspace boundary check:
stop_rowless(stop_session.sh:145-155) resolves bothPANE_CWDandWORKSPACE_ROOTviaos.path.realpathand checkscwd == root or cwd.startswith(root + os.sep). Verified on macOS that this prevents prefix confusion (/tmp/wsvs/tmp/ws-other), handles symlinks (/var->/private/var), and normalizes trailing slashes; out-of-workspace sessions are refused with exit code5before anykill-session. - Server selection order: Matches
create_session.sh:184-188:--herdr-session(HERDR_SERVER_OPT) -> ambientHERDR_SESSION_NAME(when non-empty and!= "default") -> workspace server viaresolve_herdr_session. --purge-conversationand exit codes: Refuses--purge-conversationon a row-less session with exit1, exits6ifherdr has-sessionstill succeeds afterkill-session, never mutatesagent-sessions.yamloragent-sessions.db, and matchesmulti-agent-mux-stop/SKILL.md:59-67.
- Strict registry read gate:
6. Single Sanitize Rule in lib.sh & macOS BSD / Locale Parity
- Verified (
PASS):lib.sh:40-85has the single definition of_sanitize_herdr_agent_name._init_herdr_isolation(lib.sh:179-182) writesdeclare -f _sanitize_herdr_agent_nameinto the generated shim ($wrapper_dir/herdr).- Removed the duplicate definition in the shim heredoc, removed
"00000000"and${s:0:32}fallbacks, and removedhas-session's relative-pathsys.path.insert(0, os.path.abspath(".agents/skills"))+lambda s: s.lower()[:32]fallback (lib.sh:633-647now computesssess=$(_sanitize_herdr_agent_name "$sess") || exit 1in bash and passesTARGET_SAN="$ssess"). - macOS
/bin/bash 3.2.57, BSDtr/sed, and locale (LC_ALL=CandLC_ALL=en_US.UTF-8) verification:- Pure-ASCII check uses
LC_ALL=C tr -d '\000-\177'. For ASCII inputs,printf '%s' "$n" | tr '\n' '-' | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9_-]/-/g'avoidsechoflag parsing (-n,-e,-En) and converts\nto-beforesedor command substitution$(...)can strip trailing newlines. - For non-ASCII inputs (
İ,é,ü,K, CJK/Hangul, emoji), it delegates lowercasing andre.sub(r"[^a-z0-9_-]", "-", ...)topython3so character-by-character replacement and Unicode case folding (\u0130.lower() ->'i\u0307'->'i-') matchlib_py.agents.sanitize.sanitize_herdr_agent_namebyte-for-byte, and fails closed (exit 1, stderr) ifpython3is unavailable.
- Pure-ASCII check uses
7. Registry Format Version 2 (lib_py/registry_format.py, lib_py/atomic_yaml.py)
- Verified (
PASS):- Inside
atomic_dump_yaml_main(lib_py/atomic_yaml.py:121-138), after acquiring the flock andBEGIN IMMEDIATEand loadingstate+sessions, iffrom_version < registry_format.CURRENT(2),registry_format.migrate(d)runs before the caller's mutation and setsforce_yaml = True. - Migrates all rows in
herdr_sessions(running,stopped,terminated,archived), dropskill_commandandstart_command, recomputesattach_command, and setsd["registry_format"] = 2. - If the DB was already at format 2 (
elif row:) but an earlier YAMLos.replacefailed afterconn.commit(),force_yaml = registry_format.stored_version(y) < registry_format.CURRENTdetects the stale YAML and rewrites it on the next write. - Future format preservation: Verified that when
registry_format> 2 (e.g.,3),stored_version(d)returns3,from_version < CURRENTisFalse, andforce_yamlisFalse— the writer neither downgradesregistry_formatnor clobbers future fields. - Other registry paths: Checked all references to
agent-sessions.yamlacross.agents/skills/;atomic_dump_yamlis the sole writer in production (create_session.sh,update_yaml_resumed.sh,stop_session.sh,reconcile.sh,orc_onboard.sh), whileload_state_json(lib.sh/lib_py/state.py) is strictly read-only. - CLI entry point:
PYTHONPATH=.agents/skills python3 -m lib_py.registry_format --yaml <path>printsregistry-format: <from> -> <to>, rows changed: <n>and exits0(or nonzero withregistry-format: error: ...on stderr when missing/failing), matching its module docstring contract.
- Inside
8. stall_watch.py Single Sanitization Point
- Verified (
PASS):- In
.agents/skills/lib_py/stall_watch.py:158-163, 229-230,StallWatch.__init__computesself.herdr_name = sanitize_herdr_agent_name(args.target)once and passesname=self.herdr_name(required keyword-only parameter) toHerdrSource. tests/test_issue66_stall_watch_sanitized_name.pyis unmodified ingit diffand all 7 of its tests pass.
- In
9. Test Updates, skill_code_blocks.json, and test_s11 Anchor (0fc162e)
- Verified (
PASS):tests/test_issue50_pane_lock.py,tests/test_issue21_worktree_prereqs.py,tests/test_tier1_unit.py,tests/test_tier2_component.py, andtests/test_tier3_integration.pypreserve their original test intent after replacing assertions on removedstart_command/kill_commandfields.tests/data/skill_code_blocks.jsonandtests/test_issue42_skill_blocks.pypass all 96 skill-block verification tests.- In
tests/test_issue23_24_startup_path.py:306-335,test_s11_this_branch_does_not_change_the_generated_shimpinsbase = os.environ.get("MAM_BASE_SHA", "0fc162e")and extracts both_sanitize_herdr_agent_name() { ... }and thecat <<'EOF'heredoc body in_heredoc_body. Commit0fc162eis an ancestor ofHEAD(5f60653) onfix/issue-71; when merged intomainvia merge commit (as documented in PR #73),0fc162eremains reachable in git history sotest_s11actively verifies the shim rather than skipping. (Theif show.returncode != 0: pytest.skip(...)guard is pre-existing for shallow/archive checkouts without history.)
10. macOS Compatibility (/usr/bin/python3 3.9.6, /bin/bash 3.2.57, BSD Userland, SQLite WAL)
- Verified (
PASS):- Parsed all 16 modified/added Python files with
/usr/bin/python3(3.9.6)ast.parse(..., feature_version=(3, 9))and importedlib_py.agents.sanitize,lib_py.registry_format,lib_py.atomic_yaml, andlib_py.stall_watchunder system Python 3.9.6. - Checked all 7 modified bash scripts and the generated
.mam/shim/herdrwith/bin/bash -n(GNU bash 3.2.57(1)-release); no bash 4+ constructs (declare -A,${var,,},mapfile,readarray) are used. - In
tests/test_issue71_herdr_names.py:514-527,test_stop_does_not_take_an_unreadable_registry_for_no_rowunlinksagent-sessions.db*/agent-sessions.db-*before writing corrupt DB/YAML fixtures, properly accounting for macOS SQLite's persistent-wal/-shmsidecars.
- Parsed all 16 modified/added Python files with
3. Test Execution Summary on macOS
| Suite / Command | Target | Result |
|---|---|---|
git diff --check 29cc4fd..5f60653 |
Review worktree /2 |
Clean (exit 0) |
/bin/bash -n on all 7 modified .sh scripts |
/bin/bash 3.2.57 |
All 7 OK (exit 0) |
/usr/bin/python3 (3.9.6) AST + import + LC_ALL=C/en_US.UTF-8 sanitize & path checks |
System Python 3.9.6 + /bin/bash 3.2.57 |
ALL_LOCALE_AND_MULTIBYTE_PARITY_OK, FUTURE_FORMAT_PRESERVED_OK, STOP_ROWLESS_PATH_BOUNDARY_CHECKS_OK (exit 0) |
pytest tests/test_issue71_herdr_names.py |
Base (29cc4fd in temp dir) |
68 failed, 2 passed in 104.17s |
pytest tests/test_issue71_herdr_names.py tests/test_issue66_stall_watch_sanitized_name.py tests/test_layout.py tests/test_issue42_skill_blocks.py |
Head (5f60653, worktree /2) |
152 passed, 1 skipped in 150.63s (all 70 in test_issue71 and all 7 in test_issue66 passed) |
pytest tests/test_issue60_linux_fixes.py tests/test_tier1_unit.py tests/test_tier2_component.py tests/test_tier3_integration.py tests/test_issue23_24_startup_path.py tests/test_issue50_pane_lock.py tests/test_issue21_worktree_prereqs.py tests/test_issue63_same_cwd_ids.py |
Head (5f60653) |
309 passed, 0 failed |
4. Defects & Observations
Blocking Defects
- None.
Non-Blocking Observations
- In
.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job:1021, the informational message aftersend-keysprints(연결하려면: HERDR_SESSION_NAME=$HERDR_SESSION_NAME herdr agent attach $sess — lib.sh를 source한 셸에서 실행). Because PR #73 updated thelib.shshim'sherdr agent attachhandler to resolve$sessvia_resolve_herdr_target, this command works as stated whenlib.shis sourced, though a future cleanup could also print the nativeherdr --session ... agent attach ...form.
[VERDICT: PASS]