OMC (oh-my-claudecode) · OMC 고유 기능: Teams 런타임/브리지 (프로세스 기반 멀티에이전트 + 거버넌스)

한 줄 요약

OMC의 “팀 리더(메인 Claude)“가 tmux 창마다 진짜 codex/gemini CLI를 별도 프로세스로 띄워 워커로 부리되, 출입증(권한)·CCTV(감사로그)·사규(거버넌스)로 통제하는 멀티에이전트 런타임이다. 왜 배우나: “여러 AI를 동시에 일 시키는 것”과 “그걸 안전하게 통제하는 것”이 어떻게 한 시스템에서 만나는지 보여주는 OMC의 시그니처 설계이기 때문이다.


그림

flowchart TD
  A["사용자: /team N:codex 작업"] --> B["리더가 런타임 잡 기동<br/>spawn(runtime-cli.cjs)"]
  B --> C["tmux 패인 N개 생성<br/>(창 분할)"]
  C --> D["각 패인: team-bridge.cjs<br/>--config config.json"]
  D --> E["워커 폴 루프 while(true)<br/>심장박동: polling→ready→executing"]
  E --> F["할 일 집기<br/>findNextTask: 파일락으로 원자 클레임"]
  F --> G["프롬프트 만들기<br/>TASK 태그 + 보안 sanitize"]
  G --> H["진짜 CLI 실행<br/>codex exec --json / gemini --yolo"]
  H --> I["결과 추출<br/>parseCodexOutput → outbox + 결과파일"]
  I --> J["리더가 결과 파일 읽고 다음 단계로"]
  E -.매 전이마다.-> K["감사로그 audit()<br/>team-bridge-팀명.jsonl<br/>(추가만 가능, 0o600)"]
  F -.작업이 막연하면.-> L["isBroadTeamTaskText →<br/>자동 위임 플랜 부여"]

쉽게 풀기

작은 사무실 하나를 떠올려 보자. 팀장 한 명이 있고, 일이 몰리면 외주 작업자를 여러 명 불러 나눠 시킨다. OMC의 Teams 런타임이 바로 이 사무실이다.

1) 워커를 만드는 두 가지 길이 있다.

  • (a) 네이티브 방식: Claude Code가 기본으로 주는 도구(TeamCreate / SendMessage / Task*)로, Claude “안에서” 가벼운 서브에이전트를 만든다. 같은 회사 사람을 잠깐 빌려 쓰는 느낌이다.
  • (b) 브리지 방식(이게 OMC 시그니처): tmux(터미널 창을 여러 칸으로 쪼개는 도구)의 칸마다 진짜 claude / codex / gemini 프로그램을 별개의 OS 프로세스로 띄운다. 즉 OpenAI Codex, Google Gemini 같은 타사 AI도 외주 작업자로 합류시킨다.

2) 왜 일부러 따로 두었나. (b) 방식은 일반 Claude 도구 묶음(t MCP 서버)과 섞이지 않고, 별도의 ‘team’ MCP 서버에 산다. 이유는 단순하다 — 위험하고 무거운 “진짜 프로세스 띄우기” 기능을, 평소 쓰는 가벼운 도구들과 한 바구니에 담지 않으려는 안전 격리다.

3) 그냥 프로세스만 띄우는 게 아니다. 사무실로 비유하면 세 가지 통제 장치가 동시에 돈다.

  • 출입증(권한): 워커가 건드려도 되는 폴더/명령만 허용한다.
  • CCTV(감사로그): 누가 언제 무엇을 했는지 한 줄씩 기록을 남긴다. 이 기록은 “추가만 가능(append-only)“이라 나중에 몰래 고칠 수 없다.
  • 사규(거버넌스): “팀장은 직접 코딩 말고 위임만 해라”, “팀은 세션당 하나만” 같은 규칙을 강제한다.

