← 목록
기타 2026-07-17 27KB 읽기 24분

집사 오케스트레이션 마스터 — 설계 검토

개요

사장님 지시(2026-07-17): "별도 집사창은 CLI처럼 내 컴퓨터 전체에 대해서도 작업시킬 수 있게 검토. 클로드코드 창 10개에 어떤 작업 중인지, 3번째 창에 이렇게 명령하기 등 가능하게, 일종의 오케스트레이션 마스터."

본 문서는 코드 수정 없이 기존 자산을 전수 조사하고, CrownyAI 별도 집사창(toggleButlerWindow)을 두 축 — ① 로컬 컴퓨터 CLI형 작업, ② 세션 오케스트레이션 — 으로 확장하는 설계만 검토한다. 중복구축 방지가 최우선 원칙이라, "이미 있는 것"을 먼저 정확히 확정한 뒤 "집사 UI에서 그것을 연결하는 배선"만 신규 범위로 좁힌다.


1. 현황 매핑 — 있는 것 vs 없는 것

1-1. 세션 목록 "보기" — 인프라는 있음, 데이터 품질은 낮음

  • 있음: ~/.claude/knowledge/LIVE_STATUS.md — 5분마다 LaunchAgent(crowny-knowledge.sh harvest)가 갱신, 현재 300개 세션(SID·카테고리·현재작업·경과·히스토리건수) 표 형태로 유지. 실측(/Users/ef/.claude/knowledge/LIVE_STATUS.md, 2026-07-17 21:51 기준) 621줄.
  • 있음: 등록/하트비트 API — /Users/ef/.claude/scripts/crowny_knowledge.py cmd_register(92행)·cmd_heartbeat(123행)·cmd_status(212행). PreToolUse 훅이 Write/Edit/Bash마다 하트비트를 쏘아 task 필드를 갱신(146행: 이전 태스크와 다르면 history에 적재, 최대 20개 보존).
  • 없음 / 품질 갭(실측 확인): LIVE_STATUS.md를 실제로 읽어보면 대다수 행이 카테고리=general, 현재 작업=<경로>: (콜론 뒤 공백, 즉 빈 문자열), 히스토리=0건이다. cmd_registertask 기본값은 "(시작됨)"(112행)인데 표시에는 그조차 안 보인다 — 즉 세션 목록 자체는 존재하지만, "무엇을 하고 있는지"에 대한 실제 텍스트가 대부분 비어 있다. 이 갭을 채우는 것("작업 내용을 더 잘 채운다")은 이번 조사 범위를 넘는 별도 과제이며, 집사 UI는 있는 데이터를 있는 그대로 보여주는 소비자로 설계해야 한다(과장 금지).
  • 없음: "N번째 창"이라는 안정적 서수 개념 자체가 없다. SID는 8자리 해시(e0df2a62 등)이고 등록 순서·정렬 기준이 표 노출 순서(경과시간 내림차순으로 보임) 외에 고정 규약이 없다. "3번째 창"을 매번 같은 세션에 매핑하려면 집사 쪽에서 표시 시점 기준의 임시 서수(뷰 렌더링 시 1..N 부여)를 별도로 설계해야 한다 — 아래 3장.

