Files
multi-agent-mux/.agents/reports/reviewer-agy-01/report-2fff60bd.md
T
GodopuandClaude Opus 5.5 6fd417a7b7 release(v4.9.5): a claude purge removes every path the session owns (Issue #31, PR #59) - archive the review reports, bump the lockstep version, load logroot eagerly
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
2026-10-18 01:25:15 +09:00

6.3 KiB

Review Report — PR #59 (Issue #31: Complete Claude purge, residual reporting, and live-process stop gate)

  • Reviewer: agy (herdr:reviewer-agy-01)
  • Job ID: 2fff60bd
  • Branch / Range: fix/issue-31 (26e2c43753169671b600d03274901dbe203988dd..6f4f40a4adb2115d5997dffd31fb6473f05cab68, 13 commits, 12 files)
  • Verification Worktree: /var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261016T003524Z-a51548/1
  • Platform: macOS Darwin (arm64-apple-darwin25, /usr/bin/python3 3.9.6, .venv/bin/python 3.9.6, /bin/bash 3.2.57)

1. Executive Summary

PR #59 resolves Issue #31 by unifying the Claude adapter's owned-path resolution (_owned_paths) across purge_artifacts, has_artifacts, and residual_artifacts, covering:

  • projects/<key>/<uuid>.jsonl and projects/<key>/<uuid>/ (across both ctx.claude_dir and <config_dir>/projects),
  • file-history/<uuid>/ and session-env/<uuid>/,
  • jobs/<uuid[:8]>/ (only when state.json has sessionId == uuid, removing state.json last so partial failure preserves ownership evidence),
  • sessions/<pid>.<64hex>.key (masked in output as <pid>.*.key) and sessions/<pid>.json for dead or PID-reused processes (removing keys before the .json file so ownership evidence survives partial failure),
  • <tmp>/claude-<uid>/<key>/<uuid>/ across CLAUDE_CODE_TMPDIR, TMPDIR/TMP/TEMP, and /tmp.

It also adds residual_artifacts for claude, codex, grok, and opencode; wires stop_session.sh to report residuals after --purge-conversation and to check python -m lib_py.agents in-use <agent> <uuid> <cwd> --wait 5 before purging (exiting 4 without mutating state if a live agent process still holds the conversation); supports CLAUDE_CONFIG_DIR across lib.sh, DiscoveryContext, claude.py, and _agent_home_guard.py; and updates multi-agent-mux-stop/SKILL.md + tests/data/skill_code_blocks.json to reflect that --purge-conversation removes the session row rather than leaving status: terminated.

All 12 modified files pass Python 3.9.6 AST compilation and Bash 3.2 (set -u) syntax checks, all 11 regression test cases fail against base 26e2c43 and pass on head 6f4f40a, and 217 tests across 6 test suites pass on macOS.


2. macOS Environment & Deletion-Scope Safety Verification

  1. BSD ps -o lstart= Format, Locale (LC_ALL=C), and startedAt + 2 Comparison on macOS:

    • Verified BSD ps -p <pid> -o lstart= on this Mac under LC_ALL=C: outputs 'Fri Oct 16 09:48:41 2026 \n'.
    • " ".join(out.split()) normalizes multi-space padding (including single-digit days like 'Oct 2') and trailing whitespace before time.strptime(..., "%a %b %d %H:%M:%S %Y").
    • Inspected 4 live ~/.claude/sessions/<pid>.json files on this Mac (read-only): startedAt / 1000.0 - lstart ranged from +0.694 s to +2.430 s, so lstart <= started + 2 holds for all live processes and rejects reused PIDs started after the session file was written.
    • Fail-safe behavior: invalid/non-positive PIDs, started <= 0, missing startedAt, or ps/parse errors all return True (treated as live, refusing purge).
  2. macOS Temp Root Resolution (TMPDIR vs /tmp -> /private/tmp) & Real Layout Agreement:

    • On this Mac, TMPDIR is /var/folders/q_/.../T/ (which has no claude-501), while Claude Code's actual scratchpad directory is /private/tmp/claude-501/<workspace_key>/<uuid>/ (accessed via /tmp/claude-501 symlink).
    • _tmp_roots() checks CLAUDE_CODE_TMPDIR (both <override>/claude-<uid> and <override> when already named claude-<uid>), TMPDIR/TMP/TEMP, and /tmp/claude-<uid>, deduplicating via os.path.realpath(c). This ensures /private/tmp/claude-<uid> is always discovered on macOS even when TMPDIR points to /var/folders/.../T/.
    • Read-only inspection of ~/.claude on this Mac confirmed exact structural agreement for projects/<key>/, file-history/<uuid>/, session-env/<uuid>/, sessions/<pid>.json + <pid>.<64hex>.key, and jobs/<uuid[:8]>/state.json (while shared files like jobs/pins.json and history.jsonl are untouched).
  3. Deletion Scope Isolation, Malformed UUID Rejection, and Symlink Safety:

    • _plausible_id(uuid) rejects "", ".", "..", "*", and any string containing "/", "..", or "*" before constructing any path or scanning any directory.
    • _owned_job checks json.load(open(job/state.json)).get("sessionId") == uuid, leaving another session's jobs/<uuid[:8]>/ (and jobs/pins.json) untouched.
    • _key_files matches rf"{stem}\.[0-9a-f]{{64}}\.key" only for a sessions/<pid>.json whose sessionId == uuid, never reading key contents and never touching lone keys or other sessions' PIDs.
    • _remove checks if os.path.islink(path) or not os.path.isdir(path): os.remove(path) before shutil.rmtree(path, ignore_errors=True), and scan_names uses os.walk(root) (followlinks=False), ensuring symlinks are never traversed outside the target scope.
  4. Stop Exit 4 Contract, Residual Reporting, and Documentation Lockstep:

    • stop_session.sh runs "$(_delegate_py_bin)" -m lib_py.agents in-use "$AGENT" "$PURGE_UUID" "$TARGET_CWD" --wait 5 before atomic_dump_yaml; if non-zero, it publishes a cancelled job event and exits 4 with all records and files preserved.
    • tests/data/skill_code_blocks.json hashes (301c347ff881, a973676cb2d1) and line number (stop_session.sh:327) match multi-agent-mux-stop/SKILL.md and stop_session.sh (test_issue42_skill_blocks.py passes).

3. Test Suite Execution Summary

Executed in /var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261016T003524Z-a51548/1 with HERDR_*, WORKSPACE_ROOT, MAM_ENV_FILE, MAM_JOBS_DIR, DELEGATE_JOB_LOGS_DIR, and CLAUDE_CONFIG_DIR scrubbed, PYTHONDONTWRITEBYTECODE=1, -p no:cacheprovider, and --basetemp=/tmp/mam_verify_pr59_bt:

  • tests/test_issue31_purge_residual.py: 32 passed (11 core regression cases verified to fail on base 26e2c43)
  • tests/test_issue55_agent_homes.py: 11 passed
  • tests/test_issue42_skill_blocks.py: 15 passed
  • tests/test_a4_adapter_contract.py: 55 passed
  • tests/test_orc_onboard.py: 37 passed, 1 skipped
  • tests/test_tier1_unit.py: 67 passed

Total: 217 passed, 1 skipped, 0 failed. Verification worktree git status -s is empty and no .mam directory was created.

[VERDICT: PASS]