4) 막연한 일은 자동으로 쪼개게 만든다. “코드베이스 좀 정리해줘”처럼 두루뭉술한 지시가 들어오면, 판별기(isBroadTeamTaskText)가 “이건 너무 광범위하다”고 판단해 자동으로 위임 플랜을 붙인다. 작업자가 혼자 끙끙대지 않고 병렬로 나눠 탐색하게 유도하는 장치다.

지금은 일부가 은퇴(retired) 상태

현 코드 기준 team-mcp.cjs의 MCP 런타임 도구 4종(omc_run_team_start/status/wait/cleanup)은 DEPRECATED로 표시되어 CLI(omc team ...)로 이관됐다. 설치 레지스트리도 bridge/team-mcp.cjs 경로의 MCP 엔트리를 은퇴 처리해 자동 제거한다(RETIRED_TEAM_MCP_PATH_PATTERN). 다만 워커 실행체(team-bridge.cjs)와 거버넌스/감사 형식은 그대로 살아 있다. 즉 “리모컨(MCP 도구)“은 CLI로 옮겨졌지만 “엔진(브리지)“은 동일하다.


핵심 정리

거버넌스 기본값 — 무엇을 강제하나

규칙기본값한 줄 의미
delegation_onlyfalse리더가 직접 작업 못 하고 위임만
plan_approval_requiredfalse실행 전 계획 승인 게이트
nested_teams_allowedfalse워커가 또 팀 만드는 중첩 금지
one_team_per_leader_sessiontrue리더 세션당 팀 1개
cleanup_requires_all_workers_inactivetrue모두 쉴 때만 정리 허용

전송/런타임 정책( TeamTransportPolicy) 기본값

  • display_mode = split_pane (tmux 표시 방식, 또는 auto)
  • worker_launch_mode = interactive (또는 prompt)
  • dispatch_mode = hook_preferred_with_fallback (또는 transport_direct)
  • dispatch_ack_timeout_ms = 15000 (디스패치 ACK 대기 한도)

레거시 매니페스트에서는 transport와 governance가 한 객체에 섞여 있었고, normalizeTeamGovernance()governance ?? legacyPolicy ?? DEFAULT 순으로 흡수한다.

워커 config.json — 브리지가 읽는 항목

필드필수기본값 / 메모
teamName / workerNamesanitizeName 처리됨
providercodex 또는 gemini (claude는 별 경로)
workingDirectory반드시 git worktree 안
pollIntervalMs3000
taskTimeoutMs600000 (CLI 작업 타임아웃)
maxConsecutiveErrors3 초과 시 self-quarantine
permissionEnforcementoff / audit / enforce
permissionsallowedPaths/deniedPaths/allowedCommands (**,* 위험 패턴 거부)

감사 이벤트(AuditEvent) — CCTV 기록 한 줄의 구성

형식과 보관

저장 형식은 append-only JSONL, 권한 0o600, 5MB 초과 시 절반만 보존하며 회전. 경로는 getOmcRoot(cwd)/logs/team-bridge-<teamName>.jsonl. 한 줄에는 timestamp(ISO)·eventType·teamName·workerName(필수)과 taskId·details(선택)가 담긴다.

AuditEventType 19종 (모든 상태 전이에서 한 줄씩 기록):

  • 생애주기: bridge_start · bridge_shutdown · worker_ready · worker_idle · worker_quarantined
  • 작업: task_claimed · task_started · task_completed · task_failed · task_permanently_failed
  • 메일함: inbox_rotated · outbox_rotated
  • CLI 실행: cli_spawned · cli_timeout · cli_error
  • 종료/권한: shutdown_received · shutdown_ack · permission_violation · permission_audit

막연한 작업 판별(isBroadTeamTaskText) — 어떻게 “광범위”로 보나

판정 규칙

  • 단어 4개 미만 → 광범위 아님
  • 좁은 코드 타깃(파일명·심볼)이 있고 단어 < 12 → 광범위 아님
  • broad 동사(investigate/analyze/debug/review/refactor/build/implement 등) 매칭 → 광범위
  • fix + broad 객체(runtime/system/codebase/architecture/tests 등) 동시 매칭 → 광범위
  • 광범위로 판정되면 {mode:'auto', required_parallel_probe:true, skip_allowed_reason_required:true, child_report_format:'bullets'} 부여

