Files
multi-agent-mux/session_isolation_discussion.md
T

12 KiB

🛠️ 세션 ID 중복 충돌 해결 종합 설계 및 구현 계획서 (Rev.3 — 단일 격리 디렉터리 통합본)

동일 CLI 계열(Claude / Cline)의 서로 다른 역할 에이전트가 같은 workspace에서 동시 구동될 때 대화 세션 ID를 공유하는 결함(Problem_Definition.md)을 해결하기 위한 최종 종합 설계안 및 구현 계획입니다.

기존의 하이브리드 분기(L1/L2 병행) 구조를 걷어내고, **"모든 에이전트의 격리 디렉터리 관리 일원화"**라는 사용자(GM) 피드백을 반영하여 설계를 단순화한 버전입니다. Rev.3에서는 실측 프로브 결과에 따른 격리 레버 정정 및 auth 시딩 계약(사용자 승인 조건)을 반영했습니다. 상세 계획의 단일 원본은 implementation_plan.session_isolation.md (Rev.3)입니다.


1. 배경 및 문제 정의

동일한 워크스페이스에서 다중 역할 에이전트(예: plannerreviewer-a, developerreviewer-b)가 기동될 때, 동일한 대화 UUID 또는 Cline 대화 ID를 공유함으로써 컨텍스트가 오염되고, 히스토리 쓰기 경합으로 인한 락 충돌 및 데이터 유실이 발생하는 문제가 발생했습니다.

1.1 근본 원인 분석

  • C1. 생성 시 고유 ID 미지정: 세션 생성 시점에 특정 세션 ID 인자를 지정하지 않아 CLI 기본 동작인 "최근 대화 상속"이 실행됨.
  • C2. 워크스페이스 단위 키잉: 대화 세션 매핑이 역할(role) 차원 없이 단순히 workspace_root 경로만을 키로 삼아 이뤄짐.
  • C3. mtime 기반 추측 바인딩 (Tier-2): resolver가 워크스페이스 내에서 최종 수정 시간(mtime)이 가장 최신인 세션을 바인딩하여, 다른 역할의 에이전트가 동일한 UUID를 상속받게 됨.

2. 개정된 설계 방향: "단일 격리 디렉터리" 일원화 (Rev.3 정정 반영)

각 에이전트의 ID 파라미터 지원 여부에 따라 구현을 분기하는 대신, 모든 에이전트에 대해 균일하게 격리 디렉터리 주입 방식으로 일원화하여 아키텍처의 복잡도를 제거합니다.

⚠️ Rev.3 정정 (실측 근거): 이전 판의 "Claude = CLAUDE_PROJECT_DIR 격리"는 동작하지 않는 설계였습니다. CLAUDE_PROJECT_DIR은 MAM resolver의 읽기 전용 변수(lib.sh:23,477)로, claude CLI가 대화를 쓰는 위치를 바꾸지 못합니다. claude의 쓰기 위치 이동은 CLAUDE_CONFIG_DIR(~/.claude 지붕 전체 이동)로만 가능하며, 이 경우 auth/설정 시딩이 필수입니다(§2.3). 또한 cline은 실측 결과 per-session HOME이 아닌 전용 --data-dir/--config 분리 플래그를 보유해 시딩 없이 격리 가능합니다.

graph TD
    A[세션 생성] --> U[uuidgen: isolation-UUID 발급]
    U --> B[.mam/agent_homes/<uuid>/ 생성]
    B -->|Claude| B1[CLAUDE_CONFIG_DIR 주입 + auth/설정 심링크 시딩]
    B -->|Cline| B2[--data-dir 격리 + --config 공유 auth]
    B -->|Agy / Hermes| B3[Phase 0 프로브: 전용 레버 or HOME+시딩 폴백]
    A --> C[Defense-in-depth: R1/R2 resolver 불변식]
    B1 & B2 & B3 --> E[isolation 블록 세션 row 영속화 → resume/resolve/stop 재적용]

2.1 일원화 설계의 핵심

  • 격리 식별자 ≠ 대화 식별자: MAM이 uuidgen으로 발급하는 isolation-UUID는 디렉터리 식별자입니다. CLI는 자기 방식대로 대화 ID를 mint하되(claude UUID, cline epoch_rand 등) 자기만의 격리 디렉터리 안에서 하게 됩니다 — cline의 비-UUID 포맷 문제가 원천 소멸합니다.
  • 작업 디렉터리(Cwd) 공유: 에이전트들의 작업 디렉터리는 기존과 동일하게 같은 프로젝트 루트를 바라봅니다(-c "$WORKSPACE" 불변). 협업 소스코드 컨텍스트는 완벽히 일치하며, 격리는 대화 상태 저장소의 위치만 이동합니다.
  • 격리 루트: <workspace>/.mam/agent_homes/<isolation-uuid>/.gitignore(.mam/) 자동 커버, remove/stop 청소 계약에 자연 포함.
  • 스키마 영속화: 세션 row에 isolation: {uuid, root, lever, seeded[]} 블록을 atomic_dump_yaml로 영속화(DB+YAML). resume/resolve/stop은 이 블록만 읽는 단일 디스패치 함수를 경유 — agent별 레버 차이는 함수 내부에 캡슐화되어 관리 모델은 완전 통일됩니다.
  • 근본 원인 해소 기전: 신규 격리 디렉터리엔 상속할 최근 대화가 없음(C1 무해화) / UUID 네이밍으로 cwd-key 충돌 소멸(C2) / 디렉터리당 대화 1개 → resolver 항상 유일 후보(C3 소멸).

