Files
multi-agent-mux/.agents/reports/reviewer-grok-01/report-35f0c94f.md
T
GodopuandClaude Opus 5.5 e0181ea9be release(v4.8.8): Issue #17 follow-up - archive the review reports and bump the lockstep version
Merge-workspace review loop (--all-reviewer): round 1 (@d198dd5) claude
agy PASS, grok codex NOT PASS (the same inherited WORKSPACE_ROOT in eleven
more modules; test_orc_onboard's import-time env); fixed in 8b47cc7.
Round 2 claude grok NOT PASS (test_o2_race_free_lock, test_o1_rebuttal
missed), agy codex PASS; fixed in ac48cb7. Round 3 (@ac48cb7) 4/4 PASS.
Full suite at ac48cb7 on macOS / Python 3.9 / herdr 0.9.3 with
WORKSPACE_ROOT set to an empty sentinel: 1751 passed / 0 failed / 38
skipped; the sentinel stayed empty and the main checkout's shim was not
touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
2026-10-09 05:01:05 +09:00

9.4 KiB
Raw Blame History

Review 35f0c94f — Issue #17 follow-up, inherited WORKSPACE_ROOT

Head 8b47cc76eed4582b1848b2eec529523a61497320 (tree bfe5fcbbefbde9562334515b4a9803bee96f84fd) closes the writers named in the previous review and the new isolation tests pass. tests/test_o1_rebuttal.py:68 still runs the checkout run_loop.sh with the process environment, and that script sources lib.sh before it parses --help, so an agent pane whose WORKSPACE_ROOT names this checkout still gets .mam/shim/herdr rewritten.

Snapshot /var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261008T133858Z-9e7f5b/2 only. Base 6b00b6e06f55299cb60f156ccef09ede4bae5bdd (control-plane main, unchanged). Branch commits: d198dd5 then 8b47cc7. Fifteen files, +212/−18, git diff --check clean. Product code under .agents/skills is unchanged. No version bump.

Interpreter: worktree .venv Python 3.9.6, bash 3.2.57. lib.sh picks that interpreter when AGENT_PYTHON_BIN is set (lib.sh:2050); otherwise it falls through to /usr/bin/python3, which has neither paho nor yaml.

What the branch does

tests/conftest.py:181-192 adds an opt-in fixture own_workspace_root. It creates tmp_path/.mam-test-state/workspace and sets WORKSPACE_ROOT there. It is not autouse, because in-process code also reads WORKSPACE_ROOT. Modules whose shells inherit the process environment opt in with pytestmark = pytest.mark.usefixtures("own_workspace_root").

On this pytest, a test that requests both the mark and mam_sandbox finishes with mam_sandbox's value. A one-test probe (usefixtures plus a mam_sandbox parameter) left os.environ["WORKSPACE_ROOT"] as the sandbox path. The orc-onboard tests, which use mam_sandbox and pass that directory into _scrubbed_env, passed in the suite below, so a test that names its own root still keeps it.

tests/test_issue17_item4_decisions.py:224-233 drops an inherited WORKSPACE_ROOT and sets the helper's tmp_path. Unset is the wrong fix: lib.sh:26 derives the checkout that contains the script, and lib.sh:3088 calls _init_herdr_isolation (lib.sh:143), which rewrites $WORKSPACE_ROOT/.mam/shim/herdr. pytest_configure (tests/conftest.py:144-146) drops WORKSPACE_ROOT only when its real path is a different checkout, so a value that names this checkout survives collection. That is why the item-4 helpers missed the autouse state fixture: they never request mam_sandbox.

tests/test_orc_onboard.py:19-30 still copies os.environ at import, and that copy now drops WORKSPACE_ROOT. Every source path rebuilds the env at call time from the current root (_scrubbed_env). tests/test_issue21_worktree_prereqs.py:547 sets WORKSPACE_ROOT to the temp tree inside _mutate. tests/test_workspace_scope.py:32 sets it to that test's workspace.

The same mark is on test_b19_headless_reconcile_fixes.py, test_b8_send_keys_verification.py, test_issue6_fail_closed.py, test_version_consistency.py, test_loop_cli.py, test_issue5_phase3_loop.py, test_issue5_phase4_runtime.py, test_o3_scoped_guard.py, test_issue25_plan_reuse.py, and test_orc_onboard.py.

Requirement 3 — the new tests fail on the pre-fix code

tests/test_issue17_workspace_root_isolation.py adds W1 and W2. W1 copies the checkout, runs one node from each previously inheriting module with WORKSPACE_ROOT naming that copy (conftest keeps a root that names the checkout), and requires the copy to have no .mam. W2 imports test_orc_onboard while WORKSPACE_ROOT is a sentinel, then points the process env at a later directory and calls run_find_uuid.

Pre-fix probe, snapshot lib.sh, outside the tree:

  • Base tests/test_orc_onboard.py from 6b00b6e, loaded from a temp tree whose .agents is the snapshot's, imported with WORKSPACE_ROOT set to a sentinel, then os.environ pointed at another directory and run_find_uuid called: the sentinel received .mam, .mam/shim, .mam/shim/herdr. The later directory received nothing. Return code 0.
  • The same probe against the head module: the sentinel stayed empty, and the later directory received .mam/shim/herdr. Return code 0.

bash run_loop.sh --help with WORKSPACE_ROOT set to /tmp/35f0o1sent.q8w2 also wrote .mam/shim/herdr there (usage exits 1). Source is run_loop.sh:17, before the option parser. The snapshot shim was absent before and after that probe.

W1 and W2 are in the passing suite below. The item-4 sentinel test test_c_helpers_write_nothing_to_an_inherited_workspace is in the same file as the _env fix and passed with it.

Requirement 4 — suite

AGENT_PYTHON_BIN and PATH pointed at the worktree .venv. Inherited HERDR_*, MAM_*, MQTT_*, SKS_*, and WORKSPACE_ROOT were unset in the parent. PYTHONDONTWRITEBYTECODE=1, -p no:cacheprovider. Basetemp directories are under /tmp and do not contain mam-review.

(a) WORKSPACE_ROOT unset, basetemp /tmp/35f0C.m2k8Qn, the 14 modified test modules plus tests/test_issue33_budget.py, tests/test_issue5_phase1_delegation.py, and tests/test_tier1_unit.py: 643 passed in 837.78s, exit 0.

(b) WORKSPACE_ROOT=/tmp/35f0sent2.w9c3Tb (the directory existed), basetemp /tmp/35f0D.h6p4Js, the 14 modified modules: 490 passed in 672.56s, exit 0. The sentinel was empty afterwards.

pytest_configure removes a foreign WORKSPACE_ROOT before collection, so an outer sentinel stays empty even if a helper is wrong. The runs that keep a checkout-named root are W1 (it sets WORKSPACE_ROOT to the copied checkout) and the item-4 test (it monkeypatches a sentinel after configure). Both passed inside (a) and (b).

A first pair of runs without AGENT_PYTHON_BIN reported 22 failed / 621 passed and 22 failed / 468 passed. Those failures are /usr/bin/python3 missing paho and yaml (mqtt_common.py and atomic_yaml.py via _delegate_py_bin). The same nodes pass once AGENT_PYTHON_BIN is the worktree interpreter, which is the command the brief specifies. They are not a defect of 8b47cc7.

After both pairs: snapshot porcelain empty, snapshot .mam/shim/herdr absent, control-plane main still 6b00b6e with empty porcelain, control-plane shim mtime 1791475620, ~/.config/herdr/config.toml mtime 1784456394, directory mtime 1791382452.

The writer this head leaves in place

tests/test_o1_rebuttal.py:68 and :72 (test_v3_rebut_option_parsing_and_budget) run bash on the checkout run_loop.sh (--help, and --max-rebut abc) with no env. The module has no own_workspace_root mark. run_loop.sh:17 sources lib.sh before option parsing, and lib.sh:3088 rewrites $WORKSPACE_ROOT/.mam/shim/herdr. With the variable unset, lib.sh:26 derives this checkout. W1's node list does not name this test, so the new regression does not catch it.

Add pytestmark = pytest.mark.usefixtures("own_workspace_root") to tests/test_o1_rebuttal.py, the same opt-in test_loop_cli.py uses, and add tests/test_o1_rebuttal.py::test_v3_rebut_option_parsing_and_budget to NODES in tests/test_issue17_workspace_root_isolation.py. Passing WORKSPACE_ROOT set to a temp directory on those two subprocess.run calls is the same fix at the call site.

Survey — same pattern, already pinned

These execute lib.sh, run_loop.sh, or a skill script that sources lib.sh, and they already give that shell a temp root or they do not execute the source. They stay as they are.

Location Why the checkout shim is not the target
test_issue17_item4_decisions.py:224 _env sets WORKSPACE_ROOT to the helper tmp
test_issue21_worktree_prereqs.py:547 _mutate sets the temp tree; test_t2_8 sets mam_sandbox
test_workspace_scope.py:32 subprocess env sets the test workspace
test_issue33_budget.py:47 helper home
test_issue18_liveness_contract.py:58 helper cwd; _submit uses that env
test_issue36_prompt_transport.py:61 and :537 helper home, and the shell loop sets ctl
test_issue21_phase2_loop_a.py:49 temp root
test_issue21_loop_b.py:236 tmp_path
test_issue6_turn_handshake.py:29 cwd
test_issue14_delivery_ack.py:36 cwd
test_issue26_envfile.py:76, test_issue26_ints.py:124 helper home
test_c1_tui_readiness.py:19 temp directory
test_layout.py:318 the script exports the test workspace
test_issue19_subscriber_daemon_lifecycle.py:138 the only _run_helper caller uses mam_sandbox
test_issue34_job_tree.py:590 copies the process env after mam_sandbox has set the root
test_issue10_13_purge.py:149 _run_onboard callers request mam_sandbox, so the copied env already names the sandbox
test_issue20_state_isolation.py:201 sets WORKSPACE_ROOT to the temp repo the test is about
test_issue9_server_env.py, test_issue45_restore.py callers use mam_sandbox / rig, which has already set the root
test_issue30_stall_watch.py:124 _env itself does not assign the root; wrapper calls go through mam_sandbox or test_issue18._submit
test_o2_race_free_lock.py:229 bash -n does not execute run_loop.sh
test_issue5_phase2a_loop.py:754 the source line is text inside an assertion
test_tier3_integration.py:337 the source is a debug string, and that env sets the temp root
test_deploy_freshness.py:473 reads run_loop.sh as text

Marked modules listed above are covered by own_workspace_root. test_issue21_acceptance.py runs a run_loop.sh inside a temp git repo from its ctl fixture.

[VERDICT: NOT PASS]