실제 예시

워커가 진짜 CLI를 띄우는 핵심 (시그니처)

// bridge/team-bridge.cjs  (spawnCliProcess)
function spawnCliProcess(provider, prompt, model, cwd, timeoutMs) {
  validateProvider(provider);
  validateModelName(model);
  let args; let cmd;
  if (provider === "codex") {
    cmd = "codex";
    args = ["exec", "-m", model || getBuiltinExternalDefaultModel("codex"),
      "--json", "--dangerously-bypass-approvals-and-sandbox", "--skip-git-repo-check"];
  } else {
    cmd = "gemini";
    args = ["--approval-mode", "yolo"];
    if (model) args.push("--model", model);
  }
  const child = (0, import_child_process5.spawn)(cmd, args, { stdio: ["pipe","pipe","pipe"], cwd });
  // ...stdin에 prompt 주입, stdout JSON 이벤트 파싱(parseCodexOutput), timeoutMs로 SIGTERM kill...
}

거버넌스 기본값

// src/team/governance.ts
export const DEFAULT_TEAM_GOVERNANCE: TeamGovernance = {
  delegation_only: false,
  plan_approval_required: false,
  nested_teams_allowed: false,
  one_team_per_leader_session: true,
  cleanup_requires_all_workers_inactive: true,
};

’team’ 도구가 메인 ‘t’ 서버에서 일부러 빠진 이유 (주석)

// src/mcp/tool-registry.ts (헤더 주석)
 * Team runtime tools (omc_run_team_start, omc_run_team_status) are intentionally
 * excluded: they live in the separate "team" MCP server (bridge/team-mcp.cjs).

실제로 .mcp.jsont 서버(bridge/mcp-server.cjs)만 등록한다 — team 런타임은 분리된 서버/CLI다.

복붙용 최소 워커 config (브리지가 --config로 읽는 형식)

{
  "teamName": "demo-team",
  "workerName": "w1",
  "provider": "codex",
  "model": "gpt-5.3-codex",
  "workingDirectory": "/abs/path/to/git/worktree",
  "pollIntervalMs": 3000,
  "taskTimeoutMs": 600000,
  "maxConsecutiveErrors": 3,
  "permissionEnforcement": "enforce",
  "permissions": {
    "allowedPaths": ["src/**", "tests/**"],
    "deniedPaths": [".git/**", ".env*", "**/secrets/**"],
    "allowedCommands": [],
    "maxFileSize": 1048576
  }
}

거버넌스 매니페스트 조각

{
  "schema_version": 2,
  "governance": {
    "delegation_only": true,
    "plan_approval_required": true,
    "nested_teams_allowed": false,
    "one_team_per_leader_session": true,
    "cleanup_requires_all_workers_inactive": true
  },
  "policy": { "display_mode": "split_pane", "worker_launch_mode": "interactive",
    "dispatch_mode": "hook_preferred_with_fallback", "dispatch_ack_timeout_ms": 15000 }
}

직접 만들 때 체크리스트

  • 메인 도구 서버(t)에 team 런타임 도구를 넣지 말 것 — 별도 ‘team’ 서버/CLI로 분리(tool-registry 주석 원칙).
  • 워커 프로세스는 child_process.spawn으로 진짜 CLI(codex exec --json / gemini --approval-mode yolo)를 띄운다.
  • config 경로는 ~/.claude 또는 ~/.omc 하위만 허용, cwd는 git worktree 안인지 검증.
  • 감사 로그: append-only JSONL, 0o600, getOmcRoot/logs/team-bridge-<team>.jsonl, 5MB 회전, validateResolvedPath로 traversal 차단.
  • 이벤트 타입은 enum으로 고정(bridge_start … permission_audit 19종), 모든 상태 전이에서 audit() 호출.
  • 작업 클레임은 파일락(acquireTaskLock, O_EXCL, stale 30s + PID 생존확인)으로 원자화.
  • 프롬프트는 <TASK_*> 태그 + sanitizePromptContent로 인젝션 무력화.
  • 광범위 작업(isBroadTeamTaskText)이면 자동 위임 플랜 부여.
  • 거버넌스 기본값: one_team_per_leader_session/cleanup_requires_all_workers_inactive만 true, 나머지 false.
  • SIGINT/SIGTERM에서 heartbeat 삭제 + unregisterMcpWorker로 정리.

