Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
121 lines
11 KiB
Markdown
121 lines
11 KiB
Markdown
# Issue #60 2차 리뷰 — NOT PASS
|
|
|
|
이전 자식 legacy 데이터 누락은 실제 OpenCode 2.0.32에서도 해결됐다. 새 기능의 행동 테스트와 수정 전 실패도 확인했다. 그러나 새 XDG_DATA_HOME 읽기가 테스트의 환경 격리 목록에 반영되지 않아 원본 HEAD 검사 1건이 실패하며, 실제로 sandbox 밖 DB 경로를 선택하는 회귀가 있다. 국소 수정이 필요하고 재계획은 필요하지 않다.
|
|
|
|
## Identity
|
|
|
|
- Job: `c4e96018`; loop: `loop-20261018T064723Z-79133f`
|
|
- Base: `74333f87d0c02888c2b5b281e912d97e74348c01`
|
|
- Head: `cb5f439cce41a2741a6b9974a308a765e50f00ad`
|
|
- Tree: `cfec95403cbde10a6340b9007e1246c83be461cd`
|
|
- 검증 경로: `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261018T064723Z-79133f/1`
|
|
|
|
시작/종료의 HEAD/tree가 일치하고 검증 트리는 clean이다. creator 트리에서 실행·수정하지 않았다. 제품 원본과 원본 테스트는 수정하지 않았다. 아래 대조군만 검증 트리의 별도 git archive 복사본에서 변경했다. 판정은 base..head 누적 변경을 대상으로 한다.
|
|
|
|
## 수정 필요 — P1: 상속된 XDG_DATA_HOME이 테스트 sandbox 밖을 가리킴
|
|
|
|
**근거:** `.agents/skills/lib_py/agents/adapters/opencode.py:90`~`:93`은 HOME과 ctx.home_dir가 같으면 ambient XDG_DATA_HOME을 반환하도록 새로 변경됐다. 그런데 `tests/_agent_home_guard.py`의 `DROPPED_VARS`에는 XDG_DATA_HOME이 없고, `tests/conftest.py::_isolate_agent_homes`는 그 목록만 제거한다. `mam_sandbox`는 HOME/HOME_DIR를 임시 홈으로 바꾸지만 XDG_DATA_HOME은 제거하지 않는다. 따라서 이 시점에 own=True가 되면서 호출자의 외부 XDG data root가 그대로 테스트에 유입된다. `watch_dirs()` 역시 opencode의 기본 HOME/.local/share 경로만 감시한다.
|
|
|
|
**원본 HEAD에서 관측:** `tests/test_issue55_agent_homes.py:61`의 `test_l1_every_path_variable_the_code_reads_is_accounted_for`가 다음 값으로 실패했다.
|
|
|
|
```text
|
|
path variables the adapters/lib.sh read but conftest._isolate_agent_homes does not drop: ['XDG_DATA_HOME']
|
|
```
|
|
|
|
이는 새 파일 생성에 대한 외부 에이전트 활동 오탐이 아니라, 소스 코드가 읽는 변수와 격리 목록의 결정적 불일치다. 테스트 프로세스 시작 전에 XDG를 unset한 이번 리뷰 환경에서도 목록 검사는 실패한다.
|
|
|
|
**안전한 재현 절차:**
|
|
|
|
1. 검증 HEAD 복사본에 `mam_sandbox` fixture를 사용하는 짧은 pytest 테스트를 둔다.
|
|
2. pytest 호출자의 HOME은 임시 H, XDG_DATA_HOME은 별개의 임시 D로 설정한다. 실제 사용자 경로는 사용하지 않는다.
|
|
3. 테스트 안에서 OpenCode 어댑터의 `_bin`을 None으로 대체해 실제 CLI 실행을 막고, `DiscoveryContext(workspace=sandbox, agent_name='opencode', home_dir=sandbox)`로 `_resolve_db`를 호출한다.
|
|
4. 결과가 sandbox 내부인지 검사한다.
|
|
|
|
**기대:** 각 테스트가 명시적으로 설정하지 않은 ambient XDG는 격리되어 `<sandbox>/.local/share/opencode/opencode.db`가 선택돼야 한다.
|
|
|
|
**실제:**
|
|
|
|
```text
|
|
sandbox: /private/tmp/review-c4e96018-i02oz_61/pytest-isolation/test_inherited_data_home_does_0
|
|
resolved_db: /tmp/review-c4e96018-i02oz_61/ambient-data/opencode/opencode.db
|
|
```
|
|
|
|
추가 탐침은 **1 failed in 0.09s**. 실제 파일 쓰기나 삭제를 하지 않고 경로 선택만 확인했다. 일반 개발자 환경에서 D가 실제 XDG 디렉터리라면 어댑터/테스트가 그 DB를 읽거나 변경할 수 있으므로, 단순한 목록 테스트의 기대값 갱신으로 넘길 문제는 아니다. 실제 사용자 데이터 훼손이 관측됐다는 뜻은 아니다.
|
|
|
|
**수정 방향:** `DROPPED_VARS`에 XDG_DATA_HOME을 추가하여 테스트마다 제거하고, XDG 동작을 검증하는 테스트만 명시적으로 임시 XDG를 설정하게 한다. 세션 guard의 ambient 변수 기록 및 `watch_dirs()`도 새 data-root 계약을 반영하여 실제 XDG 경로를 감시하게 한다. `LEFT_ALONE`에만 추가하거나 목록 단언을 약화하면 sandbox 경로 문제는 남는다.
|
|
|
|
**대조군:** 임시 복사본에서 DROPPED_VARS에 XDG_DATA_HOME만 추가하자 위 경로 탐침과 기존 L1 검사 두 건이 **2 passed in 0.09s**로 바뀌었다. 이는 격리 누락 원인의 확인이며, 원본 수정이나 전체 검증 완료를 의미하지 않는다. 감시 경로 보완도 함께 필요하다.
|
|
|
|
## 이전 지적 및 이번 변경 확인
|
|
|
|
### 자식 legacy 데이터 삭제와 residual
|
|
|
|
`purge_artifacts`가 CLI 호출 전에 `_descendants`로 session_v2와 legacy session의 parent_id를 따라 모든 깊이의 ID를 모은다. 중복/순환은 found와 root 비교로 제한한다. legacy message/messages/part/parts/todo/session_share/session/sessions를 해당 ID 집합에 대해 삭제한다. 관련 없는 세션은 집합에 넣지 않는다.
|
|
|
|
이전 리뷰의 실제 재현을 새로운 임시 홈에서 반복했다. 실제 2.0.32 standalone API로 부모 P, 자식 C, 무관한 O를 생성하고, 임시 DB에 P/C/O의 legacy session/message를 추가한 뒤 현재 어댑터로 P를 purge했다.
|
|
|
|
- session_v2와 legacy session 모두 **O만 남았다**.
|
|
- P와 C의 residual은 각각 `[]`였다.
|
|
- purged는 부모/자식 네이티브 삭제와 `and 1 child session(s)` legacy 정리를 보고했다.
|
|
- 원래 차단 지적이었던 C의 legacy 행 생존은 재현되지 않았다.
|
|
|
|
새 테스트는 손자 포함 삭제, 무관한 세션 보존, 삭제 실패 시 `[child session ...]` residual도 확인한다. todo의 변경된 계약은 issue31 테스트가 삭제됨을 단언하며 통과했다. session_share도 구현의 명시적 legacy 집합에 포함됐다.
|
|
|
|
### 서비스 보유자 / XDG / resident / drift C
|
|
|
|
- 공유 DB에 대한 서비스 보유자 제외, 일반 보유자 및 UUID 명령행 보유자 유지 테스트 통과.
|
|
- 호출자 자신의 HOME에서 XDG_DATA_HOME 존중, 다른 HOME에서는 그 홈의 기본 data root 사용 테스트 통과. 제품 경로 계약은 의도대로 바뀌었으나 위 테스트 격리 갱신이 누락됐다.
|
|
- resident_children=('opencode',) 테스트 통과. standalone 서버 자체는 idle 판단에서 제외하지만 그 아래 작업 프로세스는 계속 탐색하여 작업으로 본다.
|
|
- `test_comp_opencode_create_and_reconcile_and_stop` 통과. 더 최신 자식이 있어도 실제 reconcile.sh 경로가 부모를 확정한다. 이는 문자열 검사만이 아니라 임시 fixture 안의 행동 검증이다.
|
|
- 실제 바이너리 계약 테스트는 `_bin`을 발견한 바이너리로 고정하여 임시 HOME에서 PATH에 의존하지 않도록 바뀌었고 실제 2.0.32로 통과했다.
|
|
|
|
**수정 전 대조군:** 현재 `test_issue60_opencode_v2.py`를 이전 HEAD `6b4b96f`의 별도 archive에 넣어 아래 다섯 노드만 실행했다. 모두 수정 전 동작 때문에 실패했다(**5 failed, 10 deselected in 2.57s**).
|
|
|
|
1. test_purge_takes_the_1x_rows_of_the_child_sessions_too
|
|
2. test_a_failed_delete_reports_the_child_sessions_left
|
|
3. test_the_background_service_alone_does_not_hold_a_session
|
|
4. test_the_data_dir_follows_the_callers_xdg_data_home_only_for_its_own_home
|
|
5. test_the_standalone_server_is_a_resident_child_but_its_tools_are_work
|
|
|
|
같은 다섯 노드는 현재 HEAD에서 통과했다. drift-C 필터 제거 변형은 이번 리뷰에서 별도로 반복하지 않았으며 현재 행동 테스트 통과를 확인했다.
|
|
|
|
## 원본 HEAD 실행 결과
|
|
|
|
다음 6개 모듈과 tier2 노드 1개 실행:
|
|
|
|
- tests/test_issue60_opencode_v2.py
|
|
- tests/test_a4_adapter_contract.py
|
|
- tests/test_issue31_purge_residual.py
|
|
- tests/test_issue42_skill_blocks.py
|
|
- tests/test_issue55_agent_homes.py
|
|
- tests/test_issue10_13_purge.py
|
|
- tests/test_tier2_component.py::test_comp_opencode_create_and_reconcile_and_stop
|
|
|
|
**1 failed, 186 passed, 1 skipped in 91.16s**, pytest exit 1. 유일한 실패는 위 XDG 변수 목록 검사다. skip은 untracked `.claude/skills`가 검증 worktree에 없는 issue42 검사이며, 실제 OpenCode 테스트는 skip되지 않았다.
|
|
|
|
변경 Python 파일의 시스템 Python 3.9 AST 구문 검사, 변경 셸 파일의 `/bin/bash -n`, base..head `git diff --check` 모두 통과. 전체 스위트는 오케스트레이터가 별도로 실행하는 범위라 반복하지 않았다.
|
|
|
|
## 안전 범위와 남은 제한
|
|
|
|
- 지정 venv, `PYTHONDONTWRITEBYTECODE=1`, `-p no:cacheprovider`, 검증 트리 밖 basetemp로 실행했다. HERDR/MAM/WORKSPACE/CLAUDE_CONFIG_DIR 바인딩은 제거했다.
|
|
- 실제 바이너리는 HOME/TMPDIR 임시화, XDG_* unset, 서버 사용 명령은 --standalone으로만 실행했다. `_run`이 설정하는 XDG_DATA_HOME도 해당 임시 홈 아래다. 모델 프롬프트나 실제 사용자 서비스/API 호출은 하지 않았다.
|
|
- XDG ambient 재현에는 임시 경로와 `_bin=None`만 사용했다. 실제 opencode 데이터나 실제 DB는 건드리지 않았다.
|
|
- 종료 후 OpenCode 프로세스는 기존 사용자 서비스 PID 86524만 남았다. 그 서비스는 제어하지 않았다. 시험 프로세스가 남지 않았다.
|
|
- 1차 리뷰에서 확인한 create→drift C→status→stop→resume 연결 및 실제 PTY의 `/exit` 성공 근거는 `.mam/jobs/614b6263/codex-reports/report-final.md`에 있다. 이번에는 새 tier2 행동 테스트와 실제 API 삭제 계약을 재검증했고, 실제 모델 첫 메시지는 보내지 않았다.
|
|
- status.sh의 `resume_on_disk`는 아직 기본 HOME 경로를 직접 찾는다. 사용자 지정 XDG 저장소에 대한 status 표시는 어댑터의 새 경로 지원과 다를 수 있다. 이는 1차 리뷰에서도 있던 비차단 후속 사항이며 이번 NOT PASS의 추가 사유로 삼지 않았다.
|
|
- Linux 실제 실행 결과는 없다. 구문 및 플랫폼 코드 경로를 검토했고 Linux 런타임/서비스 동작 검증은 예정된 후속 작업으로 남는다.
|
|
|
|
## 증거 파일
|
|
|
|
`/tmp/review-c4e96018-i02oz_61` (실경로 `/private/tmp/...`):
|
|
|
|
- head.log — 원본 결과 및 유일한 실패.
|
|
- before-fix.log — 이전 HEAD에서 새 회귀 검사 5건 실패.
|
|
- real_probe.py, real-probe/result.json — 실제 2.0.32 부모/자식 legacy 정리 확인.
|
|
- isolation.log — sandbox 밖 임시 XDG data root 선택 재현.
|
|
- isolation-control.log — 임시 격리 목록 보완 후 2건 통과.
|
|
- isolation-probe/tests/test_review_xdg_isolation.py — 읽기만 하는 재현 테스트. 이 복사본의 guard에는 대조군 변경이 적용돼 있으므로 원본 재현 시 HEAD의 guard를 사용해야 한다.
|
|
- processes-after.json — 시험 후 OpenCode 프로세스 확인.
|
|
|
|
[VERDICT: NOT PASS]
|