1-2. 세션에 "명령 전송" — 인프라는 있음, 집사 UI 연결만 없음

  • 있음(확인): ~/.claude/scripts/크라우니메신저연동.sh — msg.crowny.org(http://localhost:9939, kps-relay 방)에 세션을 연결해 --연결(연결선언) / 자유 텍스트([세션명] 보고전송) / --수신(자기 멘션 새 지시만 필터링해 가져오기, 커서파일 /tmp/메신저연동_<세션>.cursor로 중복수신 방지) 3동작을 이미 구현. [세션작업명] prefix로 방 안에서 세션을 구분하는 규약이 이미 정착돼 있다(글로벌 CLAUDE.md에도 프로토콜로 명문화).
  • 있음: 각 세션이 마라톤 프로토콜(/Users/ef/crowny-butler/크라우니마라톤.md)의 1단계로 --수신을 주기적으로 호출해 사장님 지시를 반영하는 관례가 이미 있다 — 즉 "세션에 지시를 흘려보내는 배관"과 "세션이 지시를 주워 실행하는 관례"는 이미 존재.
  • 없음: 이 메신저 연동을 별도 집사창 UI에서 클릭/자연어로 트리거하는 경로가 없다. crowny-ai-butler.mcbButlerRespond:(638행) 라우팅 표에는 has(@"메신저")||has(@"보고")||has(@"메시지")msg|... 액션(666행)이 있고 cbExecuteButlerAction:sendCrownyMessage:(723행, butler.m 헤더 주석에 위치 언급됨)를 호출하지만, 이것은 "메신저 방에 보고를 쏘는" 편도 기능이지 "세션 N에 지시를 보내고 그 세션이 받는" 왕복 오케스트레이션 문법이 아니다. "3번 창에 지시" 같은 발화를 파싱해 특정 세션명으로 라우팅하는 로직은 전무.
  • 결론: "이미 있는 것을 새로 만들지 말라"는 원칙에 따라, 신규로 만들 것은 메신저 프로토콜 자체가 아니라, 집사창이 LIVE_STATUS를 읽어 세션 목록을 보여주고, 사용자의 자연어 지시를 [세션명] 지시… 포맷으로 변환해 크라우니메신저연동.sh(또는 그 HTTP 엔드포인트, localhost:9939/api/msg/send)에 위임하는 얇은 배선층뿐이다.

1-3. 로컬 컴퓨터 CLI형 작업 — 있음(질의응답 한정), "작업 수행"은 없음

  • 있음: native/crowny-ai-butler.mcbRunLocalCLI:(812행) — tools/집사CLI브릿지.sh를 NSTask로 백그라운드 실행(120초 타임아웃, env 4키 주입: CROWNY_CLI_CMD/MODEL/PERSONA/CLAUDE_CONFIG_DIR), stdout을 말풍선으로 표시. !로 시작하는 발화(648행) 또는 라우팅 최종 폴백(697행)이 이 경로로 간다.
  • 있음: /Users/ef/CrownyBrowser/tools/집사CLI브릿지.sh — 4상 라우팅: 티=크라우니뇌 상주서버(:9977/q) 정확키 HIT 즉답(토큰0) → 옴=claude -p --model haiku 폴백 → 타=빈입력/CLI없음 거부. --status(현황 진단), --file <경로>(N15, 현재 탭 본문 기반 Q&A) 두 특수 모드도 있음.
  • 없음(결정적 갭): 이 경로는 질의응답(claude -p)이지 "파일을 고쳐라/셸 명령을 실행해라" 같은 로컬 작업 실행이 아니다. claude -p-p 플래그 자체가 read/analyze 중심 non-interactive 모드이며, 현재 브릿지는 Write/Edit/Bash 등 실행 권한이나 승인 흐름을 전혀 다루지 않는다. "내 컴퓨터 전체에 대해 작업시킨다"(파일 생성/수정/삭제, 셸 명령 실행)에 필요한 위험도 게이트(읽기 자동 vs 쓰기·삭제·네트워크 확인)가 브릿지에도 라우터에도 없다.
  • 참고(다른 도메인의 유사 사례): /Users/ef/crowny-butler/명령브릿지.sh는 "한국어 코딩명령 → intent → 한선씨 코드"를 매칭하는 코드생성 브릿지로, 목적이 다르다(로컬 파일시스템 작업이 아니라 크라우니코드 학습DB 조회). 참고할 4상 매칭 패턴은 있지만 그대로 재사용할 실행기는 아니다.

1-4. 요약표

필요 기능상태근거 파일
세션 목록 데이터(SID·경로·카테고리·경과)있음~/.claude/knowledge/LIVE_STATUS.md, crowny_knowledge.py:cmd_status
세션 "현재 작업" 텍스트 품질부분(낮음) — 대부분 공백LIVE_STATUS.md 실측(300세션 중 절대다수 빈 task)
"N번째 창" 안정 서수없음신규 설계 필요(§3)
특정 세션에 지시 전송 배관있음크라우니메신저연동.sh, msg.crowny.org:9939
세션이 지시를 수신하는 관례있음(마라톤 세션 한정)크라우니마라톤.md 프로토콜 1단계
집사 UI ↔ 메신저 오케스트레이션 배선없음신규 설계 필요(§2-B)
집사 UI ↔ LIVE_STATUS 배선없음신규 설계 필요(§2-B)
로컬 CLI 질의응답(읽기 위주)있음집사CLI브릿지.sh, cbRunLocalCLI:
로컬 파일/셸 "작업 수행" + 위험도 게이트없음신규 설계 필요(§2-A)

2. 두 능력 분리 설계

두 축은 위험 프로파일이 다르므로 반드시 분리된 확인 경로를 가져야 한다. 세션 오케스트레이션(B)은 "이미 신뢰된 다른 클로드코드 세션에게 자연어 지시를 전달"하는 것이라 상대적으로 안전(각 세션 자체가 이미 권한 게이트를 갖고 있음). 로컬 CLI 작업(A)은 "집사 프로세스 자신이 직접 파일/셸을 건드리는" 것이라 훨씬 위험하다.

2-A. 로컬 컴퓨터 작업(CLI형)

현재: cbRunLocalCLI:집사CLI브릿지.shclaude -p ... --model haiku. -p(print/non-interactive) 모드는 도구 실행이 아니라 텍스트 응답 생성이 기본이며, 현재 스크립트는 --model haiku만 지정하고 도구/권한 플래그를 넘기지 않는다.

두 가지 확장 경로 비교:

  1. 경로 A1 — cbRunLocalCLI 확장(같은 브릿지에 실행모드 추가)
- 브릿지에 신규 인자 --do <지시>(가칭)를 추가해, 내부에서 claude-p 아닌 에이전틱 모드(도구 허용, 예: --dangerously-skip-permissions 류 또는 프로젝트 .claude/settings.json allowlist 기반)로 기동. - 장점: 기존 파이프(4상 라우팅, exit 코드 규약, 로그 psv, N8 계정 프로필)를 그대로 재사용. 집사 라우터(cbButlerRespond:) 쪽 변경도 "cli" 액션에 서브타입만 추가하면 됨. - 단점: 브릿지 하나가 "질문"과 "실행"을 겸하면 실수로 위험 명령이 질의응답 경로로 새는 오분류 리스크가 커진다(현재 4상 라우팅은 "정확키 HIT/MISS"만 보지, "위험도"를 보지 않음).

  1. 경로 A2 — 신규 경로(별도 브릿지 스크립트, 예: 로컬작업브릿지.sh)
- 질의응답(집사CLI브릿지.sh)과 실행(신규)을 아예 파일 단위로 분리 — 코드 경로가 나뉘어 있으면 "질문인데 실행됐다" 같은 사고를 원천적으로 줄인다. - 단점: 파이프(타임아웃/로그/exit코드/env주입)를 통째로 복제해야 하므로 관성 위반 소지(중복구축) — 만들 경우 공통부(타임아웃 래퍼, exit 규약)는 함수로 공유(source)하는 게 낫다.

권고: A2(분리)를 하되 공통 유틸은 소스 공유. 근거: 위험도가 근본적으로 다른 두 기능을 같은 스크립트/같은 4상 판정 안에 넣으면, 4상의 "티(즉답)"가 실은 "즉시실행"이 될 위험이 생겨 헌법의 4상 의미(정의/사실 즉답 vs 파일변경)가 오염된다. 실행 경로는 애초에 "즉답 티"가 존재해선 안 되고(읽기조차 확인 없이 자동은 논란 여지), 4상 자체를 아래처럼 다시 정의해야 한다.

위험도 게이트(4상 재정의, 실행 전용):

판정 기준동작
티(자동)순수 읽기(cat/ls/grep/crowny_knowledge.py status류, 부작용 없음)즉시 실행, 결과만 표시
옴(확인)파일 쓰기/수정, 프로세스 재시작, git(로컬 커밋 포함)집사 말풍선에 "실행할 명령: …" 노출 + 사용자 클릭/문구 확인 후 실행
타(거부)삭제(rm), 권한 상승(sudo), 네트워크 발신(외부 curl/push), 인프라 파일(gateway.yaml·plist·crontab — 가드레일 §산출물규칙과 동일 금지선)자동 거부, "직접 터미널에서 하십시오" 안내만
음(이관)판정 불가/모호 명령실행하지 않고 "무엇을 의도하신 건지" 재질문
  • 이 표는 가드레일(한선씨-가드레일.md)의 "공유 인프라 무접촉" 원칙과 정확히 정합한다 — 집사가 gateway.yaml/plist/crontab을 만지는 경로는 애초에 타로 하드 차단해야 한다.
  • 판정 로직 자체는 신규 로직이므로 한선씨 우선 원칙에 따라 .한선 정본(가칭 집사로컬작업게이트.한선)으로 작성하고, 브릿지 셸은 그 판정 결과만 소비하는 얇은 wrapper여야 한다(가드레일 §산출물규칙: "도구·CLI·실행기는 한선씨 실행프로그램으로").
권한·샌드박스 우려(명시):
  • 집사 프로세스(CrownyBrowser.app)가 사용자 전체 파일시스템 권한으로 셸을 실행하게 되면, 브라우저 앱 하나의 버그·프롬프트 인젝션(악성 웹페이지 텍스트가 pageask 경로를 통해 지시로 둔갑) 경로가 곧 로컬 RCE 경로가 된다. 실측 유사사례: 메모리 feedback_os_site_deacon_rce_execSync — "인증 없는 execSync 셸패스스루=공개 RCE"가 이미 한 번 발생한 패턴. 집사가 웹페이지 본문에서 읽어들인 텍스트(cbPageAsk 등)를 실행 명령으로 승격시키는 경로는 만들지 않는다 — 실행 트리거는 반드시 사용자가 별도 집사창에 직접 타이핑한 입력만 인정.
  • macOS 샌드박스: CrownyBrowser.app이 App Sandbox 하에 있는지 확인 필요(현재 native/ 빌드가 codesign/entitlements를 어떻게 쓰는지는 이번 조사 범위 밖 — 구현 단계에서 반드시 확인). 샌드박스가 걸려있다면 NSTask 자체가 제한될 수 있어 A안 설계보다 우선 확인해야 할 전제조건.

2-B. 세션 오케스트레이션

패널 설계: 별도 집사창(오케스트레이션 모드) 상단 또는 좌측에 "세션 목록" 패널을 추가.

  • 데이터 소스: ~/.claude/knowledge/LIVE_STATUS.md를 파싱(마크다운 표 파싱, 이미 고정 포맷 — SID/카테고리/작업/경과/히스토리 5열)하거나, crowny_knowledge.py status를 서브프로세스로 호출해 더 원본에 가까운 JSON을 얻는다(LIVE_STATUS.md는 5분 지연이 있을 수 있으므로 실시간성이 필요하면 cmd_status가 낫다 — _load_sessions()SESSIONS_FILE을 직접 읽음, 212행).
  • 표시 형태: 번호(집사가 그때그때 부여) | SID 앞 4자리 | 경로 basename | 카테고리 | 작업(있으면) | 경과분. 예:
  1  e0df2a  crownyc        general   —          30분
  2  47a249  crownyc        general   —          29분
  3  cb0dfa  crowny-hub     general   —          29분
  ...
  
  • 번호 부여 규약(§1-1의 "없음" 항목 해소): 매 렌더링 시점에 LIVE_STATUS 표의 노출 순서(경과시간 내림차순, 오래된 세션이 위)를 그대로 1..N으로 매긴다. 이 번호는 집사 프로세스 로컬 세션이며 msg.crowny.org에 저장하지 않는다 — "3번째 창"은 항상 "지금 이 순간 집사가 보여준 목록의 3번째 행"으로 해석(사용자가 목록을 본 직후 지시해야 정합 — 목록과 지시 사이 시간차가 크면 번호가 밀릴 수 있음을 UI에 고지). 더 안정적인 식별을 원하면 SID 앞 4~6자리 또는 경로 basename("crownyc 창들")을 병기해 번호+별칭 이중 참조를 지원한다.
  • 명령 문법:
  • "3번 창에 <지시>" / "3번에 <지시>" → 목록의 3번째 행 SID를 추출 → 크라우니메신저연동.sh kpsmacs "[<SID앞8자리>] <지시>" 형태로 전송(주의: 발신자는 항상 kpsmacs 고정이므로, 세션 쪽 --수신 필터가 자기 세션명을 인식하려면 세션명 규약을 SID 기반으로 통일해야 함 — 현재 각 세션이 무슨 이름으로 --연결했는지는 세션마다 제각각이라, SID로 직접 지시하는 신규 규약이 필요하다. 이 부분은 §4에서 리스크로 재확인).
  • "전체에 <지시>" / "모든 창에 <지시>" → 브로드캐스트(방 전체에 [전체] <지시> 전송, prefix 없는 세션도 관례상 [전체]는 다 봐야 한다는 새 규약 필요 — 기존 세션들은 자기 세션명 prefix만 필터링하므로 그대로는 못 받는다. §5 리스크).
  • "crownyc 하는 창들에 <지시>" → 경로 basename 매칭(LIVE_STATUS의 workdir로 필터) → 매칭된 각 SID에 개별 전송(팬아웃).
  • "N번 창 뭐해" / "세션 목록" → 목록 재조회 및 재표시(순수 읽기, 티 자동).
  • 수신 확인 루프: 메신저는 편도 전송이라, 지시가 실제로 그 세션에 도달·반영됐는지 확인하려면 ①크라우니메신저연동.sh <SID> --수신을 집사가 대신 폴링(수 초 간격, 유한 횟수)해 해당 세션명 응답이 방에 올라오는지 보거나, ②세션 쪽이 관례상 보고를 남기는 것에 의존(마라톤 프로토콜의 7단계 "보고"가 이미 이 역할). MVP는 ②(기존 관례 의존, 확인 없음)로 시작하고, 필요해지면 ①(짧은 폴링 확인)을 추가.

  • 3. UI 설계 — 별도창 vs 도킹의 역할 분리

    • 도킹 모드(chromeToggleButler, butlerUsesWindow=NO가 기본): 현재 그대로 "페이지 어시스턴트" 역할 유지 — 활성 탭 요약(cbSummarizeActivePage)·페이지 Q&A(cbPageAsk:)·SLM 질의·네비게이션 단축(메모/계산기/시계 등). 이 모드는 오케스트레이션 패널을 노출하지 않는다 — 도킹은 브라우저 콘텐츠 폭을 줄이는(relayoutMiddleRow) 340px 좁은 패널이라 세션 목록 표를 넣기엔 협소하고, 애초 목적(지금 보는 페이지 도우미)과 결이 다르다.
    • 별도창 모드(toggleButlerWindow, butlerUsesWindow=YES, 설정 > 크라우니AI 토글): 여기만 "오케스트레이션 마스터" 역할을 추가한다. 근거: crowny-ai-main.m 132행 주석 "N2 — 집사를 별도 창으로 열기 토글(설정>크라우니AI). 기본 OFF=도킹"과 143행 "별도 창 모드일 때만 사용하는 전용 인스턴스"가 이미 도킹과 별도창을 별개 인스턴스로 분리해두었다 — 같은 CAButlerDock 클래스를 재사용하되, 별도창 인스턴스에서만 활성화되는 플래그(예: isOrchestrationWindow)를 하나 추가하면 클래스 분기 안에서 자연스럽게 두 역할이 나뉜다(신규 클래스 불필요, 최소 침습).
    • 별도창 상단에 탭 형태(또는 접이식 섹션)로 "채팅"과 "세션"을 나눌 것을 권고 — 기존 로그뷰(logAppend:who:)를 오염시키지 않고 세션 목록은 별도 영역(테이블뷰 또는 고정폭 텍스트 그리드)에 렌더링.

    4. 명령 라우팅 문법 표 (cbButlerRespond: 확장안)

    현재 cbButlerRespond:(638행)는 순차 has(@"…") 검사 체인이다. 아래 규칙을 기존 규칙보다 앞쪽(적어도 §680 "코드/함수/생성" 및 §681 범용 "?" 폴백보다 앞)에 삽입해야 오분류를 막는다 — 특히 "3번 창에 뭐 물어봐줘" 류는 "?"를 포함해 SLM 텍스트 질의로 잘못 라우팅될 위험이 크다.

    자연어 예시액션 코드(신규)정규식/키워드 힌트실행부
    "세션 목록", "지금 몇 개 창 떠있어", "창들 뭐해"sessions\|\|목록을 표시합니다has(@"세션")\|\|has(@"창") AND has(@"목록")\|\|has(@"몇 개")\|\|has(@"뭐해")LIVE_STATUS 파싱 → 표 렌더 (읽기, 티)
    "3번 창에 <지시>", "3번째 세션에 <지시>"orch\|3\|<지시>\|N번 세션에 지시를 전달합니다정규식 ^([0-9]+)\s*번(째)?\s*(창\|세션)에\s*(.+)$번호→SID 매핑 → 메신저 전송 (옴, 확인 없이 전송하되 로그에 "누구에게 뭘 보냈다" 명시)
    "전체에 <지시>", "모든 창에 <지시>"orchAll\|<지시>\|전체 세션에 브로드캐스트합니다has(@"전체")\|\|has(@"모든 창")\|\|has(@"모든 세션")방 전체 브로드캐스트(§5 리스크 검토 후 구현)
    "crownyc 하는 창들에 <지시>"orchFilter\|crownyc\|<지시>\|경로 일치 세션에 전달합니다has(@"하는 창들")\|\|has(@"작업하는 세션")workdir basename 필터 → 팬아웃
    "!<셸명령/파일작업>" (기존 ! 규약 재사용, 실행모드로 승격)localdo\|<지시>\|로컬 작업을 실행합니다(확인 필요시 대기)기존 hasPrefix:@"!" 그대로 재사용, 단 실행모드 힌트(실행/고쳐/삭제/만들어) 동반 시 A2 브릿지로 분기§2-A 4상 게이트 통과 후 실행
    "!<질문>" (기존 그대로)cli\|<질의>\|…변경 없음(기존 648행)기존 집사CLI브릿지.sh(질의응답)
    • schema/enum 값은 가드레일 규정대로 영문 액션 키(sessions/orch/orchAll/orchFilter/localdo)를 쓰고, 사용자에게 보이는 라벨만 한글로 렌더링.
    • "3번 창에"의 정규식은 has()(부분 문자열 포함 검사) 체계와 안 맞으므로, 이 신규 규칙만 NSRegularExpression으로 별도 전처리(기존 688행 도메인 판정 블록이 이미 정규식을 쓰고 있어 선례가 있음)해 매치되면 즉시 return, 매치 안 되면 기존 체인 계속.

    5. 단계적 구현 로드맵

    단계범위크기리스크선행조건
    MVP별도창에 "세션" 패널: LIVE_STATUS 파싱해 번호 매긴 표 렌더(읽기 전용) + "N번 창에 <지시>" 파싱 → 크라우니메신저연동.sh로 전송(수신확인 없음, 편도)작음 — 기존 스크립트 재사용, 신규 코드는 파싱+정규식 라우팅뿐낮음(읽기 전용 + 기존 편도 전송 재사용)isOrchestrationWindow 플래그 분기(§3), 세션명 규약 확정(SID 기반 통일, §1-1 갭 해소)
    1단계 — 로컬 셸(게이트)2-A의 A2 분리 브릿지 + 4상 위험도 게이트(.한선 정본) 구현, "티(읽기)"만 자동 실행중간 — 신규 판정 로직(.한선) + 신규 브릿지 셸중간(읽기 명령이라도 정보 유출 가능성, 명령 화이트리스트 필요)MVP 완료, 위험도 판정 함수 자체시험
    2단계 — 쓰기 작업 확인 흐름"옴(쓰기)" 등급 명령을 집사 UI에 확인 버튼/문구로 노출 후 실행. 실행 결과를 로그(psv)에 남김중간높음(실수로 확인 없이 실행되는 UI 버그가 곧 RCE급) — 반드시 "명시적 클릭"만 인정, 텍스트 자동확인 금지1단계 게이트가 실측 오탐 0 확인된 후
    3단계 — 수신확인 루프 + 팬아웃/브로드캐스트"전체에"/"crownyc 하는 창들에" 팬아웃, 짧은 폴링으로 응답 도착 여부 확인해 UI에 "N개 세션 확인, M개 응답 대기" 표시중간~큼(폴링 타임아웃/부분실패 처리)중간(방 오염 — §6)세션명 표준화(§1-1), 방 규약 사장님 승인
    4단계(보류, 미착수 권고)타(거부) 등급 명령(rm/sudo/네트워크발신)을 별도 승인경로로 열기매우 높음사장님 명시 승인 없이는 설계도 하지 않음 — 이번 문서도 이 등급은 "자동 거부"로만 설계, 승인경로 자체를 만들지 않는 것을 권고
    권고: MVP만으로도 사장님이 요청한 두 가지("10개 창 뭐하는지 보기", "3번째 창에 명령하기")의 핵심을 충족한다. 로컬 셸 실행(1~2단계)은 위험도가 다른 축이라 MVP와 분리 착수를 권고 — 특히 2단계는 확인 UI 설계가 부실하면 그대로 로컬 RCE 벡터가 되므로 별도 적대검증(사용자 승인 텍스트만으로는 부족, 명시적 버튼 클릭 필수) 없이는 진행하지 않는다.


    6. 리스크·경계

    • 무한루프(집사가 집사에게 지시): 오케스트레이션 창이 지시를 보낸 세션이 우연히 이 집사 자신의 백엔드 프로세스이거나, 지시를 받은 세션이 다시 메신저로 "전체에 지시"를 쏘면 순환이 생길 수 있다. 가드: 팬아웃/브로드캐스트 지시는 수신 세션이 다시 브로드캐스트를 트리거하지 않는다는 규약을 방 프로토콜에 명시(현재 마라톤 프로토콜은 세션이 지시를 "실행"하지 "재전파"하지는 않으므로 자연히 낮은 리스크지만, 문서화 필요).
    • 권한 상승: §2-A에서 이미 다룸 — 로컬 실행 경로는 집사 프로세스의 권한을 그대로 물려받는다. 웹페이지 콘텐츠(cbPageAsk 등 외부 신뢰불가 입력)가 실행 지시로 승격되는 경로를 만들지 않는 것이 핵심 방어선.
    • 메신저 방 오염: kps-relay 방은 사장님이 폰에서도 보는 전 세션 공용 채널이다(글로벌 CLAUDE.md "출장/원격 모니터링" 절). 오케스트레이션 창이 자동으로 자주 폴링/전송하면(§5 3단계 폴링 루프) 방이 노이즈로 채워져 사장님의 실제 지시 확인을 방해할 수 있다. 가드: 폴링은 사용자가 명시적으로 "N번 창 응답 확인해"라고 요청했을 때만, 유한 횟수(예 5회, 총 30초)로 제한하고 자동 반복 금지.
    • 자기 세션 지시(집사 자신을 오케스트레이션 대상으로 착각): 별도 집사창 자체는 LIVE_STATUS에 등록된 "클로드코드 세션"이 아니라 CrownyAI 앱 프로세스이므로 목록에 나타나지 않는다 — 혼동 소지는 낮지만, "0번 창"처럼 자기 자신을 가리키는 표현이 나올 경우 명확히 "집사는 목록에 없습니다"로 안내.
    • 세션명/SID 불일치: §2-B에서 지적했듯, 각 세션이 크라우니메신저연동.sh에 어떤 이름으로 --연결했는지 제각각이라("게이트웨이개선", "KPS코드" 등 자유 텍스트) 집사가 LIVE_STATUS의 SID로 지시를 보내도 그 세션이 자기 SID를 자기 세션명으로 --수신 필터링하고 있지 않으면 지시를 못 받는다. 이 규약 정렬(세션명=SID 통일 또는 별도 매핑 테이블)이 없으면 MVP도 실제로는 "보내기는 했지만 아무도 못 받음"이 될 수 있다 — MVP 착수 전 필수 확인 과제로 명시.
    • 읽기 전용이라도 정보 유출: "3번 창에 '~/.ssh 내용 보여줘'라고 시켜"처럼, 오케스트레이션 자체는 읽기 명령 전달이라도 그 세션이 민감 정보를 읽어 방에 다시 보고하면 방 전체(사장님 포함 관찰자)에 노출된다. 이 리스크는 집사 UI 설계로 막을 수 없고, 수신 세션 쪽의 판단(각 세션도 이미 CLAUDE.md 가드레일 하에 있음)에 의존 — 문서화만 하고 기술적 강제는 이번 범위 밖.

    관련 파일

    • /Users/ef/.claude/knowledge/LIVE_STATUS.md — 세션 실시간 상태(5분 갱신)
    • /Users/ef/.claude/knowledge/PLAN.md — 태스크 보드
    • /Users/ef/.claude/scripts/crowny_knowledge.py — 세션 register/heartbeat/status/plan 전체 구현(cmd_register 92행, cmd_heartbeat 123행, cmd_status 212행, _generate_live_status 286행)
    • /Users/ef/.claude/scripts/크라우니메신저연동.sh — 세션↔msg.crowny.org(kps-relay) 연결/보고/수신
    • /Users/ef/CrownyBrowser/native/crowny-ai-butler.m — CAButlerDock 전체(cbButlerRespond: 638행, cbExecuteButlerAction: 706행, cbRunLocalCLI: 812행, cbPageAsk: 894행)
    • /Users/ef/CrownyBrowser/native/crowny-ai-main.mtoggleButlerWindow(471행)·chromeToggleButler(518행)·별도창/도킹 인스턴스 분리(132~143행)
    • /Users/ef/CrownyBrowser/tools/집사CLI브릿지.sh — 로컬 CLI 브릿지(질의응답 전용, 4상+exit코드 규약)
    • /Users/ef/crowny-butler/크라우니마라톤.md — 유사 오케스트레이션 개념(단일 세션 내 작업큐+메신저 수신, 참고용 — 멀티세션 팬아웃은 아님)
    • /Users/ef/crowny-butler/명령브릿지.sh — 한국어 명령→intent 매칭 패턴 참고(용도 다름, 그대로 재사용 불가)
    • /Users/ef/.claude/templates/한선씨-가드레일.md — 신규 로직(.한선 정본화)·공유인프라 무접촉 원칙 SSOT

    잔여 이슈

    1. 세션명/SID 정렬 문제(§6) — MVP 착수 전 반드시 해소해야 "지시가 실제로 도달"함.
    2. LIVE_STATUS task 필드 품질 낮음(§1-1) — 집사 UI가 보여줄 내용 자체가 빈약할 수 있음. 별도 과제로 하트비트 훅이 실제 작업 텍스트를 채우도록 개선하는 것을 사장님께 별도 제안할 가치 있음(이번 문서 범위 밖).
    3. CrownyBrowser.app 샌드박스/entitlements 확인(§2-A) — NSTask 기반 로컬 실행이 실제로 가능한 권한 모드인지 구현 착수 전 확인 필요.
    4. 본 문서는 설계 검토 산출물이며 코드/설정 변경은 전혀 하지 않았다(touchedExistingFiles=[]).