요약 & 셀프체크

3줄 요약:

  1. OMC Teams는 tmux 패인마다 진짜 codex/gemini CLI를 별도 프로세스로 띄워 타사 AI까지 워커로 합류시킨다.
  2. 위험한 프로세스 기능이라 일반 ‘t’ 도구 서버와 격리된 별도 ‘team’ 서버/CLI에 두고, 권한·감사로그·거버넌스로 통제한다.
  3. 막연한 작업은 isBroadTeamTaskText가 자동으로 위임 플랜을 붙여 병렬 탐색을 유도한다.

스스로 답해보기:

  • 네이티브 방식과 브리지 방식의 가장 큰 차이는 무엇이고, 브리지 방식이 가능케 하는 일은? (힌트: 별개 OS 프로세스, 타사 AI 합류)
  • team 런타임 도구를 메인 ‘t’ 서버에서 일부러 뺀 이유를 한 문장으로?
  • 감사로그가 “추가만 가능(append-only)·0o600”인 것이 거버넌스 관점에서 왜 중요한가?

연결

OMC_개요 · _분석축_루브릭


근거 파일

  • /home/seunghyeong/harness-work/oh-my-claudecode/bridge/team-mcp.cjs (handleStart spawn runtime-cli.cjs, TOOLS 4종 DEPRECATED, new Server({name:"team"}), sendToWorker send-keys)
  • /home/seunghyeong/harness-work/oh-my-claudecode/bridge/team-bridge.cjs (runBridge 폴 루프, spawnCliProcess codex/gemini, formatPromptTemplate, audit, main config 검증)
  • /home/seunghyeong/harness-work/oh-my-claudecode/src/team/governance.ts (DEFAULT_TEAM_GOVERNANCE, DEFAULT_TEAM_TRANSPORT_POLICY, normalize*)
  • /home/seunghyeong/harness-work/oh-my-claudecode/src/team/audit-log.ts (AuditEventType enum, AuditEvent, 0o600 JSONL, rotateAuditLog)
  • /home/seunghyeong/harness-work/oh-my-claudecode/src/team/delegation-evidence.ts (isBroadTeamTaskText, BROAD_TASK_DELEGATION_PLAN, 정규식)
  • /home/seunghyeong/harness-work/oh-my-claudecode/src/team/types.ts (TeamGovernance/TeamTransportPolicy/TeamTaskDelegationPlan/McpWorkerMember/HeartbeatData/TeamManifestV2)
  • /home/seunghyeong/harness-work/oh-my-claudecode/src/team/permissions.ts (SECURE_DENY_DEFAULTS, isPathAllowed, findPermissionViolations — team-bridge.cjs에 인라인)
  • /home/seunghyeong/harness-work/oh-my-claudecode/src/mcp/tool-registry.ts (team 런타임 도구 의도적 제외 주석)
  • /home/seunghyeong/harness-work/oh-my-claudecode/src/installer/mcp-registry.ts (RETIRED_TEAM_MCP_PATH_PATTERN, isRetiredTeamMcpEntry)
  • /home/seunghyeong/harness-work/oh-my-claudecode/.mcp.json (t 서버만 등록)
  • /home/seunghyeong/harness-work/oh-my-claudecode/skills/omc-teams/SKILL.md (CLI-team 사용법, claude/codex/gemini)