Review loop (--review-only, --all-reviewer, focused on this Mac: macOS, /usr/bin/python3 3.9.6, bash 3.2): round 1 (@6f4f40a) agy and grok PASS, claude and codex NOT PASS (a non-uuid id `memory` purged the auto-memory dir, a linked dir below a root was followed, a NaN startedAt counted as dead), sent as a PR comment and fixed by the author in eleven commits; round 2 (@2a8d738) confirmed all three fixed but 4/4 NOT PASS on one test holder (`sh -c "sleep 120" <uuid>` loses the uuid on macOS /bin/sh), fixed in de6f3fc; round 3 (@de6f3fc) 4/4 PASS. Full suite on macOS / Python 3.9.6 @de6f3fc: 1934 passed, 56 skipped, 1 error (the known #55 false positive: agy conversations of another workspace). The real ~/.claude, /private/tmp/claude-<uid> and herdr panes unchanged. Also names lib_py.delegation.logroot in eager.CORE_MODULES (the v4.9.4 review's non-blocking note). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
7.4 KiB
7.4 KiB
Pre-Merge Review Report (Round 2): PR #59 (fix/issue-31)
- Job ID:
b4aa3aff - Reviewer:
herdr:reviewer-agy-01(agy) - Commit Range:
26e2c43753169671b600d03274901dbe2039887..2a8d73825b3832376aeaf96b09548359dc3cb887(Round 2 delta:6f4f40a4adb2115d5997dffd31fb6473f05cab68..2a8d73825b3832376aeaf96b09548359dc3cb887, 11 commits) - Verification Worktree:
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261017T135731Z-e93f0a/1 - Host / Runtime: macOS
arm64-apple-darwin25(Darwin 25.3.0),/usr/bin/python33.9.6 &.venv/bin/python3.9.6,/bin/bash3.2.57
1. Round 1 Findings Verification
All three required production fixes and all four non-blocking improvements from Round 1 are cleanly resolved in 6f4f40a..2a8d738:
-
[Blocker — Resolved] Canonical UUID enforcement across Claude adapter (
bd09c6a,c0248b8,81b6c2e,2a8d738):_canonical(uuid)in.agents/skills/lib_py/agents/adapters/claude.py:10-18validatesisinstance(uuid, str) and _UUID.fullmatch(uuid)([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}, case-insensitive) and normalizes to lowercase._candidates,has_artifacts,conversation_in_use,purge_artifacts, andresidual_artifactsall gate on_canonical(uuid), ensuring non-UUID names (such asmemory,notes, oruuid-c) never touchprojects/<key>/memory/or any other directory/file.conversation_in_usechecksany(conversation_in_use(u, [self.artifact_path(canon, ctx)]) for u in {canon, uuid})plusself._session_files(canon, ctx), properly handling uppercase/mixed-case UUIDs on case-insensitive APFS.tests/test_a4_adapter_contract.py:225-232was updated with user approval to use a canonical UUID (0c1a5f3e-6b2d-4c8e-9a7f-1d2e3f4a5b6c).
-
[P1 — Resolved] Symlink boundary check below Claude roots (
6e0361c,dd292c1):_inside(root, path)in.agents/skills/lib_py/agents/adapters/claude.py:108-122walks every path component ofos.path.relpath(os.path.dirname(path), root)and verifiesstat.S_ISDIR(os.lstat(cur).st_mode)(rejecting any intermediate symlink) andos.path.realpath(os.path.dirname(path)).startswith(real_root.rstrip(os.sep) + os.sep).- Roots that are themselves symlinks (such as
/tmp -> /private/tmpon macOS or a symlinked~/.claude) and leaf symlinks (_removeunlinks the leaf symlink without traversing) work as intended. has_artifactschecksbool(self._candidates(uuid, ctx))so a candidate behind a symlink still selectsclaudeinpurge auto/purge claudeand is reported inresidual_artifactswithout being deleted.
-
[P2 — Resolved] Fail-safe
startedAtvalidation in_session_file_live(f943b0d,60779ba):.agents/skills/lib_py/agents/adapters/claude.py:87-95rejectsbool, non-(int, float),OverflowErroronfloat(started) / 1000.0(e.g.10**400),NaN,±inf, and<= 0, returningTrue(fail-safe live) whenever the PID is alive.
-
[Non-Blocking Notes — Resolved]:
- UTC
lstartparsing (af8ed39):ps -p <pid> -o lstart=runs withLC_ALL="C", TZ="UTC0"and parses viacalendar.timegm. Verified directly on macOS Darwin with both.venv/bin/pythonand/usr/bin/python3(abs(time.time() - lstart) < 0.7s). - Actionable
in_use_hints& exit-4 wording (e156179):BaseAgentAdapter.in_use_hintsandClaudeAgentAdapter.in_use_hintsprint the active<cfg>/sessions/path andps -axww | grep <uuid>hint on stderr whenin-useorstop_session.sh --purge-conversationexits 4;SKILL.mdandstop_session.shaccurately state that registry records and conversation files are preserved even though the herdr session itself may already be down. - Host
/tmp/claude-<uid>test isolation (26fb1c4):_SYSTEM_TMP = "/tmp"inclaude.pyis redirected by theown_tmp_rootsautouse fixture (andsitecustomize.pyfor child Python processes) intests/test_issue31_purge_residual.py.
- UTC
-
Pre-fix failure verification:
- Running the new Round 2 regression tests from
tests/test_issue31_purge_residual.pyagainst pre-fix code (26fb1c4, beforebd09c6a..2a8d738) in an isolated/tmpcopy produced 25 test failures across all targeted bug classes (test_claude_rejects_malformed_uuid,test_any_spelling_of_the_uuid_is_the_lowercase_session,test_claude_purge_never_follows_a_linked_dir_below_a_root,test_purge_cli_reports_a_linked_orphan_it_will_not_remove,test_a_non_uuid_id_reaches_nothing,test_liveness_reads_lstart_as_utc,test_an_unusable_started_at_means_live,test_a_live_holder_with_an_unusable_started_at_blocks_the_purge).
- Running the new Round 2 regression tests from
2. Remaining Issue Blocking Merge
[P1] test_any_spelling_of_the_uuid_is_the_lowercase_session fails on macOS due to /bin/sh -c single-command exec optimization
- Location:
tests/test_issue31_purge_residual.py:315(introduced in commitc0248b8) - Symptom:
Running pytest on macOS fails both parametrizations (
[upper]and[mixed]):FAILED tests/test_issue31_purge_residual.py::test_any_spelling_of_the_uuid_is_the_lowercase_session[upper] FAILED tests/test_issue31_purge_residual.py::test_any_spelling_of_the_uuid_is_the_lowercase_session[mixed] AssertionError: a command line naming the lowercase id assert False is True - Root Cause:
Line 315 spawns the dummy holder process as:
On macOS (
named = subprocess.Popen(["sh", "-c", "sleep 120", conv])/bin/shis GNU bash 3.2.57 in POSIX mode),sh -c "sleep 120" <conv>sees a single simple command with no subsequent commands or traps and optimizes by callingexecve("/bin/sleep", ["sleep", "120"])directly in-place (CMD_NO_FORK). Within ~10ms (beforetime.sleep(0.05)finishes), the process image and argv inps -axo pid=,command=becomesleep 120, and$0(conv) vanishes from the process list. Note thattests/test_issue10_13_purge.py:111-118(_holder) already documents this exact pitfall:(Not sh -c 'sleep …': a shell execs its last simple command and the uuid would vanish from the process list.) - Required Fix:
In
tests/test_issue31_purge_residual.py:315, usesys.executableinstead ofsh -c:(Verified in an isolatednamed = subprocess.Popen([sys.executable, "-c", "import time; time.sleep(120)", conv])/tmpcopy on macOS: replacing line 315 withsys.executablemakes both[upper]and[mixed]pass in 2.24s, with no orphaned childsleepprocess afternamed.kill()).
3. Verification Summary
- Targeted pytest suite run against
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261017T135731Z-e93f0a/1with scrubbed herdr/MAM/Claude environment variables:tests/test_issue55_agent_homes.py: PASSEDtests/test_issue42_skill_blocks.py: PASSEDtests/test_a4_adapter_contract.py: PASSEDtests/test_orc_onboard.py: PASSEDtests/test_tier1_unit.py: PASSEDtests/test_issue31_purge_residual.py: 65 passed, 2 failed (test_any_spelling_of_the_uuid_is_the_lowercase_session[upper]and[mixed])- Total: 250 passed, 2 failed, 1 skipped (all 252 pass once
tests/test_issue31_purge_residual.py:315usessys.executable).
- Verification worktree
/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261017T135731Z-e93f0a/1verified clean (git status -sempty, no.mamdirectory created).
[VERDICT: NOT PASS]