Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
44 lines
7.8 KiB
Markdown
44 lines
7.8 KiB
Markdown
# Issue #64 코드 리뷰 — 읽기 전용 에이전트 DB 조회
|
|
|
|
## 판정 요약
|
|
|
|
head `b0eccc530149bc6e9211727e19921665c7b80492`의 변경은 조회 전용 SQLite 경로를 공용 `_ro_connect`로 일관되게 바꾸며, 의도된 DELETE 경로와 MAM 자체 DB 쓰기는 보존합니다. 다른 워크스페이스 agy DB에 WAL/SHM이 생긴 원인 경로도 코드와 격리 재현 테스트로 확인했습니다.
|
|
|
|
검증은 브리프의 검증 트리 `/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1`에서만 수행했습니다. identity는 base `3ce5d5fdc108b0435e933a251fe1c1256b5d7973`, head `b0eccc530149bc6e9211727e19921665c7b80492`, tree `f5a412a3ee96465db7c4b3223d2d76e95716ddfa`이며, `git status --short`는 비어 있습니다. 소스 워크트리는 실행하거나 수정하지 않았습니다.
|
|
|
|
## 원인
|
|
|
|
원인은 `reconcile.sh`의 agy drift C 경로입니다. [reconcile.sh:786](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/multi-agent-mux-monitor/scripts/reconcile.sh:786)에서 대화 DB 디렉터리를 설정하고, [reconcile.sh:800](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/multi-agent-mux-monitor/scripts/reconcile.sh:800)에서 해당 홈의 모든 `*.db`를 훑은 뒤 [reconcile.sh:816](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/multi-agent-mux-monitor/scripts/reconcile.sh:816)에서 각 UUID에 `verify_session_uuid(..., mode="discover")`를 호출합니다. 이는 adapter의 `verify_artifact`로 이어집니다. agy의 [agy.py:74-76](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/lib_py/agents/adapters/agy.py:74)는 이전에 DB를 쓰기 가능 모드로 열어 `steps`를 조회했습니다. 캐시에 다른 workspace가 기록된 UUID도 sibling claim에 막히지 않으면 조회 대상이 될 수 있었고, macOS SQLite가 새 `-wal` 및 `-shm`을 남겨 #55 홈 가드에 걸릴 수 있었습니다. 연결이 이미 닫혀 프로세스가 없던 관찰과도 맞습니다.
|
|
|
|
신규 테스트 `test_unpinned_agy_reconcile_leaves_other_workspace_clean_db_without_sidecars`는 workspace A의 미고정 agy 행과 workspace B에 매핑된 DB를 임시 HOME에 만들고 `verify_session_uuid`가 그 DB를 조회하는 상황을 재현합니다. head에서는 조회 결과를 유지하면서 디렉터리 항목이 바뀌지 않습니다. 이번 변경은 다른 workspace 후보를 선택할 수 있는 기존 정책 자체는 바꾸지 않습니다.
|
|
|
|
## 변경 범위와 코드 리뷰
|
|
|
|
- [base.py:111-134](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/lib_py/agents/base.py:111)에 기존 opencode `_ro_connect`를 공용화했습니다. WAL/SHM 쌍은 `mode=ro`, WAL이 없으면 `mode=ro&immutable=1`, WAL만 있으면 DB와 WAL의 임시 사본을 메모리에 backup합니다. [base.py:137-145](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/lib_py/agents/base.py:137)의 `db_rows_naming` 기본 연결도 helper를 사용하며 사용자 지정 `connect` 인자는 유지됩니다.
|
|
- [agy.py:74](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/lib_py/agents/adapters/agy.py:74): `steps` 검증 조회. `discover`도 이 검증을 거칩니다.
|
|
- [codex.py:80,195](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/lib_py/agents/adapters/codex.py:80): rollout 조회와 state DB 검색. residual 조회는 `db_rows_naming`의 기본 helper를 사용합니다.
|
|
- [hermes.py:51,100](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/lib_py/agents/adapters/hermes.py:51): artifact 검증과 discover 조회.
|
|
- [status.sh:90-132,290-332](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/multi-agent-mux-status/scripts/status.sh:90): JSON/text 양쪽 hermes·codex 및 opencode 분기를 전환했습니다. agy status는 기존처럼 DB를 열지 않고 파일 존재만 확인합니다.
|
|
- [reconcile.sh:423,865,954,1048](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/multi-agent-mux-monitor/scripts/reconcile.sh:423): 공용 helper를 import하고 hermes, opencode, codex drift 조회에 사용합니다. agy 경로의 `verify_artifact` 전환도 포함됩니다.
|
|
- 신규 `tests/test_issue64_ro_connect.py`: helper 공유, agy/codex/hermes 조회, 4가지 WAL 상태, status/reconcile 정적·런타임 동작, 다른 workspace agy 재현을 확인합니다.
|
|
|
|
제외한 쓰기 연결은 그대로입니다: [codex.py:152-155](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/lib_py/agents/adapters/codex.py:152)의 purge DELETE, [hermes.py:74-78](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/lib_py/agents/adapters/hermes.py:74)의 purge DELETE, [opencode.py:193-199](/var/folders/q_/68sxgw3j073dgjg8wl9s08vw0000gn/T/mam-review/loop-20261019T134017Z-d90c23/1/.agents/skills/lib_py/agents/adapters/opencode.py:193)의 legacy-row DELETE. `lib.sh`, `lib_py/state.py`, `atomic_yaml.py`, `verify_session.py`, loop runner의 연결은 MAM 자체 상태/트랜잭션 용도입니다. Claude/grok adapter에는 대화 SQLite 조회가 없습니다. helper 내부의 read-only URI와 메모리 DB, 임시 사본 연결도 의도된 구현입니다.
|
|
|
|
변경된 조회 호출은 기존 쿼리·정렬·필터 조건을 보존하면서 helper를 경유합니다. `git diff --check base..head`와 `bash -n`으로 두 셸 스크립트 문법도 확인했습니다. 커밋은 8개 파일, 461 insertions / 56 deletions이고 검증 트리는 clean입니다. 버전 파일과 문서는 바뀌지 않았습니다.
|
|
|
|
## 테스트 결과
|
|
|
|
- Python 3.9.6 / pytest 8.4.2, `PYTHONDONTWRITEBYTECODE=1`, `-p no:cacheprovider`; basetemp는 검증 트리 밖을 사용했습니다. 임시 경로와 fixture로만 DB를 만들었으며 실제 agent home DB는 열지 않았습니다.
|
|
- `tests/test_issue64_ro_connect.py`: **43 passed** (11.59초).
|
|
- 관련 회귀 묶음 `test_issue60_linux_fixes.py`, `test_issue60_opencode_v2.py`, `test_a4_adapter_contract.py`, `test_issue31_purge_residual.py`, `test_issue50_dispatch.py`: **216 passed** (148.47초).
|
|
- 이번 리뷰에서 base 별도 checkout을 만들지는 않았습니다. 동일 identity/head/base를 사용한 독립 리뷰 `.mam/jobs/366a4d58/grok-reports/report-final.md`에 기록된 base 실행은 새 테스트 **23 failed, 20 passed**였습니다. 실패에는 helper 이동, clean/WAL-only 조회 뒤 sidecar 생성, status/reconcile helper 부재, 다른 workspace agy 재현이 포함되며, 남은 20개는 sidecar가 이미 있던 상태입니다. 이 수치는 그 독립 리뷰의 결과이지 이번 실행에서 재현한 수치는 아닙니다.
|
|
|
|
## 남은 위험
|
|
|
|
1. helper가 `-wal` 존재 여부를 확인한 뒤 immutable 연결을 여는 사이 writer가 WAL을 만들면 해당 연결은 새 WAL을 보지 못할 수 있습니다. 기존 구현에서 온 경쟁 조건입니다.
|
|
2. stale WAL/SHM을 동반한 파일은 `mode=ro` 분기로 처리됩니다. 파일명 생성·삭제는 막지만 SQLite가 기존 공유 메모리 영역을 업데이트할 가능성은 남습니다.
|
|
3. reconcile의 agy drift C가 다른 workspace DB를 후보로 보는 정책은 그대로입니다. 수정은 해당 검사가 원본 sidecar를 만들지 않게 하며 후보 귀속 규칙은 변경하지 않습니다.
|
|
4. 브리프가 제외한 purge 쓰기는 WAL/SHM을 만들거나 변경할 수 있습니다.
|
|
|
|
[VERDICT: PASS]
|