release(v4.9.3): u1e no longer writes the checkout's .mam/shim/herdr - archive the review reports and bump the lockstep version

Review loop (--review-only, --all-reviewer): round 1 (@3a30d7d) 4/4 PASS;
each reviewer reproduced W1 failing on the pre-fix tree. Seven #50/#55 and
leak modules (112 tests) from a clean worktree left no .mam in the
checkout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-10-13 12:33:02 +09:00
1 parent 3a30d7da89
commit 121eab71d4
24 files changed
+299 -25

No files matched your search

@@ -0,0 +1,14 @@
{
"loop_id": "loop-20261013T025803Z-c69408",
"base_sha": "42e2a7ea9bd3e2770ed8626d789193428ca3aa8b",
"head_sha": "3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db",
"tree_sha": "bd43e4642f0109091f5f497b2703dcbaf8e2616b",
"review_commit": "3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db",
"reviewer": "planner-reviewer-claude-01",
"job_id": "495a1373",
"verdict": "PASS",
"transport": "herdr",
"evidence": "review-tree",
"isolation": "worktree",
"verified_at": "2026-10-13T03:32:19Z"
}
@@ -0,0 +1,34 @@
# 테스트 격리 수정(u1e 의 WORKSPACE_ROOT) 리뷰 — 커밋 3a30d7d (job 495a1373)
- 리뷰어: planner-reviewer-claude-01 (Reviewer)
- 리뷰 identity: loop-20261013T025803Z-c69408 / base 42e2a7ea .. head 3a30d7da (커밋 1개, 테스트 2 파일) / tree bd43e464
- 검증 경로: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1` — HEAD·tree 일치, `git status --porcelain` 0 줄, **`.mam` 없음**(시작·종료 모두). 본 체크아웃 HEAD 42e2a7e(main) 변동 없음.
- 실행 안전: 모든 실행은 `env -i`(PATH/HOME/LANG/TMPDIR 만)로 브리프의 `-u` 목록을 포함한 라우팅 변수를 비웠고, basetemp 는 scratchpad 아래(이름에 "shim" 없음). 수정 전 재현은 검증 경로를 `git clone --no-hardlinks` 한 임시 복사본에서만 했고 거기 생긴 `.mam` 은 지웠다. 검증 경로에서 돌린 것은 W1/W2 뿐이며(W1 은 체크아웃을 tmp 로 복사해 그 안에서 노드를 돌린다), 끝난 뒤 검증 경로에 `.mam` 이 없음을 확인했다. 실제 `.mam`·`.mam.env`·모니터·실제 서버·에이전트 홈 미접촉.
## 1. 원인과 수정
- 원인(1차 README 리뷰에서 재현한 그대로): `test_u1e` 는 래퍼를 `subprocess.run(["bash", WRAPPER, "submit", …, "--dry-run"], env=dict(os.environ, HOME=…, MAM_ENV_FILE=…), cwd=tmp_path)` 로 부르는데, 래퍼가 source 하는 `lib.sh` 는 `WORKSPACE_ROOT` 가 없으면 체크아웃을 기본값으로 삼고(`lib.sh:26`) `_init_herdr_isolation` 이 `$WORKSPACE_ROOT/.mam/shim/herdr` 를 쓴다(`lib.sh:150-154`). conftest 의 `pytest_configure` 는 다른 체크아웃을 가리키는 `WORKSPACE_ROOT` 만 떨어뜨리므로 pane 밖에서는 변수가 없고, 체크아웃이 기본값이 된다.
- 수정(`tests/test_issue50_dispatch.py:167-170`): env 에 `WORKSPACE_ROOT=str(tmp_path)` 추가 → shim 은 `tmp_path/.mam/shim/herdr` 에 쓰인다. `tests/test_issue17_workspace_root_isolation.py:43`: 이 노드를 W1 `NODES` 에 추가. 두 파일 `py_compile` 통과. 같은 파일의 다른 테스트는 `decide.*` 를 in-process 로 부르므로(`_register`/`_edit`/`_event` 헬퍼) shim 을 쓰지 않는다 — 아래 §2 의 파일 전체 실행이 이를 확인.
## 2. 실행 결과
| 실험 | 결과 |
|---|---|
| **수정 전 복사본**(검증 경로 clone + `test_issue50_dispatch.py` 를 base 42e2a7e 로 되돌림): u1e 단독 | 1 passed, 복사본에 **`.mam/shim/herdr` 생성** — 원인 재현 |
| **수정 전 복사본**: W1(새 NODES 포함) | **FAIL**: `AssertionError: tests wrote into the checkout they ran in: ['.mam/shim', '.mam/shim/herdr']` — W1 이 이 노드로 회귀를 잡는다(복사본 안의 tmp 복사본에만 쓰였고 복사본 자체에는 `.mam` 없음) |
| **head, 검증 경로**: `test_issue17_workspace_root_isolation.py` (W1+W2) | **2 passed**, 검증 경로 `.mam` 없음 |
| **head 복사본**(`git archive`): `test_issue50_dispatch.py` 전체 | **49 passed**, 복사본 `.mam` 없음 |
W1 이 잡는 원리: W1 은 복사본에 `WORKSPACE_ROOT=<복사본>` 을 주고 노드를 돌린다. 수정 전 u1e 는 `os.environ` 을 그대로 물려주므로 래퍼가 그 복사본에 shim 을 쓰고, 수정 후는 `tmp_path` 로 덮어써 복사본에 아무것도 남지 않는다. pane 밖(변수 없음)에서도 lib.sh 의 체크아웃 기본값 때문에 같은 결과이므로 두 환경 모두를 대표한다.
## 3. 같은 결함이 남은 테스트가 있는가
- 정적: 래퍼·`lib.sh`·`run_loop.sh`·`create_session.sh`·`reconcile.sh`·`stop/resume_session.sh` 를 subprocess 로 부르는 테스트 파일 59 개를 추려 `mam_sandbox`/`own_workspace_root`/`WORKSPACE_ROOT` 참조 여부를 봤다. 참조가 하나도 없는 7 파일과, 참조가 적어 테스트별 누락이 있을 수 있는 18 파일(W1 NODES 에 없는 것 위주)을 골랐다.
- 경험적: 그 **25 파일을 head 의 `git archive` 복사본에서 파일마다 따로** 돌리고 매번 복사본의 `.mam` 을 봤다 — `test_b7_diff_untracked`, `test_deploy_freshness`, `test_deploy_layout`, `test_issue42_skill_blocks`, `test_issue45_herdr_config`, `test_issue5_phase0_contracts`, `test_local_mqtt`, `test_issue14_delivery_ack`, `test_issue18_liveness_contract`, `test_issue6_turn_handshake`, `test_issue26_envfile`, `test_issue26_ints`, `test_issue33_budget`, `test_layout`, `test_workspace_scope`, `test_a4_adapter_contract`, `test_issue45_restore`, `test_sanitize_and_mock_errors`, `test_sanity`, `test_issue12_registry_secrets`, `test_issue21_acceptance`, `test_issue15_registry_isolation`, `test_issue20_state_isolation`, `test_issue36_prompt_transport`, `test_issue34_job_tree`: 전부 통과(skip 은 tomllib/nats-docker 류), **`.mam` 생성 0 건**.
- 이전 리뷰들에서 같은 방식으로 돌린 파일들(leak·startup_path·shim_contract·issue17/9/45, scenario4, pane_lock, issue50_dispatch_herdr 등)도 검증 경로에 `.mam` 을 남기지 않았고, `.mam/shim` 이 생긴 경우는 모두 `test_issue50_dispatch.py` 가 포함된 실행뿐이었다(u1e). 즉 이 결함은 u1e 하나였고, 수정으로 닫혔다.
## 4. 잔류물
- clone 복사본·archive 복사본·basetemp·로그는 scratchpad 아래. 검증 경로는 깨끗(`.mam` 없음, `git status` 0 줄). #55 가드 오탐은 이번 실행에서 나오지 않았다.
## 결론
원인 진단이 맞고, `WORKSPACE_ROOT=tmp_path` 가 정확한 최소 수정이며, W1 은 수정 전 트리에서 이 노드로 실패하고 수정 후 통과한다. 다른 테스트에서 같은 누출은 25 파일의 파일별 실행으로 재현되지 않았다.
[VERDICT: PASS]
@@ -0,0 +1,14 @@
{
"loop_id": "loop-20261013T025803Z-c69408",
"base_sha": "42e2a7ea9bd3e2770ed8626d789193428ca3aa8b",
"head_sha": "3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db",
"tree_sha": "bd43e4642f0109091f5f497b2703dcbaf8e2616b",
"review_commit": "3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db",
"reviewer": "planner-reviewer-codex-01",
"job_id": "8db89564",
"verdict": "PASS",
"transport": "herdr",
"evidence": "review-tree",
"isolation": "worktree",
"verified_at": "2026-10-13T03:32:19Z"
}
@@ -0,0 +1,54 @@
# 테스트 격리 수정 리뷰 — 8db89564
판정: PASS. u1e의 체크아웃 쓰기 원인을 정확히 수정했고, 추가된 W1 노드가 그 수정의 제거를 실제로 검출합니다. 차단할 결함은 발견하지 못했습니다.
## Identity 및 변경 범위
- loop: `loop-20261013T025803Z-c69408`
- base: `42e2a7ea9bd3e2770ed8626d789193428ca3aa8b`
- head: `3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db`
- tree: `bd43e4642f0109091f5f497b2703dcbaf8e2616b`
- 검증 경로: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1`
- 변경은 테스트 두 파일뿐입니다. 시작과 종료 시 HEAD/tree 일치 및 clean 상태를 확인했고, 종료 시 리뷰 체크아웃에 `.mam`이 없음을 확인했습니다. 리뷰 트리나 creator 작업 트리를 수정하지 않았습니다.
## 원인과 수정 검토
`tests/test_issue50_dispatch.py:169–170`은 래퍼 subprocess에 `WORKSPACE_ROOT=str(tmp_path)`를 명시하여 상속된 루트와 체크아웃 기본값을 모두 덮어씁니다.
원인은 `.agents/skills/lib.sh:26`이 변수 미설정 시 라이브러리 위치에서 체크아웃 루트를 계산하고, 3168행에서 `_init_herdr_isolation`을 무조건 호출하기 때문입니다. 이 함수의 150–157행은 `$WORKSPACE_ROOT/.mam/shim`을 만들고 shim 파일을 씁니다. 래퍼 `.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job:53`은 dry-run 처리를 하기 전에 이 라이브러리를 source합니다. 따라서 기존의 임시 HOME/cwd 설정만으로는 격리가 되지 않으며, 이번 명시적 루트 설정이 필요한 최소 수정입니다.
u1e의 원래 검증(미전달 dry-run 레코드는 admission을 막지 않고, running 또는 일반 pending 레코드는 막음)은 175–186행에 그대로 남아 있습니다. 새 환경 설정이 테스트를 우회하거나 assertion을 약화하지 않습니다.
`tests/test_issue17_workspace_root_isolation.py:43`의 노드 추가는 적절합니다. W1은 복사본의 루트를 의도적으로 상속시키고(62행), 내부 테스트가 모두 실행·통과했는지 검사한 뒤(67–69행), 복사본 `.mam`의 생성 자체를 확인합니다(70–72행). 이 때문에 u1e가 환경을 다시 덮어쓰지 않으면 회귀를 잡습니다.
## 실행 검증
아티팩트 루트: `/tmp/review-8db89564-gw9g__7l`
1. **수정된 리뷰 트리: 51 passed, 68.05초, 종료 코드 0.**
- 대상: `tests/test_issue50_dispatch.py` 전체와 `tests/test_issue17_workspace_root_isolation.py` 전체(W1/W2 포함).
- 로그: `/tmp/review-8db89564-gw9g__7l/positive.log`.
- W1 내부 노드 실행의 통과·누수 부재 검사도 통과했습니다.
2. **수정 제거 대조군: W1이 기대한 위치에서 실패, 30.06초, pytest 종료 코드 1.**
- 현재 HEAD의 추적 파일을 임시 체크아웃으로 복사했습니다. 새 W1 NODES는 유지하고, `tests/test_issue50_dispatch.py`만 base 버전으로 교체했습니다.
- W1의 내부 테스트 실행 및 통과 수 검사를 지난 뒤, 71행에서 `tests wrote into the checkout they ran in: ['.mam/shim', '.mam/shim/herdr']`로 실패했습니다.
- 이는 단순 실행 오류가 아니라 원래 누수에 대한 회귀 검출입니다.
- 로그: `/tmp/review-8db89564-gw9g__7l/negative.log`.
- 수정 전 코드와 누수가 생긴 중첩 복사본을 포함한 negative 임시 트리는 검증 뒤 삭제했습니다.
3. **`git diff --check 42e2a7e..HEAD`: 성공.**
두 실행은 요구된 `env -u HERDR_SOCKET_PATH -u HERDR_PANE_ID -u HERDR_TAB_ID -u HERDR_WORKSPACE_ID -u HERDR_SESSION_NAME -u HERDR_SESSION -u WORKSPACE_ROOT -u MAM_ENV_FILE`을 적용했습니다. 외부 가상환경 Python, `PYTHONDONTWRITEBYTECODE=1`, `-p no:cacheprovider` 및 리뷰 트리 밖 basetemp를 사용했습니다. 시작 환경의 HOME/XDG_CONFIG_HOME도 임시 경로로 설정하고 에이전트·제어 평면 관련 상속 변수를 제거했습니다. 실제 서버나 에이전트를 실행하지 않았으며, #55의 다른 에이전트 대화 파일 관련 오탐도 발생하지 않았습니다.
## 유사 경로 점검 및 한계
래퍼 실행 및 `source lib.sh` 검색 결과의 주요 테스트 헬퍼를 추가로 검토했습니다. 살펴본 경로에서는 동일한 누락을 발견하지 못했습니다.
- `test_issue18_liveness_contract.py:54–60`, `test_issue14_delivery_ack.py:32–36`, `test_issue33_budget.py:42–47`, `test_issue34_job_tree.py:50–64`, `test_issue36_prompt_transport.py:47–61`, `test_issue26_envfile.py:73–76`, `test_issue26_ints.py:127–129`: 명시적으로 임시 WORKSPACE_ROOT 설정.
- `test_issue17_item4_decisions.py:225–233`, 689–699: 래퍼 source/submit 환경에서 임시 루트를 고정.
- `test_issue6_fail_closed.py:21–23`, `test_version_consistency.py:11–13`, `test_b19_headless_reconcile_fixes.py:11–13`, `test_b8_send_keys_verification.py:5–7`: 모듈의 `own_workspace_root` 픽스처 사용.
- `test_issue21_worktree_prereqs.py`의 `_dry_submit` 호출부 92–149행은 루트를 전달하며, legacy loop 테스트 398–408행은 샌드박스 루트를 명시합니다.
- `test_issue20_state_isolation.py:192–202`, `test_workspace_scope.py:30–32`, `test_layout.py:318`: 호출 환경에서 루트 지정. `test_a4_adapter_contract.py`의 `test_wait_for_tui_ready_missing_tokens_diagnostic` 및 `test_issue5_phase1_delegation.py`의 `_run_helper`는 샌드박스 픽스처·복사본을 사용합니다.
전체 테스트 스위트를 실행하거나 모든 동적 호출 경로의 무누수를 증명한 것은 아닙니다. 이번 수정에 직접 관련된 51건 및 W1의 수정 제거 검증으로 동작과 회귀 방지 효과를 확인했습니다.
[VERDICT: PASS]
@@ -0,0 +1,14 @@
{
"loop_id": "loop-20261013T025803Z-c69408",
"base_sha": "42e2a7ea9bd3e2770ed8626d789193428ca3aa8b",
"head_sha": "3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db",
"tree_sha": "bd43e4642f0109091f5f497b2703dcbaf8e2616b",
"review_commit": "3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db",
"reviewer": "reviewer-agy-01",
"job_id": "4be97273",
"verdict": "PASS",
"transport": "herdr",
"evidence": "review-tree",
"isolation": "worktree",
"verified_at": "2026-10-13T03:32:19Z"
}
@@ -0,0 +1,86 @@
# Code Review Report: `fix/u1e-workspace-root` (Test Isolation Fix for `test_u1e`)
- **Job ID**: `4be97273`
- **Reviewer**: `agy` (Session: `herdr:reviewer-agy-01`, Role: `Reviewer`)
- **Date**: 2026-10-13
- **Target Branch**: `fix/u1e-workspace-root`
- **Base Commit**: `42e2a7ea9bd3e2770ed8626d789193428ca3aa8b`
- **Head Commit**: `3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db`
- **Verification Path**: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1`
- **Verification Environment**: macOS (Darwin 24.3.0, arm64), Python 3.9.6, pytest-8.4.2
---
## 1. Executive Summary
This review evaluated commit [`3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1), which fixes a test workspace isolation leak discovered during earlier review runs in [`tests/test_issue50_dispatch.py::test_u1e_a_dry_run_record_is_not_an_open_job`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue50_dispatch.py#L162-L174) and adds regression coverage to [`tests/test_issue17_workspace_root_isolation.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue17_workspace_root_isolation.py).
The root cause was confirmed, the fix was verified both positively and negatively (reproducing failure on base, success on head), and an exhaustive audit across `tests/` confirmed no other tests suffer from this defect.
**Verdict: PASS**.
---
## 2. Root Cause Analysis
In [`tests/test_issue50_dispatch.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue50_dispatch.py), `test_u1e_a_dry_run_record_is_not_an_open_job` invokes `subprocess.run` on `WRAPPER` (`multi-agent-mux-delegate-job`).
1. `WRAPPER` sources [`.agents/skills/lib.sh`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/.agents/skills/lib.sh).
2. In `lib.sh:26`, if `WORKSPACE_ROOT` is unset in the execution environment, it defaults to:
```bash
WORKSPACE_ROOT="${WORKSPACE_ROOT:-$(cd "$SKILL_DIR/../.." && pwd)}"
```
which points to the repository checkout root.
3. In `lib.sh:149-155` and `lib.sh:3168`, `_init_herdr_isolation` automatically creates and populates `$WORKSPACE_ROOT/.mam/shim/herdr`.
4. Because `u1e` previously created a custom `env` containing `HOME` and `MAM_ENV_FILE` but omitted `WORKSPACE_ROOT`, executing `u1e` wrote directly into `<checkout>/.mam/shim/herdr`.
---
## 3. Verification of Fix & Regression Coverage
### 3.1 The Fix
In [`tests/test_issue50_dispatch.py:169-170`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue50_dispatch.py#L169-L170), `WORKSPACE_ROOT=str(tmp_path)` was added to `env`:
```python
env = dict(os.environ, HOME=str(tmp_path), MAM_ENV_FILE=str(tmp_path / ".mam.env.absent"),
WORKSPACE_ROOT=str(tmp_path))
```
This guarantees that any `.mam/shim` creation is strictly contained within `tmp_path`.
In [`tests/test_issue17_workspace_root_isolation.py:43`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue17_workspace_root_isolation.py#L43), `"tests/test_issue50_dispatch.py::test_u1e_a_dry_run_record_is_not_an_open_job"` was appended to `NODES`. Test W1 copies the checkout into a temporary sandbox and executes every node in `NODES`, asserting that no `.mam` directory is created in the copy.
### 3.2 Negative Reproduction (Pre-fix Validation)
In an isolated temporary clone checked out at base commit `42e2a7ea9bd3e2770ed8626d789193428ca3aa8b`, `u1e` was added to `NODES` before the fix. Running W1 failed immediately with:
```text
AssertionError: tests wrote into the checkout they ran in: ['.mam/shim', '.mam/shim/herdr']
assert ['.mam/shim',...m/shim/herdr'] == []
FAILED tests/test_issue17_workspace_root_isolation.py::test_w1_no_test_writes_into_the_checkout_it_runs_in
```
This confirms that W1 effectively catches the defect.
### 3.3 Positive Execution (Post-fix Validation)
Ran [`tests/test_issue17_workspace_root_isolation.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue17_workspace_root_isolation.py) in the verification directory with sanitized environment and isolated `--basetemp`:
```text
tests/test_issue17_workspace_root_isolation.py .. [100%]
============================== 2 passed in 30.47s ==============================
```
Verified that no `.mam` was created in the verification tree (`ls: .mam: No such file or directory`, git status clean).
---
## 4. Codebase Audit for Similar Invocations
An automated search across all test modules in [`tests/`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests) identified all other locations where `WRAPPER` or `lib.sh` are invoked:
- [`test_issue14_delivery_ack.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue14_delivery_ack.py): `_base_env` explicitly sets `env["WORKSPACE_ROOT"] = str(cwd)`.
- [`test_issue17_item4_decisions.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue17_item4_decisions.py): `_env` explicitly sets `WORKSPACE_ROOT=str(tmp_path)`.
- [`test_issue18_liveness_contract.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue18_liveness_contract.py): `_env` explicitly sets `env["WORKSPACE_ROOT"] = str(cwd)`.
- [`test_issue26_envfile.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue26_envfile.py): `_env` explicitly sets `"WORKSPACE_ROOT": str(home)`.
- [`test_issue26_ints.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue26_ints.py): `_base_env` explicitly sets `"WORKSPACE_ROOT": str(home)`.
- [`test_issue33_budget.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue33_budget.py): `_env` explicitly sets `env["WORKSPACE_ROOT"] = str(home)`.
- [`test_issue50_dispatch_herdr.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue50_dispatch_herdr.py): `Box.env` explicitly sets `WORKSPACE_ROOT=str(self.ctl)`.
- [`test_issue6_turn_handshake.py`](file:///var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261013T025803Z-c69408/1/tests/test_issue6_turn_handshake.py): `_run_lib_sh` and `_run_wrapper` explicitly set `run_env["WORKSPACE_ROOT"] = str(cwd)`.
- Modules using `mam_sandbox` or `own_workspace_root` fixtures: correctly receive isolated `WORKSPACE_ROOT`.
No other un-isolated invocations exist in the test suite.
---
[VERDICT: PASS]
@@ -0,0 +1,14 @@
{
"loop_id": "loop-20261013T025803Z-c69408",
"base_sha": "42e2a7ea9bd3e2770ed8626d789193428ca3aa8b",
"head_sha": "3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db",
"tree_sha": "bd43e4642f0109091f5f497b2703dcbaf8e2616b",
"review_commit": "3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db",
"reviewer": "reviewer-grok-01",
"job_id": "db786989",
"verdict": "PASS",
"transport": "herdr",
"evidence": "review-tree",
"isolation": "worktree",
"verified_at": "2026-10-13T03:32:19Z"
}
@@ -0,0 +1,32 @@
# Review db786989 — u1e workspace root
Head `3a30d7da894cdac26b7ec054b4e66fe9fa3dc8db` (tree `bd43e4642f0109091f5f497b2703dcbaf8e2616b`) is the one commit `3a30d7d fix(tests): u1e gives the wrapper its own WORKSPACE_ROOT - it wrote the checkout's .mam/shim/herdr` on base `42e2a7ea9bd3e2770ed8626d789193428ca3aa8b`. Two test files, +5/−1. `git diff --check` is clean. Snapshot porcelain empty, and the snapshot has no `.mam` directory. Control-plane main is still `42e2a7ea9bd3e2770ed8626d789193428ca3aa8b`, porcelain empty.
## The cause and the pin
The wrapper sources `lib.sh` at startup (`.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job:53`). `lib.sh:26` sets `WORKSPACE_ROOT` to the checkout that contains the sourced file when the variable is unset, and the end of the file calls `_init_herdr_isolation` (`lib.sh:3168`). That function creates `$WORKSPACE_ROOT/.mam/shim` and writes `herdr` there (`lib.sh:149-156`). `pytest_configure` drops `WORKSPACE_ROOT` only when its real path is some other directory (`tests/conftest.py:153-155`), so an unset value stays unset and a value that names the checkout is kept.
`test_u1e_a_dry_run_record_is_not_an_open_job` now puts `WORKSPACE_ROOT` on the wrapper environment, set to that test's `tmp_path` (`tests/test_issue50_dispatch.py:167-173`). The shim is written under the test directory. The dry-run assertions in the same function are unchanged.
## W1 runs this node
`tests/test_issue17_workspace_root_isolation.py:43` adds the node to `NODES`. W1 copies the checkout, sets `WORKSPACE_ROOT` to that copy, runs the nodes, and fails if the copy grows a `.mam` directory (`:57-72`). On the fixed tree the node's own pin replaces the copy path, so the copy stays empty.
On an archive of base `42e2a7e` (outside the snapshot, deleted afterward) the old function does not mention `WORKSPACE_ROOT`. With the control-plane interpreter selected via `AGENT_PYTHON_BIN` (the same variable W1 exports at `:62`):
- `WORKSPACE_ROOT` unset: the node passed in 1.05s and the archive gained `.mam/shim/herdr`.
- `WORKSPACE_ROOT` set to the archive, which is what W1 would pass in: the node passed in 0.97s and the archive again had `.mam/shim/herdr`.
That directory is what W1's assertion at `:70-72` rejects. A passing node is still a W1 failure on the pre-fix tree.
On the snapshot, the fixed node passed in 1.05s. The shim was `test_u1e_a_dry_run_record_is_n0/.mam/shim/herdr` under a basetemp outside the snapshot. The snapshot had no `.mam`. W1 itself passed in 30.80s, which includes the inner count of at least `len(NODES)` and the empty-copy assertion. The snapshot still had no `.mam` after that run.
The wrapper's interpreter check runs after `source lib.sh` (`multi-agent-mux-delegate-job:69`). A first run that left `python3` without paho-mqtt exited 1 at that check on both trees, and the shim had already been written: on the archive in the unset and inherited-copy cases, and under the fixed test's basetemp. The snapshot stayed clean in that run too.
## Other wrapper callers
The same shape — a checkout wrapper or `lib.sh`, with the inherited root left unset — is already closed on the other callers checked here. Their child environments pin `WORKSPACE_ROOT` to the test directory (`tests/test_issue17_item4_decisions.py:229-231` and `:693-694`, `tests/test_issue26_ints.py:129`, `tests/test_issue36_prompt_transport.py:61`, `tests/test_issue30_stall_watch_stop.py:32`, `tests/test_issue6_fail_closed.py:38` and `:198`, `tests/test_issue50_dispatch_herdr.py:78`). The modules W1 already listed opt into `own_workspace_root` or that pin. `tests/test_issue30_stall_watch.py` builds its wrapper environment from `mam_sandbox` / `box`, which set `WORKSPACE_ROOT` before the copy (`tests/conftest.py:263`). No second unpinned checkout-wrapper call turned up.
No herdr server was started. `install.sh` was not run. User herdr config mtime stayed `1791858154`.
[VERDICT: PASS]