# Implementation Plan — 세션 ID 중복 충돌 해소 / 역할별 세션 격리 (Rev.3) - **작성자**: Planner Agent - **날짜**: 2026-07-10 (Rev.2 → Rev.3 개정) - **상태**: Draft (Phase 0 검증 게이트 대기 — 구현 미착수) - **관련 자료**: [Problem_Definition.md](Problem_Definition.md) - **확정된 방향 (Rev.3)**: **all-L2 단일화** — 전 에이전트 isolation-UUID 격리 디렉터리 + 세션 row 영속화 + R1 claimed-set 불변식 --- ## 0. Rev.2 → Rev.3 변경 이력 (Decision Log) | # | 결정 | 사유 | |---|------|------| | D1 | **L1(생성-시 대화 ID 인자 주입) 전략 폐기** | 관리 모델 통일 우선. claude `--session-id`는 실측 확정된 레버지만, agent별 L1/L2 분기 유지 비용보다 단일 격리 메커니즘의 일관성을 우선함 (트레이드오프 인지 후 사용자 확정) | | D2 | **격리 식별자 = MAM 발급 isolation-UUID** (CLI 대화 ID와 분리) | cline 등 비-UUID 자체 ID 포맷 문제 원천 소멸 — CLI가 자기 방식대로 대화 ID를 mint하되 **자기만의 격리 디렉터리 안에서** 하게 함 | | D3 | **격리 정보를 세션 row(`isolation` 블록)로 영속화** | resume/resolve/stop이 단일 디스패치로 재적용 → 주입/해석 불일치(RK2) 차단 | | D4 | **claude 격리 레버 = `CLAUDE_CONFIG_DIR` + auth 시딩** | 실측: `~/.claude/` 한 지붕 아래 `.credentials.json`+`settings`+`plugins`+`projects/` 동거 확인. 대화만 옮기는 좁은 레버 부재 → 지붕 이동 + 시딩 계약 필수. ⚠️ 기존 문서의 `CLAUDE_PROJECT_DIR`은 **MAM resolver 읽기 전용 변수**(lib.sh:23,477)로 CLI **쓰기** 위치를 바꾸지 못함 — Rev.2의 해당 서술 정정 | | D5 | **cline 격리 레버 = `--data-dir` + `--config` 분리 조합** | 실측: `cline --data-dir `("Use isolated local state", default `~/.cline`) + `--config `(설정/auth, default `~/.cline/data/settings`) 별도 플래그 확인 → 대화만 격리·auth 공유 가능, 시딩 불필요 예상 | --- ## 1. 배경 및 문제 정의 동일 CLI 계열(Claude/Cline)의 서로 다른 역할 에이전트가 같은 workspace에서 동시 구동될 때 **동일한 대화 세션 ID를 공유**하는 결함 (`Problem_Definition.md`). ### 1.1 근본 원인 (코드 근거) 및 all-L2가 끊는 방식 | # | 원인 | 근거 | all-L2 해소 기전 | |---|------|------|------------------| | C1 | spawn 시 세션 ID 미지정 → CLI "cwd 최근 대화 상속" | `create_session.sh:125/:135` | 신규 격리 디렉터리엔 상속할 최근 대화가 **없음** → 각자 fresh 시작 | | C2 | 저장소가 workspace(cwd) 단위 키잉, role 차원 없음 | `lib.sh:480`, `:615` | 격리 디렉터리가 **UUID 네이밍** → cwd 파생 key 충돌 원천 소멸 | | C3 | resolver Tier-2가 mtime 최신 파일 반환 → 동일 UUID 해석 | `lib.sh:564` | 격리 디렉터리 안엔 대화가 **하나뿐** → disk-scan 항상 유일 후보, mtime 경합 소멸 | ### 1.2 불변 전제 (변경 금지) - **cwd 공유**: 모든 에이전트는 `-c "$WORKSPACE"`로 동일 workspace에서 기동 (협업 전제). 격리는 **대화 상태 저장소의 위치만** env/플래그로 옮기며 cwd는 절대 건드리지 않음. - CLI(claude/cline/agy/hermes) 자체 미수정 — 인자/환경변수 인터페이스만 사용 (Non-Goal). - 기존 단일-에이전트 워크플로우 회귀 0. --- ## 2. 설계 (all-L2 단일 격리 메커니즘) ### 2.1 격리 디렉터리 - 생성 시 `uuidgen`으로 **isolation-UUID** 발급. - 격리 루트: `/.mam/agent_homes//` - `.mam/` 하위 → `.gitignore:11` 자동 커버, `remove.sh`/stop 청소 계약에 자연 포함. ### 2.2 세션 row 스키마 확장 (`isolation` 블록) ```yaml tmux_sessions: - name: role: isolation: uuid: root: .mam/agent_homes/ lever: claude_config_dir | cline_data_dir | home | seeded: [".credentials.json", "settings.json", "plugins"] # 심링크 목록 (해당 시) ``` - `atomic_dump_yaml` 경유로 DB+YAML 동시 기록 (P1 상시 미러 수정본 전제). - resume/resolve/stop은 이 블록만 읽는 **단일 디스패치 함수**로 재적용 — agent별 레버 차이는 이 함수 내부에 캡슐화. ### 2.3 agent별 레버 매핑 — ✅ Phase 0 실측 확정 매트릭스 (2026-07-10) | Agent | Spawn 레버 | 시딩 목록 (전부 **심링크**) | 격리 내 대화 저장 실경로 | 실증 | |---|---|---|---|---| | claude | `CLAUDE_CONFIG_DIR=` env | `.credentials.json`, `settings.json`, `plugins/` | `/projects//.jsonl` | ✅ 실호출 — jsonl 격리 생성, 로그인 유지, 실HOME 무변화(27→27) | | cline | `--data-dir ` 플래그 | `settings/*`(providers.json 등 5종), `globalState.json` | `/sessions//.json` ⚠️ | ✅ 실호출 — 세션 격리 생성, 실HOME 무변화(13→13) | | agy | `HOME=` env | `~/.gemini/{oauth_creds.json, google_accounts.json, installation_id, settings.json, state.json}` + `antigravity-cli/{antigravity-oauth-token, installation_id, settings.json}` | `/.gemini/antigravity-cli/conversations/.db` | ✅ auth 검증(`agy models` — 시딩 전 실패/후 성공), fresh conversations/ 확인 | | hermes | `HOME=` env | `~/.hermes/{auth.json, config.yaml, .env}` | `/.hermes/state.db` (sessions 테이블) | ✅ 읽기 격리 검증 — 격리 HOME "No sessions found" + fresh state.db, 실HOME 세션 비노출 | **Phase 0 실측 정정·주의사항**: 1. ⚠️ **cline `--config` 가정 반증**: `--config ~/.cline/data/settings` 공유 지정만으로는 auth가 공유되지 않음(`Unauthorized`) — cline이 data-dir 안에 자체 settings를 생성. → **cline도 시딩 필수** (Rev.3 본문 "시딩 불필요 예상" 정정). 2. ⚠️ **cline 격리 레이아웃 상이**: 격리 시 `/sessions/`(실HOME은 `~/.cline/data/sessions/`) — resolver 재적용(T5)에서 **lever별 경로 템플릿 분기** 필요. 3. hermes `config.yaml`에 실HOME 절대경로(런타임 `hermes-agent`) 내장 — 런타임은 읽기 공유라 무해하나, 격리 범위가 state/세션에 한정됨을 기록. 4. agy는 첫 실행 시 격리 HOME에 디렉터리 구조를 자동 부트스트랩(fresh `conversations/` 포함). - **resolver 연동**: 디스패치 함수가 row의 `isolation`을 읽어 `HOME_DIR`/`CLAUDE_PROJECT_DIR`(MAM 읽기 변수, `lib.sh:22-23`)를 격리 루트 기준으로 export한 뒤 `find_workspace_uuid`/resume 호출. ### 2.4 공통 불변식 (전략 무관 유지) - **R1. claimed-set 필터**: Tier-2 반환 전, 같은 workspace의 다른 running row 소유 `*_own` 집합 제외 (`lib.sh:540-575`). 격리가 부분 실패해도 이중 배정 구조적 차단. - **R2. 생성-시 유일성 assert**: 새 `*_own`이 기존 running `*_own`과 충돌 시 `SystemExit` (`lib.sh:377` 근처). --- ## 3. 단계별 태스크 ### Phase 0 — 검증 게이트 (**구현 전 필수·차단**, Rev.3 재조준: 구 G1 삭제) - **G2-claude**: `CLAUDE_CONFIG_DIR=<격리경로>` 기동 시 (a) 대화 jsonl이 `<격리경로>/projects//`에 생성되는가 (b) `.credentials.json` **심링크만으로 로그인 유지**되는가 (c) settings/plugins 심링크로 행동 드리프트 없는가. - **G2-cline**: `--data-dir <격리경로> --config ~/.cline/data/settings` 기동 시 (a) 세션이 `<격리경로>/data/sessions/`(또는 상응 경로)에 격리 생성되는가 (b) auth/providers가 공유 config에서 정상 동작하는가. - **G2-agy**: `--new-project` per-role 부여 시 대화 격리 여부, 또는 데이터 경로 env 존재 여부. 부재 시 `HOME` 오버라이드+auth 시딩 유효성. - **G2-hermes**: home 이동 env/플래그 실측. 부재 시 `HOME` 오버라이드+auth 시딩 유효성. - **DoD**: `{agent × (레버, 시딩 목록, 대화 저장 실경로)}` 매트릭스 확정 → §2.3 갱신. ### Phase 1 — 공통 불변식 (Phase 0과 병렬 착수 가능) - **T1. claimed-set 필터** / **T2. 생성-시 유일성 assert** (§2.4). - **DoD**: 동일 ID 강제 주입 2개 create → 두 번째 거부 (단위 재현). ### Phase 2 — all-L2 격리 구현 (구 Phase 2/3 통합) - **T3. 격리 디렉터리 프로비저닝**: create 시 isolation-UUID 발급 → `.mam/agent_homes//` 생성 → agent별 시딩(§2.3, 심링크) 수행. - **T4. spawn 주입**: 디스패치 함수가 agent별 레버(env/플래그)로 격리 루트 주입. - **T5. 스키마 영속화 + 재적용**: `isolation` 블록 atomic_dump 기록, resume/resolve가 동일 디스패치로 재소싱 (**저장+재적용은 원자적 세트** — RK2). - **T6. stop 청소 계약**: `stop_session.sh`가 `isolation.root`를 퍼지(`rm -rf`), 심링크 대상 원본은 보존 확인. - **DoD**: 동일 workspace, 같은 CLI 2개(다른 role) 동시 생성 → 대화 파일 물리 분리, `*_own` 상이, 교차 write 0. ### Phase 3 — 통합 및 회귀 검증 - **V1**: planner/reviewer-a(claude) + developer/reviewer-b(cline) 4개 동시 기동 → 4개 대화 ID 전부 유일. - **V2**: 동시 write 락 충돌·컨텍스트 오염 재현 불가. - **V3**: 각 role resume이 자기 대화만 복원 (isolation 재적용 경유). - **V4**: 단일-에이전트 기존 워크플로우 회귀 0 (격리 미사용 경로 불변). - **V5**: stop 후 격리 저장소 잔존 0 + 실HOME auth/설정 원본 무손상. --- ## 4. 리스크 및 검수 포인트 (Rev.3 갱신) | ID | 리스크 | 완화 | |----|--------|------| | RK1 | 격리 실패/부분 적용 시 이중 배정 | R1 claimed-set 필터가 최후 방어선 (무조건 유지) | | RK2 | spawn 주입 경로와 resolve/resume 스캔 경로 불일치 → 전역 회귀 | T5 "저장+재적용" 원자적 세트, 단일 디스패치 함수 강제 | | RK3 | **시딩 드리프트**: CLI 업데이트로 신규 파일 등장 시 심링크 목록 누락 → 행동 이상 | `seeded` 목록을 row에 기록, Phase 0 매트릭스에 파일 목록 명세, 온보딩 문서화 | | RK4 | 토큰 갱신 발산 (복사 시딩 시) | **심링크 강제** — 갱신이 원본 단일 파일에 수렴 (현행 다중 인스턴스 동작과 동일) | | RK5 | 비정상 종료 시 격리 디렉터리 누수 | `.mam/` 하위 배치로 remove.sh 전체 청소 커버 + T6 stop 퍼지 + (선택) create 시 고아 `agent_homes/*` GC | | RK6 | 단일-에이전트 기존 사용자 회귀 | 격리 발동을 명시 플래그/다중성 조건으로 제어 (`--isolate` 등, Phase 0 후 확정), V4 회귀 게이트 | --- ## 5. 승인 및 다음 단계 - 본 계획(Rev.3)은 **Phase 0 게이트 통과 전 코드 구현 착수 금지**를 대전제로 유지한다. - 다음 액션: **Phase 0 (G2-claude / G2-cline / G2-agy / G2-hermes) 프로브 실행** — 스크래치 워크스페이스에서 실측 후 §2.3 매트릭스 확정.