2.2 agent별 격리 레버 매핑 — Phase 0 실측 확정 (2026-07-10)

Agent Spawn 레버 시딩 (전부 심링크) 격리 내 대화 경로 실증
claude CLAUDE_CONFIG_DIR=<root> env .credentials.json, settings.json, plugins/ <root>/projects/<key>/<uuid>.jsonl 실호출 PASS
cline --data-dir <root> 플래그 settings/* + globalState.json (⚠️ --config 공유만으론 auth 미공유 — 실측 반증, 시딩 필수) <root>/sessions/<id>/<id>.json (⚠️ 실HOME data/sessions/와 레이아웃 상이) 실호출 PASS
agy HOME=<root> env ~/.gemini/ auth 3종(oauth_creds.json, google_accounts.json, antigravity-oauth-token) + 메타(installation_id/settings.json/state.json) <root>/.gemini/antigravity-cli/conversations/<uuid>.db auth 검증 PASS (시딩 전 실패→후 성공)
hermes HOME=<root> env ~/.hermes/{auth.json, config.yaml, .env} <root>/.hermes/state.db 읽기 격리 PASS ("No sessions found" + fresh state.db)

2.3 시딩 계약 (claude 및 HOME 폴백 agent) — 사용자 승인 조건

  • 심링크 강제, 복사 금지: 복사 시 토큰 갱신이 격리 사본으로 발산해 원본과 어긋남. 심링크는 갱신이 원본 단일 파일에 수렴(현행 다중 인스턴스 동작과 동일 의미론).
  • claude 시딩 목록(초안): .credentials.json, settings.json, plugins/ — Phase 0에서 최종 확정, row의 seeded[]에 기록.
  • stop 퍼지 시 심링크 원본 무손상 검증 포함.

2.4 R1/R2 resolver 불변식 (2중 안전 장치)

  • R1: resolver(find_workspace_uuid 등)가 최신 파일을 스캔해오기 전, 현재 실행 중인 다른 세션들이 소유한 *_own 대화 ID 집합을 후보군에서 제외(claimed-set filtering)합니다.
  • R2: atomic_dump 시점에 새로 등록하려는 세션 ID가 이미 실행 중인 다른 세션의 ID와 중복될 경우 SystemExit 에러로 강제 진입 차단합니다.

2.5 정리(Cleanup) 계약 (RC-2)

  • 세션 정지(stop_session.sh) 시 isolation.root를 일괄 청소(rm -rf)하는 단순·명확한 클린업 규칙. .mam/ 하위 배치로 remove.sh 전체 청소도 자동 커버.

2.6 Non-Goals

  • 에이전트 CLI(claude, cline, agy, hermes) 자체 바이너리/코드를 수정하지 않습니다. 구동 시 외부 환경변수·플래그 주입 인터페이스만 사용합니다.
  • 기존 단일 에이전트 동작 구조의 하위 호환성은 완벽하게 보존합니다.

3. 단계별 구현 계획 (Roadmap)

대전제: Phase 0 검증 게이트 통과 전에는 격리 주입(Phase 2 이후) 코드 구현에 착수하지 않습니다.

Phase 0 (격리 레버 실측 및 검증 — G2 재조준)
      ▼
Phase 1 (R1/R2 공통 불변식 필터 — Phase 0 산출물 비의존, 병렬 선행 가능)
      ▼
Phase 2 (격리 프로비저닝·시딩·주입 및 isolation 스키마 영속화 구현)
      ▼
Phase 3 (stop_session.sh 클린업 계약 연동 구현)
      ▼
Phase 4 (통합 및 회귀 검증)

Phase 0 — 격리 레버 실측 (구현 전 필수 실측)

  • G2-claude: CLAUDE_CONFIG_DIR=<격리경로> 기동 시 (a) 대화 jsonl이 <격리경로>/projects/<key>/에 생성 (b) .credentials.json 심링크만으로 로그인 유지 (c) settings/plugins 심링크로 행동 드리프트 없음.
  • G2-cline: --data-dir <격리경로> --config ~/.cline/data/settings 기동 시 (a) 세션이 격리경로에 생성 (b) 공유 auth 정상 동작.
  • G2-agy / G2-hermes: 전용 레버(project/home env) 실측, 부재 시 HOME 오버라이드+auth 시딩 유효성.
  • DoD: {agent × (레버, 시딩 목록, 대화 저장 실경로)} 매트릭스 확정 → §2.2 갱신.

Phase 1 — 공통 불변식 구현 (전략 무관, 선행 가능)

  • 의존성 참고: Phase 1은 격리 프로브 결과에 의존하지 않고 공통 lib.sh에만 적용되므로, Phase 0 완료 여부와 무관하게 병렬로 또는 선제적으로 구현할 수 있습니다.
  • T1. claimed-set 필터 구현: lib.sh (find_workspace_uuid 계열) 탐색 로직 수정. 후보군 중 다른 실행 중인 row의 *_own을 필터링 아웃.
  • T2. 생성-시 중복 assertion: 세션 등록/덤프 로직 부근에서 중복 assert — 중복 ID 충돌 시 즉시 거부.
  • DoD: 동일 대화 ID 강제 주입으로 세션 2개 생성 시도 시, 두 번째 생성 요청이 거부됨을 증명.

Phase 2 — 격리 프로비저닝 및 영속화 구현 (전체 에이전트 적용)

  • T3. 격리 프로비저닝: create 시 isolation-UUID 발급 → .mam/agent_homes/<uuid>/ 생성 → agent별 심링크 시딩(§2.3).
  • T4. spawn 주입: 디스패치 함수가 agent별 레버(§2.2: CLAUDE_CONFIG_DIR / --data-dir / 프로브 결과)로 격리 루트 주입.
  • T5. 스키마 영속화: isolation 블록을 row에 저장하고 resume/resolve 시 재소싱 적용 — 저장+재적용은 원자적 세트.
  • DoD: Claude / Cline 세션 각각 2개 동시 생성 시 대화 파일 물리 분리·캐시 비공유, resume 시 올바른 복원 확인.

Phase 3 — stop 정리 계약 확장

  • T6. stop 정리 계약 확장: stop_session.shisolation.root를 자동 퍼지(rm -rf), 심링크 원본 무손상 검증 포함.
  • DoD: stop 후 격리 디렉터리 잔존 0, 실HOME auth/설정 원본 무손상, 디스크 누수 없음.

Phase 4 — 통합 및 회귀 검증

  • V1. 다중 기동 테스트: planner(claude), reviewer-a(claude), developer(cline), reviewer-b(cline) 4개 동시 기동 시 4개 세션 ID 모두 유일성 확보 검증.
  • V2. 동시 쓰기 경합: 4개 에이전트 동시 동작 시 락 경합/데이터 유실 현상 재현 불가 확인.
  • V3. 단일 세션 회귀 검증: 격리 정책 적용 후 기존 단일 에이전트 구동 환경에서 문제없이 정상 동작함을 검증.

4. 리스크 및 검수 포인트 (Reviewer 관점, Rev.3 갱신)

ID 리스크 완화책
RK1 격리 경로 주입 후 resolve/resume 시 전역 기본 경로 스캔으로 회귀 isolation 블록 저장과 resume 시 재적용을 원자적 세트로 묶고 단일 디스패치 함수 강제
RK2 시딩 드리프트: CLI 업데이트로 신규 파일 등장 시 심링크 목록 누락 → 격리 인스턴스 행동 이상 seeded[]를 row에 기록, Phase 0 매트릭스에 시딩 파일 목록 명세
RK3 토큰 갱신 발산: auth 파일을 복사 시딩하면 격리 사본의 토큰 갱신이 원본과 어긋남 심링크 강제 — 갱신이 원본 단일 파일에 수렴
RK4 비정상 종료 시 격리 디렉터리 미정리로 인한 디스크 누수 .mam/ 하위 배치(remove.sh 커버) + stop 퍼지 + 고아 agent_homes/* GC(선택)
RK5 단일 에이전트 워크플로우 기존 사용자의 하위 호환성 회귀 격리 활성화를 세션 다중성 조건 혹은 명시적 플래그(--isolate-strict)로 제어, V3 회귀 게이트

5. 의사결정 및 다음 단계

  • 모든 에이전트를 격리 디렉터리 방식으로 일원화하는 방향 최종 승인 (트레이드오프 — claude --session-id 실측 확정 레버의 미채택 — 인지 후 결정).
  • 승인 조건 반영: (1) Phase 0에 claude CLAUDE_CONFIG_DIR+심링크 로그인 유지 프로브 포함, (2) 시딩 계약(§2.3) 명문화.
  • ⏭️ 승인에 따라 Phase 0 (격리 레버 실측)Phase 1 (공통 불변식 필터) 태스크에 착수합니다. Phase 2 이후 구현은 Phase 0 매트릭스 확정 후 진행합니다.