# mogui-ADE-orchestrator 문서

> Orca ADE 위에서 장기 마스터 세션, 워커 디스패치, 승계, 라인지를 운영하는 워크스페이스 오케스트레이션 런타임의 공개 표면, 설치, CLI, 설정, 운영 절차를 정리한 기술 문서입니다. 설치·운영자, 마스터 에이전트, 기여자가 스크립트·스키마·가드 경계를 그대로 참조할 때 사용합니다.

## Context Links

- [Agent index](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/llms.txt)
- [Human interactive docs](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac)

## Repository Metadata

- Repository: local/mogui-ADE-orchestrator

- Generated: 2026-08-07T07:09:27.222Z
- Updated: 2026-08-07T08:06:25.549Z
- Runtime: Grok CLI
- Format: Documentation
- Pages: 22

## Page Index

- 01. [Overview](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/01-overview.md) - 공개 표면, Orca 런타임 전제, 마스터/워커 역할, 네트워크·API 키 없음 제약, 그리고 다음에 읽어야 할 문서 경로.
- 02. [Installation](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/02-installation.md) - Orca·에이전트 CLI·git·gh·python3·bd·skills 전제조건, preflight FAIL/WARN, 셸 명령 등록, 클론 후 측정 신호.
- 03. [Quickstart](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/03-quickstart.md) - 클론 → Orca 준비 → wake-up 온보딩 → Generation 1 부트 → 첫 supervised worker까지 최단 성공 경로와 검증 신호.
- 04. [런타임 유닛](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/04-page-4.md) - U1–U12 책임 경계, L0/L1 컨텍스트, 부분 구현 유닛, bootstrap·dispatch·succession·lineage 모듈 대응표.
- 05. [Orca 객체 모델](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/05-orca.md) - project·folder workspace·worktree·terminal·Run, 마스터 좌석 규칙, 검증된 selector 형식, 오해하기 쉬운 UI 라벨.
- 06. [마스터 라이프사이클](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/06-page-6.md) - founding spawn → boot measurement → steady state → clean succession → lineage 기록 루프와 Role State·compaction 규칙.
- 07. [증거와 수락](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/07-page-7.md) - 워커 self-report와 독립 검증 구분, 계약 필드, 리뷰 렌즈, acceptance 판정 규칙과 측정 가능한 증거 형태.
- 08. [프로그레시브 온보딩](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/08-page-8.md) - ONBOARDING 라우터, 단계 파일 1회 1로드·Verify 후 진행, Stage 1/2, founding spawn, 템플릿 치환 경계.
- 09. [Supervised dispatch](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/09-supervised-dispatch.md) - check → dispatch → register 흐름, 계약 해시·ledger, model probe, Codex/Cursor pretrust, 완료 채널 orchestration.
- 10. [Clean succession](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/10-clean-succession.md) - handoff 작성, placement 검증 spawn, successor 검증, predecessor retire, revival 측정, lineage 스키마 필드.
- 11. [인스턴스 설정](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/11-page-11.md) - instance-runtime·model-tier-policy 작성, env 우선순위, transcript glob, master host runtime, 티어×fan-out 캡.
- 12. [Workspace descriptor](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/12-workspace-descriptor.md) - sibling 저장소 인벤토리, role·capabilities·prohibited, master_seat, workspace-descriptor-check 액션과 해석 순서.
- 13. [Redaction gates](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/13-redaction-gates.md) - redaction-scan 범위·exit, REDACTION_REQUIRE_EXTRA, inventory 역검사, gitleaks 설정, 스테이징 전제와 pre-push 훅.
- 14. [CLI 레퍼런스](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/14-cli.md) - scripts/ 공개 명령 표, 하위 명령 목적, 주요 옵션, --help 동기화 테스트 계약과 비공개 표면 경계.
- 15. [Configuration reference](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/15-configuration-reference.md) - JSON 키·기본값·필수/선택, INSTANCE_RUNTIME_CONFIG·DISPATCH_TIER_POLICY·WORKSPACE_DESCRIPTOR·MOGUI_* 환경 변수 해석 순서.
- 16. [dispatch-gate 레퍼런스](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/16-dispatch-gate.md) - check·register·watch·report 플래그, ledger 스키마, reason code, 티어 정책 해석, 티켓 TTL, 기본 문자 한도.
- 17. [master-succeed 레퍼런스](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/17-master-succeed.md) - detect·handoff·verify-successor·check-duplicates·retire·spawn 옵션, exit 코드, SPAWN_PLACEMENT_MISMATCH, JSON 출력.
- 18. [acceptance-loop 레퍼런스](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/18-acceptance-loop.md) - validate·split·run·inspect 하위 명령, 스위트 구조, holdout, max-iterations, baseline/restore, 판정 산출물.
- 19. [방어 인벤토리](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/19-page-19.md) - 디스패치 게이트, 모델 신원 프로브, placement·empty-seat·duplicate, redaction, revival, progressive onboarding 가드 표.
- 20. [Worker reap](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/20-worker-reap.md) - lease 상태 issued→reaped, settled 검증, terminal close, worktree 정리, --dry-run·--ledger, 거부 exit 코드.
- 21. [Troubleshooting](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/21-troubleshooting.md) - preflight BLOCKED, placement mismatch, MODEL_PROBE_FAILED, undecidable exit 2, seat 중복, revival, 복구 프로브.
- 22. [Contributing](https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/22-contributing.md) - 5문항 스택 기준, pytest 실행, exit 코드 규약, redaction 게이트, master-ops 템플릿 경계, 릴리스 cut·CHANGELOG.

## Source File Index

- `.github/workflows/gates.yml`
- `AGENTS.md`
- `CHANGELOG.md`
- `config/gitleaks.toml`
- `config/instance-runtime.example.json`
- `config/model-tier-policy.example.json`
- `config/workspace-descriptor.example.json`
- `CONTRIBUTING.md`
- `docs/internal/release-runbook.md`
- `docs/internal/tooling/redaction-scan.md`
- `docs/public/concepts.md`
- `docs/public/defense-inventory.md`
- `docs/public/delegation-and-review.md`
- `docs/public/getting-started.md`
- `docs/public/master-lifecycle.md`
- `docs/public/orca-concepts.md`
- `docs/public/overview.md`
- `docs/public/reference.md`
- `docs/README.md`
- `docs/runbooks/worker-reap.md`
- `hooks/pre-push`
- `INSTALL-PROMPT.txt`
- `master-ops/docs/charter/04-worker-routing-review.md`
- `master-ops/docs/charter/05-dispatch-gate.md`
- `master-ops/docs/charter/06-succession.md`
- `master-ops/docs/MASTER-OPERATIONS.md`
- `master-ops/docs/runbooks/contract-conventions.md`
- `master-ops/docs/runbooks/error-and-logging.md`
- `master-ops/docs/runbooks/role-state.md`
- `master-ops/docs/runbooks/succession-boot-card.md`
- `master-ops/MANIFEST.json`
- `master-ops/model-tier-policy.json`
- `master-ops/ONBOARDING.md`
- `master-ops/onboarding/00-orientation.md`
- `master-ops/onboarding/01-preflight.md`
- `master-ops/onboarding/02-workspace-facts.md`
- `master-ops/onboarding/04-seat.md`
- `master-ops/onboarding/09-spawn.md`
- `master-ops/onboarding/10-card-and-retire.md`
- `master-ops/scripts/dispatch`
- `README.md`
- `scripts/acceptance-loop`
- `scripts/adapter`
- `scripts/codex-worker-pretrust`
- `scripts/cursor-worker-pretrust`
- `scripts/dispatch-gate`
- `scripts/generate-manifest`
- `scripts/l1-digest`
- `scripts/master-bootstrap`
- `scripts/master-bootstrap-live`
- `scripts/master-recover`
- `scripts/master-succeed`
- `scripts/model-drift-audit`
- `scripts/model-identity-probe`
- `scripts/next-version`
- `scripts/onboarding-preflight.sh`
- `scripts/redaction-allowlist.txt`
- `scripts/redaction-inventory`
- `scripts/redaction-scan.sh`
- `scripts/worker-reap`
- `scripts/workspace-descriptor-check`
- `SECURITY.md`
- `src/master_runtime/core/__init__.py`
- `src/master_runtime/core/acceptance/casebook.py`
- `src/master_runtime/core/acceptance/config.py`
- `src/master_runtime/core/acceptance/loop.py`
- `src/master_runtime/core/acceptance/verdict.py`
- `src/master_runtime/core/adapter/doctor.py`
- `src/master_runtime/core/approval/gates.py`
- `src/master_runtime/core/bootstrap_live.py`
- `src/master_runtime/core/bootstrap.py`
- `src/master_runtime/core/context/resolver.py`
- `src/master_runtime/core/dispatch_gate.py`
- `src/master_runtime/core/instance_runtime_config.py`
- `src/master_runtime/core/lineage.py`
- `src/master_runtime/core/recovery.py`
- `src/master_runtime/core/succession.py`
- `src/master_runtime/core/work_ledger.py`
- `src/master_runtime/core/worker_reap.py`
- `src/master_runtime/core/workspace_descriptor.py`
- `tests/conftest.py`
- `tests/test_acceptance_loop.py`
- `tests/test_dispatch_gate.py`
- `tests/test_documented_reason_codes.py`
- `tests/test_instance_runtime_config.py`
- `tests/test_onboarding_preflight.py`
- `tests/test_onboarding_structure.py`
- `tests/test_recovery.py`
- `tests/test_redaction_scan_native_script.py`
- `tests/test_reference_command_table.py`
- `tests/test_succession.py`
- `tests/test_worker_reap.py`
- `tests/test_workspace_descriptor.py`

---

## 01. Overview

> 공개 표면, Orca 런타임 전제, 마스터/워커 역할, 네트워크·API 키 없음 제약, 그리고 다음에 읽어야 할 문서 경로.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/01-overview.md
- Generated: 2026-08-07T07:02:12.892Z

### Source Files

- `README.md`
- `docs/public/overview.md`
- `docs/README.md`
- `AGENTS.md`
- `src/master_runtime/core/__init__.py`
- `INSTALL-PROMPT.txt`

---
title: "Overview"
description: "공개 표면, Orca 런타임 전제, 마스터/워커 역할, 네트워크·API 키 없음 제약, 그리고 다음에 읽어야 할 문서 경로."
---

`mogui-ADE-orchestrator`는 멀티 저장소 워크스페이스에서 장수명 에이전트 세션을 조율하는 **워크스페이스 마스터 런타임**이다. 모델 API를 호출하지 않으며 in-process 에이전트 프레임워크가 아니다. 실행 단위는 Orca ADE의 실제 터미널·worktree·orchestration task/mailbox이고, 의사결정 로직은 `src/master_runtime/core/`의 stdlib-only Python과 `scripts/` CLI에 있다.

## 공개 표면

설치·운영·기여가 만나는 표면은 네 층으로 나뉜다.

| 표면 | 경로 | 역할 |
| --- | --- | --- |
| 공개 CLI | `scripts/` | 부트, succession, dispatch gate, acceptance, redaction, descriptor, worker reap |
| 코어 라이브러리 | `src/master_runtime/core/` | 순수/주입 가능한 유닛 로직 (bootstrap, succession, lineage, dispatch_gate, acceptance, …) |
| 공개 설명 문서 | `docs/public/` | 문제·개념·수명주기·위임·레퍼런스 (이 저장소에 고정) |
| 운영 템플릿 | `master-ops/` | 온보딩이 복사·치환해 별도 ops 저장소를 만드는 Stage 2 템플릿 |

```text
mogui-ADE-orchestrator/
├── scripts/                 # 공개 명령 표면
├── src/master_runtime/core/ # U1–U12에 대응하는 런타임 유닛
├── config/                  # instance-runtime·tier-policy·descriptor 예제
├── docs/public/             # 인간/에이전트용 설명
├── master-ops/              # 온보딩 라우터 + 치환 템플릿
└── tests/                   # 유닛·CLI·게이트 계약 테스트
```

`docs/internal/`은 기여자 메모·스펙이며 코드와 어긋나면 **코드와 테스트가 우선**이다. `master-ops/`의 `{{PLACEHOLDER}}`는 이 저장소 안에서 채우지 않는다. Stage 1 온보딩이 새 ops 저장소로 복사한 뒤에만 치환한다.

### 공개 CLI (요약)

| 명령 | 목적 |
| --- | --- |
| `scripts/master-bootstrap` / `master-bootstrap-live` | 경계 있는 L0/L1 부트 블록 생성, session-start 훅 |
| `scripts/master-succeed` | succession detect · handoff · spawn · verify · retire · duplicates |
| `scripts/dispatch-gate` | worker dispatch `check` → `register` · `watch` · `report` |
| `scripts/acceptance-loop` | casebook 기반 독립 수락 루프 |
| `scripts/worker-reap` | settled worker terminal/worktree 정리 |
| `scripts/model-identity-probe` / `model-drift-audit` | transcript 기반 모델 신원·드리프트 |
| `scripts/workspace-descriptor-check` | sibling repo 금지 액션 조회 |
| `scripts/redaction-scan.sh` / `redaction-inventory` | 게시 전 redaction 게이트 |
| `scripts/onboarding-preflight.sh` | founding 전 도구·런타임 측정 |
| `scripts/adapter doctor` | 어댑터 계층 로컬 도구 존재 여부 |

전체 하위 명령·플래그·exit 코드는 [CLI 레퍼런스](/cli-reference)를 본다. 표 행은 `tests/test_reference_command_table.py`가 `scripts/* --help`와 동기화한다.

## Orca 런타임 전제

Orca는 이 시스템이 추상화하지 않는 **필수 실행 기판**이다. `master-ops/ONBOARDING.md`는 “Orca is REQUIRED infrastructure. Supervised dispatch = orca orchestration only.”로 고정한다. Step 0 preflight(`scripts/onboarding-preflight.sh`)는 사용 가능한 `orca`와 ready 런타임이 없으면 founding을 `BLOCKED`로 막는다.

| 능력 | Orca 없이 | Orca 와 함께 |
| --- | --- | --- |
| 세션 수명 | 창/탭에 묶임 | 창을 닫아도 handle로 주소 지정 가능 |
| 완료 감지 | 화면 폴링 | Run mailbox (`worker_done` 등) |
| 배치(placement) | 추론·cwd 추측 | worktree/folder workspace selector로 검증 |
| 마스터 세션 | 지원하지 않음 | founding spawn → Gen-1 master |

<Warning>
stdlib 순수 함수(`dispatch-gate check`, `master-succeed detect` 등)는 Orca 없이 호출 가능하다. 라이브 마스터 세션·supervised dispatch·retire/spawn은 Orca 없이 지원하지 않는다.
</Warning>

Orca 객체 모델의 최소 어휘:

| 객체 | 의미 |
| --- | --- |
| Project | Orca에 등록한 폴더(단일 저장소, sibling 다수 저장소 루트, 빈 폴더) |
| Folder workspace / repository worktree | 세션이 앉는 seat. 마스터는 보통 workspace-level folder workspace |
| Terminal | 에이전트가 돌아가는 라이브 세션 |
| Run | task·dispatch·mailbox를 담는 내구성 orchestration 컨텍스트 |

선택자 형식·“Unavailable worktree” 라벨 오해는 [Orca 객체 모델](/orca-object-model)에 정리한다.

## 마스터와 워커

### 마스터 (임시 역할)

마스터는 프로세스 이름이 아니라 **운영 역할**이다. 온보딩 시 callsign을 고른다(문서·UI 라벨 “master”는 역할 표기). 책임:

- 계획·작업 분해·크로스 저장소 라우팅
- 계약 기반 worker dispatch와 예산/티어 게이트
- worker self-report와 **독립된** 증거 검증·수락
- context pressure 시 clean succession (advisory는 제안만, auto-succeed 없음)
- Role State·charter·tracker/git 기록 유지

부트는 `bootstrap` / `bootstrap_live`가 charter·handoff·Role State로 경계 있는 L0/L1 블록을 만든다. compaction 시 Role State/active-tracks를 의도적으로 생략해 recall probe를 건다.

### 워커

워커는 Orca pane 안의 **실제 CLI 세션**이다(Claude, Codex, Cursor, Grok, Gemini 등). 플러그인 없이 터미널에서 기동한 바이너리 그대로다. 워커는 좁은 **계약 파일**을 받고 artifact+증거를 반환한다. 격리 단위는 git worktree이며 가상 파일시스템이 아니다.

```text
check  →  dispatch  →  register  →  독립 검증  →  acceptance
 (gate)   (Orca)       (probe)      (master)      (casebook/리뷰)
```

`dispatch_gate`의 `ReasonCode` 예: `OK`, `BUDGET_EXCEEDED`, `TIER_FANOUT_CAP`, `NO_COMPLETION_CHANNEL`, `CONTRACT_UNREADABLE`, `PATH_OUTSIDE_KNOWN_ROOTS`, `MODEL_PROBE_FAILED`, …

### 역할 경계 (형제 프로젝트)

| | 이 저장소 | mogui-agent-harness (형제) |
| --- | --- | --- |
| 계층 | Workspace Master Runtime | Repository Harness |
| 단위 | 다수 저장소 워크스페이스 | 단일 저장소 |
| 소유 | 조율 상태, 역할, succession, lineage, dispatch | repo-local rules, hooks, wiki, runbooks |
| 결합 | **계약만** — 소스 트리 결합 없음 | 동일 |

```mermaid
flowchart TB
  subgraph WS["워크스페이스 계층"]
    M["master session<br/>plan · gate · verify · succeed"]
  end
  subgraph Orca["Orca"]
    WT1["worker worktree / CLI"]
    WT2["worker worktree / CLI"]
    M2["successor master"]
  end
  subgraph Durable["내구성 상태"]
    T[("tracker / ledger")]
    G[("git: charter · decisions · lineage")]
  end
  You(["operator"]) -->|"지시"| M
  M -->|"contract"| WT1
  M -->|"contract"| WT2
  WT1 -->|"artifact + evidence claim"| M
  WT2 -->|"artifact + evidence claim"| M
  M --> T
  M --> G
  M -->|"verified handoff"| M2
  G -.->|"boot"| M2
```

## 네트워크·API 키 없음 제약

이 런타임은 **로컬 전용**으로 설계되었다.

| 제약 | 측정/구현 |
| --- | --- |
| 네트워크 import 없음 | `src/`·`scripts/`에 `urllib`/`http`/`socket`/`ssl`/`requests` import 없음 |
| API 키·모델 엔드포인트·텔레메트리 없음 | 코어가 프로바이더를 호출하지 않음 |
| 읽기 범위 | 지정한 폴더·config 경로. 홈 디렉터리 전역 스캔 없음 |
| Redaction | 추적 파일은 `git ls-files` 경유; gitleaks + 선택 조직 규칙 |
| 에이전트 CLI 트래픽 | 각 CLI가 기존 구독/엔드포인트로 통신 — 이 저장소가 키를 추가하지 않음 |

스택 채택 5문항 기준(README / CONTRIBUTING): API 키 강제 여부, 불필요 텔레메트리, 관리 지점 추가 여부, 1인 이상 확장, “에이전트 컨텍스트” 외에 실제로 푸는 문제.

<Note>
in-process 하네스(예: API 키를 들고 그래프를 소유하는 라이브러리)와 비교하면, 여기서 subagent는 프로세스 내부 actor가 아니라 **계약 아래 실제 CLI 세션**이고, 파일시스템은 worktree이며, 인터럽트는 사람이 쥐는 approval gate다. 오케스트레이터 세션 자체를 교체하는 succession은 in-process 그래프 모델에 대응물이 없다.
</Note>

### 호스트·플랫폼 한계 (정직한 상태)

- 기본 master spawn 경로는 Claude Code(`claude`)를 호출한다. 설계상 필수 벤더는 아니나, 실측·권장은 Claude Code 쪽이다.
- Codex를 master로 쓰는 경로는 미실측(unsupported가 아니라 untested).
- 실측 OS는 macOS. Orca는 Linux/Windows 빌드를 제공하나 이 저장소에서 전부 검증하지 않았다.
- U4(Repository Runtime Loader) 등 일부 런타임 유닛은 설계 어휘만 있거나 부분 구현이다 — [런타임 유닛](/runtime-units).

## 핵심 메커니즘 (한 페이지 맵)

| 영역 | 메커니즘 | 공개 진입점 |
| --- | --- | --- |
| Execution | Orca terminal · worktree · Run | host CLI + seat 규칙 |
| Context | L0/L1 bootstrap, Role State, compaction probe | `master-bootstrap`, `master-bootstrap-live` |
| Delegation | contract hash, tier×fan-out, ledger, model probe | `dispatch-gate check/register` |
| Steering | Proposal → Approval → Execution, Role State | `approval/`, 운영 정책 |
| Succession | IMMEDIATE/ADVISORY detect, handoff, placement spawn, verify, retire | `master-succeed` |
| Lineage | 고정 스키마 append-only ledger (결정 입력 아님) | `lineage.py` + ops 문서 |
| Acceptance | casebook · holdout · scorecard (self-report ≠ proof) | `acceptance-loop` |
| Defense | placement mismatch exit 26, empty-seat, redaction, revival | [방어 인벤토리](/defense-inventory) |

운영 규칙 한 줄: **worker의 `worker_done`은 claim**이다. 수락은 재검증·diff·artifact·redaction·(해당 시) acceptance suite로 한다.

## 문서 세트와 읽는 순서

| 세트 | 독자 | 수명 |
| --- | --- | --- |
| `docs/public/` | 시스템 설명 독자 | 이 저장소에 고정 |
| `master-ops/` | 설치자 + 이후 master 에이전트 | 새 ops 저장소로 복사·치환 |
| `docs/internal/` | 기여자 | 빠르게 낡음; 코드가 우선 |

에이전트 세션이 구체 작업 없이 클론만 열리면 `AGENTS.md` / `CLAUDE.md` / `INSTALL-PROMPT.txt`가 `master-ops/ONBOARDING.md` 라우터로 보낸다. 단계 파일은 **한 턴에 하나**, Verify 통과 후에만 다음 파일을 연다.

## 첫 검증 신호 (Orca 없이도 가능)

```console
$ scripts/master-succeed detect "routine status update" --context-ratio 0.7 --json
$ scripts/dispatch-gate --ledger /tmp/gate.jsonl check \
  --runtime codex --model grok-4.5 --contract README.md \
  --agents 1 --est-chars 1000 --completion-channel orchestration
$ scripts/adapter doctor
```

라이브 경로(클론 → Orca shell command → wake-up → Gen-1 → 첫 supervised worker)는 [Installation](/installation)과 [Quickstart](/quickstart)를 따른다.

## Next

<CardGroup>
  <Card title="Installation" href="/installation">
    Orca·에이전트 CLI·git·gh·python3·bd·skills 전제조건, preflight FAIL/WARN, 셸 명령 등록.
  </Card>
  <Card title="Quickstart" href="/quickstart">
    wake-up 온보딩부터 Generation 1 부트·첫 supervised worker까지 최단 성공 경로.
  </Card>
  <Card title="런타임 유닛" href="/runtime-units">
    U1–U12 책임 경계, L0/L1, bootstrap·dispatch·succession·lineage 대응표.
  </Card>
  <Card title="Orca 객체 모델" href="/orca-object-model">
    project·folder workspace·worktree·terminal·Run, 마스터 좌석, selector 형식.
  </Card>
  <Card title="마스터 라이프사이클" href="/master-lifecycle">
    founding spawn → steady state → clean succession → lineage 루프.
  </Card>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    check → dispatch → register, 계약 해시·ledger, model probe, orchestration 완료 채널.
  </Card>
  <Card title="온보딩" href="/onboarding">
    ONBOARDING 라우터, 1회 1단계 로드, Stage 1/2, founding spawn.
  </Card>
  <Card title="CLI 레퍼런스" href="/cli-reference">
    scripts/ 공개 명령 표와 비공개 표면 경계.
  </Card>
</CardGroup>

---

## 02. Installation

> Orca·에이전트 CLI·git·gh·python3·bd·skills 전제조건, preflight FAIL/WARN, 셸 명령 등록, 클론 후 측정 신호.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/02-installation.md
- Generated: 2026-08-07T07:02:16.815Z

### Source Files

- `docs/public/getting-started.md`
- `scripts/onboarding-preflight.sh`
- `README.md`
- `tests/test_onboarding_preflight.py`
- `INSTALL-PROMPT.txt`
- `master-ops/ONBOARDING.md`

---
title: "Installation"
description: "Orca·에이전트 CLI·git·gh·python3·bd·skills 전제조건, preflight FAIL/WARN, 셸 명령 등록, 클론 후 측정 신호."
---

설치 게이트는 `scripts/onboarding-preflight.sh`다. 클론 후 이 스크립트가 호스트 도구, Orca 런타임·orchestration Run, skills 아티팩트, 조직 redaction 규칙, 에이전트/워커 CLI, 디스패치 레저 경로를 한 번에 측정하고, 필수 항목이 비면 exit 1과 `BLOCKED`로 founding을 막는다. 애플리케이션 설치는 수동이며, `--fix`는 전역 Orca skills 추가·갱신만 수행한다.

## 설치 경계

| 구분 | 내용 |
| --- | --- |
| 런타임 기판 | Orca 앱 + 셸에 등록된 CLI (`orca` / `orca-dev` / `orca-ide`) |
| 측정 게이트 | `bash scripts/onboarding-preflight.sh` (Step 0, `master-ops/onboarding/01-preflight.md`) |
| 에이전트 경로 | 클론 루트에서 태스크 없이 wake-up → `master-ops/ONBOARDING.md` 라우터 |
| 비대상 | 제품 코드 배포, 네트워크 API 키, CI 설치 파이프라인 |

이 저장소는 workspace/orchestrator 런타임과 ops 템플릿이다. 마스터 세션은 Orca 터미널에서 살고, preflight 없이 라이브 마스터를 띄우는 경로는 지원되지 않는다.

## 전제조건 (FAIL / WARN)

판정은 preflight 출력 라벨을 따른다.

- **FAIL** → `FAIL` 인쇄, failures 카운트, 종료 요약 `BLOCKED`, exit **1** (또는 `PREFLIGHT_WAIVE`로 명시 면제)
- **WARN** → `WARN` 인쇄, 단독으로는 exit 1을 만들지 않음. `ESSENTIAL_LABELS`에 속하면 요약의 essential 블록에 재강조

### FAIL — founding 차단

| 라벨 | 요구 | 부재 시 신호 |
| --- | --- | --- |
| `orca` | CLI 존재, basename이 `orca`·`orca-dev`·`orca-ide`, `status --json`에 `"ok": true` | 설치/셸 등록 힌트 포함 FAIL |
| `orchestration` | Orca ready 후 `orchestration run-current --json`: RPC ok, 비-legacy Run 바인딩, `legacy_read_only` 아님 | `run-create` 안내; 앱 재시작만으로는 legacy coordinator 미해결 |
| `skills` | 디스크에 `orca-cli`+`orchestration` 스킬 디렉터리, 또는 skills 패키지 전역 목록에 둘 다 존재 | 설치 명령 안내; `--fix`로 전역 추가 가능 |
| `agent-cli` | `ORCA_AGENT_CLI` 설정 + 해당 바이너리 `PATH` | unset이면 agent-specific 검사가 조용히 빠지므로 FAIL |
| `worker-runtime` | `codex` 또는 `cursor-agent` 중 **하나 이상** | 둘 다 없으면 FAIL; 하나만 있으면 나머지는 WARN |
| `bd` | `bd` 바이너리; ops 레포가 있으면 `bd where`가 그 안을 가리켜야 함 | 바이너리 없음 또는 ops 밖 해석 시 FAIL |
| `python3` | `PATH`의 `python3` | 버전 하한 없음; 엔트리포인트가 python3 스크립트 |
| `git` / `gh` | 바이너리 존재 | PR 관리 전제로 FAIL |
| `redaction-extra` | `REDACTION_EXTRA_PATTERNS` 또는 `~/.config/redaction-extra.txt`에 **컴파일 가능한 규칙 ≥1** | 내용 미인쇄, 개수만 보고; publish 게이트 2개가 거부 |
| `gate-ledger` | `DISPATCH_GATE_LEDGER`(기본 `.mogui/dispatch-ledger.jsonl`) 상위 디렉터리 생성·쓰기 가능 | 쓰기 불가 시 FAIL |

### WARN — 단독 비차단

| 라벨 | 조건 | 결과/비용 |
| --- | --- | --- |
| `gitleaks` | PATH 없음 | redaction 스캔이 결정 불가로 exit 2; 퍼블리시 전 설치 |
| `ctx` | 없거나 `ctx status` 실패 | 크로스-프로바이더 히스토리 조회 불가; 마스터 자체는 동작 |
| `skill-stack` | `superpowers` / `ponytail` 없음 | 마스터는 동작하나 방법론·절제 레이어 없이 다르게 동작 |
| `gh-auth` | `gh` 미인증, 또는 `workflow` 스코프 없음 | 푸시/PR 또는 Actions 워크플로 편집 실패 |
| `worker-runtime` | 나열된 런타임 중 일부만 없음 (다른 하나는 있음) | 단일 실행기로 라우팅하는 정상 구성 |
| `pytest` | 3.11+ pytest도 `uv`도 없음 | 테스트 게이트를 돌릴 에이전트에 둘 중 하나 필요 |
| `codex-plugin` | `ORCA_AGENT_CLI`가 claude/claude-code일 때만, Codex 플러그인 미설치 | Codex 워커 디스패치 호스트에 필요; 그 외 INFO skip |

### Essential 라벨

요약 끝 `!! ESSENTIAL COMPONENTS MISSING` 블록에 다시 찍히는 라벨:

`orca`, `orchestration`, `skills`, `agent-cli`, `worker-runtime`, `bd`, `python3`, `gitleaks`, `ctx`, `redaction-extra`, `skill-stack`

WARN이어도 essential이면 여기 반복된다. 마스터 spawn 전에 설치하거나, 거부와 수용 동작을 기록로 남겨야 한다.

## Orca 설치와 셸 명령 등록

Orca는 추상화 대상이 아니다. 세션 수명, 핸들, supervised dispatch 메일박스가 여기서만 성립한다.

### 플랫폼별 설치

<Tabs>
  <Tab title="macOS">
```console
$ brew install --cask stablyai/orca/orca
```
앱과 번들 CLI 바이너리를 설치한다. Homebrew `--cask`는 macOS 전용이다.
  </Tab>
  <Tab title="Linux / Windows">
[공식 다운로드](https://www.onorca.dev/download)를 사용한다. 유지자 보고: AppImage/`.deb`, Arch `yay -S stably-orca-bin`. Linux에서는 GNOME 스크린 리더 `orca`와 충돌을 피하기 위해 바이너리 이름이 `orca-ide`일 수 있다. 그 경우 preflight가 허용하는 basename을 쓰거나 `ORCA_CLI_COMMAND`로 경로를 지정한다.
  </Tab>
</Tabs>

### Shell command

앱을 연 뒤:

1. **Settings → Orca CLI**
2. **Shell command** 켜기

측정 성공 조건:

```console
$ command -v orca   # 또는 orca-dev / orca-ide
$ orca status
# 기대: appRunning: true, runtimeState: ready
$ orca status --json
# 기대: "ok": true
```

앱이 닫혀 있으면 `orca open`이 런타임이 닿을 때까지 대기한다. UI 라벨이 빌드마다 바뀌어도 성공 판정은 위 측정이다.

### Orca CLI 해석 순서

preflight(및 동일 패턴을 쓰는 런북) 해석:

1. `ORCA_CLI_COMMAND` — 명시 경로/이름
2. `ORCA_DEV_REPO_ROOT` 설정 시 → `orca-dev`
3. 기본 → `orca`

지원 basename 외 이름은 `orca` FAIL이다.

## 저장소 클론과 Orca 등록

```console
$ git clone https://github.com/baksohyeon/mogui-ADE-orchestrator
$ cd mogui-ADE-orchestrator
```

두 개념을 섞지 않는다.

| 개념 | 역할 |
| --- | --- |
| 폴더 workspace root | 여러 레포를 묶는 절대 경로. 마스터 좌석 |
| 이 오케스트레이터 클론 | 설치 세션 자리 + 런타임/템플릿 소스. 마스터가 영구 거주하는 곳이 아님 |

```console
$ orca repo add --path <absolute-folder-path>
```

Orca UI에서 프로젝트를 추가해도 된다. 이후 **해당 프로젝트 안** 터미널에서 작업 디렉터리가 클론(또는 의도한 install seat)인지 `pwd`로 확인한다. 프로젝트 밖 셸에서 시작하면 placement/spawn이 잘못된 좌석을 측정한다.

## 클론 후 측정 신호

### Preflight 실행

```console
$ cd <clone-root>
$ ORCA_AGENT_CLI=claude bash scripts/onboarding-preflight.sh
```

| 옵션/환경 | 의미 |
| --- | --- |
| (인자 없음) | 읽기 전용 측정. 상태 변경 없음 |
| `--fix` | 승인 후: `skills add stablyai/orca -g --skill orca-cli --skill orchestration` 및 `update orchestration -g`. 앱 설치는 하지 않음 |
| 잘못된 인자 | `Usage: … [--fix]`, exit **2** |

#### 환경 변수

| 변수 | 역할 |
| --- | --- |
| `ORCA_AGENT_CLI` | 마스터 호스트 CLI 이름. unset → `agent-cli` FAIL |
| `ORCA_CLI_COMMAND` | Orca CLI 경로 오버라이드 |
| `ORCA_DEV_REPO_ROOT` | 설정 시 `orca-dev` 선택 |
| `ORCA_SKILLS_DIRS` | `:` 구분 skills 루트 오버라이드 (미설정 시 `~/.claude/skills`, `~/.agents/skills`, `~/.codex/skills` 등) |
| `REDACTION_EXTRA_PATTERNS` | 조직 규칙 파일 경로 (기본 `~/.config/redaction-extra.txt`) |
| `DISPATCH_GATE_LEDGER` | 디스패치 레저 경로 (기본 `.mogui/dispatch-ledger.jsonl`) |
| `PREFLIGHT_WAIVE` | 쉼표 구분 라벨. 해당 FAIL을 인쇄·카운트되는 `WAIVED`로 강등 |

규칙 파일 한 줄 형식: `id|description|regex` (빈 줄·`#` 주석 무시, 정규식 컴파일 필수). preflight는 규칙 본문을 절대 출력하지 않는다.

### 요약 판정

| 출력 | exit | 의미 |
| --- | --- | --- |
| `READY: all required checks passed` | 0 | 필수 전부 PASS |
| `READY WITH WAIVERS: … downgraded, not satisfied` | 0 | 일부 FAIL이 면제됨. 충족이 아님 |
| `BLOCKED: fix every FAIL before onboarding` | 1 | 면제되지 않은 FAIL 존재 |
| `NOTE: PREFLIGHT_WAIVE named checks that did not run: … still enforced` | (해당 FAIL 유지) | 오타 면제 — 검사는 그대로 강제 |
| `!! ESSENTIAL COMPONENTS MISSING` | — | essential 갭 재나열 + 결과 문구 |

예시 (성공 요약 형태):

```text
INFO resolved Orca CLI: orca
PASS orca           orca status --json returned ok:true
PASS orchestration  RPC reachable and a non-legacy Run is bound to this terminal
…

Preflight summary
  PASS: N
  WARN: M
  FAIL: 0
  WAIVED: 0
  READY: all required checks passed
```

### 세션 상태 신호 (도구 설치 외에 필요한 것)

| 신호 | 측정 | 복구 |
| --- | --- | --- |
| Orca runtime ready | `orca status` / `--json` ok | 앱 실행, Shell command, `orca open` |
| non-legacy Run 바인딩 | `orca orchestration run-current --json` | `orca orchestration run-create` |
| legacy coordinator | `legacy_read_only` / `effectsApplied:false` | 새 Run 생성 (앱 재시작만으로는 미해결) |
| Run null | `"run": null` | 동일하게 `run-create`; 바인딩 소실 후 empty mailbox와 구분 어려움 |
| agent CLI 선택 | `ORCA_AGENT_CLI` + `command -v` | onboarding이 설정; 바이너리는 그 전에 설치 |

### 조직 redaction 규칙 최소 스캐폴드

```console
$ mkdir -p ~/.config
$ printf '%s\n' 'org-example|example pattern only|EXAMPLE_SECRET_[0-9]+' > ~/.config/redaction-extra.txt
```

실제 조직 패턴으로 교체한다. 버전 관리에 올리지 않는다.

### Skills 수동 설치 (preflight 힌트와 동일)

아티팩트가 없고 skills 실행기가 있을 때:

```console
$ skills add stablyai/orca -g --skill orca-cli --skill orchestration
$ skills update orchestration -g
```

또는 `bash scripts/onboarding-preflight.sh --fix` (skills 명령이 가용할 때).

## 설치 절차 (사람 경로)

<Steps>
  <Step title="Orca 설치·런타임 확인">
    플랫폼에 맞게 Orca를 설치하고 앱을 연다. **Settings → Orca CLI → Shell command**를 켠 뒤 `orca status`가 ready/`ok:true`인지 확인한다.
  </Step>
  <Step title="호스트 도구 준비">
    마스터용 에이전트 CLI, 워커 런타임(`codex` 및/또는 `cursor-agent`), `git`, `gh`, `python3`, `bd`를 PATH에 둔다. 퍼블리시 예정이면 `gitleaks`, 히스토리 조회면 `ctx`. 조직 redaction 규칙 파일을 만든다.
  </Step>
  <Step title="클론·Orca 프로젝트">
    이 저장소를 클론하고, workspace root(또는 임시 install seat)를 `orca repo add --path`로 등록한다. Orca 터미널을 그 프로젝트 안에서 연다.
  </Step>
  <Step title="Preflight">
    `ORCA_AGENT_CLI=<cli> bash scripts/onboarding-preflight.sh`를 실행한다. `BLOCKED`면 FAIL을 고친다. 정당한 불가 항목만 `PREFLIGHT_WAIVE=<label>`로 명시 면제한다.
  </Step>
  <Step title="Wake-up 온보딩">
    같은 터미널에서 에이전트 CLI를 시작하고, 구체 태스크 없이 wake-up 문구만 준다. 에이전트는 `master-ops/ONBOARDING.md`만 라우터로 읽고 세션 모드(Founding / Reverify / Upgrade / Template improve)를 묻는다. Founding은 ops 레포·lineage가 없을 때만.
  </Step>
</Steps>

에이전트 호스트가 루트 `CLAUDE.md`/`AGENTS.md`를 자동 로드하지 않으면 `INSTALL-PROMPT.txt` 문구로 동일 라우터 경로를 강제한다. Orca가 준비되지 않았으면 설치·기동 안내 후 중단한다.

## Preflight 이후 onboarding이 남기는 인스턴스 파일

Step 0 Verify는 다음을 요구한다 (커밋하지 않는 인스턴스 소유 파일; 템플릿은 `*.example.json`만 선적).

```console
$ test -f config/instance-runtime.json || cp config/instance-runtime.example.json config/instance-runtime.json
# master_host_runtime = 확인된 ORCA_AGENT_CLI
$ test -f config/model-tier-policy.json || cp config/model-tier-policy.example.json config/model-tier-policy.json
# version 2, consent, tiers / fanout_caps — 모델 id 추측 금지
```

해석 순서(소비자): 환경 오버라이드 → 인스턴스 파일 → unconfigured (하드코딩 기본값 없음).

## 첫 장애 대응

| 관측 | 확인 | 조치 |
| --- | --- | --- |
| spawn이 호스트를 못 부름 | `command -v orca`; `orca status` | Shell command 등록 |
| `BLOCKED` | FAIL 줄 전체 | 항목 수리 또는 의도적 `PREFLIGHT_WAIVE` |
| orchestration FAIL, legacy | 출력의 `legacy_read_only` / inspect-only | `orca orchestration run-create` 후 preflight 재실행 |
| orchestration FAIL, no Run | `"run": null` | 동일 `run-create` |
| `agent-cli` FAIL | `echo $ORCA_AGENT_CLI`; `command -v …` | 변수 설정 + 바이너리 설치 |
| `redaction-extra` FAIL | 파일 존재·유효 규칙 개수(내용 비공개) | 형식 `id\|description\|regex`로 재작성 |
| 면제했는데 여전히 FAIL | `named checks that did not run` | 라벨 철자 수정 (예: `redaction-extra` not `redaction-extras`) |
| Orca 없이 에이전트가 계속 진행 | 런타임 ready 여부 | 중단. 완료 감지가 스크린 폴링으로  degenerates — 실사고 기록됨 |

## 범위 밖 (이 페이지에서 끝내지 않는 것)

- Generation 1 spawn, boot smoke, 첫 supervised worker 검증 → [Quickstart](/quickstart), [온보딩](/onboarding)
- 인스턴스·티어 정책 상세 → [인스턴스 설정](/configure-instance)
- preflight 이후 장애 카탈로그 → [Troubleshooting](/troubleshooting)

## Next

<CardGroup>
  <Card title="Quickstart" href="/quickstart">
    클론 → Orca 준비 → wake-up → Gen-1 부트 → 첫 supervised worker까지 최단 경로
  </Card>
  <Card title="프로그레시브 온보딩" href="/onboarding">
    ONBOARDING 라우터, 1단계 1파일, Founding/Reverify/Upgrade
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    BLOCKED, placement, MODEL_PROBE_FAILED, seat 중복, revival
  </Card>
  <Card title="Overview" href="/overview">
    공개 표면, Orca 전제, 마스터/워커 역할, 네트워크·API 키 없음 제약
  </Card>
</CardGroup>

---

## 03. Quickstart

> 클론 → Orca 준비 → wake-up 온보딩 → Generation 1 부트 → 첫 supervised worker까지 최단 성공 경로와 검증 신호.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/03-quickstart.md
- Generated: 2026-08-07T07:02:16.006Z

### Source Files

- `docs/public/getting-started.md`
- `README.md`
- `master-ops/ONBOARDING.md`
- `master-ops/onboarding/09-spawn.md`
- `scripts/master-succeed`
- `scripts/dispatch-gate`
- `scripts/adapter`

---
title: "Quickstart"
description: "클론 → Orca 준비 → wake-up 온보딩 → Generation 1 부트 → 첫 supervised worker까지 최단 성공 경로와 검증 신호."
---

첫 실행 경로는 `docs/public/getting-started.md`의 인간 경로와 `master-ops/ONBOARDING.md` 라우터(단계 파일 1회 1로드)가 합쳐진 설치 인터뷰다. Orca 런타임이 준비된 뒤 이 저장소 클론 안에서 에이전트를 깨우면 Founding 모드가 ops 저장소를 만들고, `scripts/master-succeed spawn`으로 Generation 1 마스터를 좌석 검증 후 띄우며, 첫 작업은 `scripts/dispatch-gate check` → Orca orchestration dispatch → `register` → `worker_done` 수락 순으로 끝난다.

<Warning>
Orca 없이 마스터를 돌리면 완료 감지가 화면 폴링으로 떨어지고 감독이 사라진다. Step 0 preflight는 준비된 런타임 없이 founding을 진행하지 않는다.
</Warning>

## 성공 경로 한눈에

| 단계 | 행위자 | 핵심 명령 / 표면 | 통과 신호 |
| --- | --- | --- | --- |
| 1. Orca 설치·CLI 등록 | 사람 | `brew install --cask stablyai/orca/orca` 또는 [download](https://www.onorca.dev/download); Settings → Orca CLI → Shell command | `orca status` → `appRunning: true`, `runtimeState: ready` (또는 `--json` `"ok": true`) |
| 2. 클론·프로젝트 등록 | 사람 | `git clone` …; `orca repo add --path <path>` | Orca 프로젝트 안 터미널에서 `pwd`가 클론/워크스페이스 경로 |
| 3. Wake-up 온보딩 | 설치 에이전트 | 에이전트 CLI 기동 후 구체 작업 없는 짧은 문구 | `master-ops/ONBOARDING.md` 라우터 로드, 세션 모드 질문 |
| 4. Preflight (Step 0) | 설치 에이전트 | `ORCA_AGENT_CLI=<cli> bash scripts/onboarding-preflight.sh` | exit 0, 요약 `READY` 또는 `READY WITH WAIVERS` |
| 5. Founding → Gen-1 | 설치 에이전트 + 신생 마스터 | `master-succeed spawn` + orchestration Run/Task/Dispatch | placement `MATCH` 또는 `MATCH_REISSUED`; seat에 마스터 1개; boot smoke `worker_done` |
| 6. 첫 supervised worker | 마스터 | `dispatch-gate check` → dispatch → `register` → mailbox wait | 게이트 `allow: true`; 마스터가 증거를 재검증 후 수락 |

```text
[사람] Orca ready + clone in project terminal
    │
    ▼
[설치 에이전트] wake-up → ONBOARDING router → Founding
    │  00…08 prepare ops seat rules
    ▼
[Gen-1 마스터] spawn (empty-seat + placement) → boot smoke → installer retire
    │
    ▼
[사람] 작은 계약 승인 → [마스터] gate/dispatch/register → [워커] worker_done
    │
    ▼
[마스터] 독립 증거 수락  (self-report ≠ proof)
```

## 전제조건 (측정)

분류는 `scripts/onboarding-preflight.sh` 기준이다. `FAIL`은 exit 1 `BLOCKED`에 합산되어 founding을 막는다. `WARN`만으로는 exit 1이 되지 않지만, essential로 표시된 항목은 요약에서 반복된다. 만족할 수 없는 필수 검사만 `PREFLIGHT_WAIVE=<check-label>`로 명시 면제한다(미일치 라벨은 그대로 강제).

| 필요 | 확인 | 부재 시 |
| --- | --- | --- |
| Orca 앱 + 준비된 런타임 | `orca status` / `orca status --json` | `orca` FAIL |
| `orca` on `PATH` | `command -v orca` (지원 basename: `orca`, `orca-dev`, `orca-ide`) | 동일 FAIL |
| 마스터 에이전트 CLI | `command -v claude` 등; 온보딩이 `ORCA_AGENT_CLI` 설정 | `agent-cli` FAIL |
| 워커 런타임 ≥1 | `command -v codex` 및/또는 `cursor-agent` | 둘 다 없으면 `worker-runtime` FAIL |
| `git`, `gh`, `python3`, `bd` | 각 `--version` / `command -v` | 각 FAIL (`gh` 미인증은 WARN) |
| skills `orca-cli`, `orchestration` | 에이전트 skills root 또는 전역 목록 | `skills` FAIL |
| 조직 redaction 규칙 파일 | 기본 `~/.config/redaction-extra.txt` 또는 `REDACTION_EXTRA_PATTERNS` (id\|desc\|regex ≥1) | `redaction-extra` FAIL |

WARN 예: `gitleaks`, `ctx`, skill-stack(`superpowers`/`ponytail`), `gh-auth` / `workflow` scope.

클론 루트에서 세션 상태 포함 전체 측정:

```console
$ bash scripts/onboarding-preflight.sh
```

`--fix`는 승인 후에만: 전역 Orca skills 보강 가능, 앱 설치는 수동.

## 최단 절차

<Steps>
<Step title="Orca 설치 및 셸 명령 등록">
macOS(검증된 경로):

```console
$ brew install --cask stablyai/orca/orca
```

Linux/Windows: [Orca download](https://www.onorca.dev/download). Linux에서는 GNOME 스크린 리더와 충돌을 피하려고 바이너리명이 `orca-ide`일 수 있다.

Orca 앱을 연 뒤 **Settings → Orca CLI → Shell command**를 켠다. 성공 조건:

```console
$ command -v orca
$ orca status
```

앱이 꺼져 있으면 `orca open`이 런타임이 닿을 때까지 기다린다.
</Step>

<Step title="저장소 클론 및 폴더 등록">
두 계층을 혼동하지 않는다.

1. **폴더 워크스페이스 루트** — 여러 저장소를 묶는 디렉터리(자체 git이 아닐 수 있음). 마스터 좌석.
2. **이 오케스트레이터 클론** — 설치기·런타임 소스. 마스터는 여기에 영구 거주하지 않는다.

```console
$ git clone https://github.com/baksohyeon/mogui-ADE-orchestrator
$ cd mogui-ADE-orchestrator
$ orca repo add --path <path-to-folder>
```

Orca에서 **해당 프로젝트 안** 터미널을 연다. `pwd`가 설치 세션 체크아웃이어야 한다. 프로젝트 밖 임의 cwd는 이후 placement/spawn이 잘못된 좌석을 측정한다.

워크스페이스 루트는 나중에 **절대 경로**로 붙여넣는다. 상대 경로와 bare `~`는 불충분하다.
</Step>

<Step title="에이전트 기동 및 wake-up">
프로젝트 터미널에서 마스터로 쓸 에이전트 CLI를 켠다(`claude`가 하드 런 검증된 조합; 다른 CLI는 워커로 검증됨, Codex 마스터는 미테스트).

구체 작업 없이 짧게 깨운다. 문구 예:

```text
Wake the master.
```

라우터 키는 의식 문구가 아니라 **구체 태스크 부재**다. 에이전트는 `master-ops/ONBOARDING.md`만 먼저 읽고 세션 모드를 묻는다.

| 모드 | 의미 | 스폰 |
| --- | --- | --- |
| **Founding** | 새 워크스페이스: ops 저장소 구축 + Gen-1 | 허용 (`00`→`10`) |
| **Reverify** | 이미 ops+마스터 있음: 건강 점검만 | 차단 |
| **Upgrade** | 템플릿 드리프트 적용 | 차단 |
| **Template improve** | 이 오케스트레이터 자체 수정 | 온보딩 중단 |

기존 ops 저장소·lineage 파일이 있으면 Founding이 아니다. 반쯤 끝난 설치에 Founding을 다시 돌리면 lineage/거버넌스가 깨진다. 죽은 마스터·반쯤 끝난 Gen-1은 ops의 `docs/runbooks/succession-boot-card.md` 경로다.
</Step>

<Step title="Founding 인터뷰 (사람 결정)">
설치 에이전트(Herald)는 한 번에 한 단계 파일을 읽고, 해당 Verify가 통과하기 전에는 다음 파일을 열지 않는다. Founding에서 사람이 답해야 하는 결정:

| 결정 | 이유 |
| --- | --- |
| 워크스페이스 루트(절대 경로 붙여넣기) | 이후 측정·마스터 좌석 기준. 홈 스캔/후보 목록 없음 |
| 목적·소속 저장소 인벤토리 | 마스터가 조정할 허용 표면 |
| ops 저장소 이름·위치 | 거버넌스용 **별도** git (제품 코드 아님) |
| 마스터 callsign | 문서의 “master”는 임시 역할 라벨; 살아 있는 세션 호칭 |
| 이슈 트래커·선택 스킬 스택 | 실행 상태; 선택 스킬 거부는 정상 |
| Gen-1 스폰 확인 | 기록된 좌석에 터미널 1개 생성·킥오프·boot smoke. smoke 중 해당 패인에 타이핑하지 말 것 |

설치 인터뷰 세션 ≠ 마스터. Founding 스폰 후 설치기는 은퇴하고, 마스터는 워크스페이스 좌석의 **새** 세션이다.
</Step>

<Step title="Preflight 및 인스턴스 설정 (Step 0)">
에이전트가 `{{RUNTIME_ROOT}}`에서 실행한다(서술은 `$` 프롬프트):

```console
$ cd "{{RUNTIME_ROOT}}"
$ ORCA_AGENT_CLI="<user-named agent CLI>" bash scripts/onboarding-preflight.sh
```

통과 후 인스턴스 전용(커밋하지 않음) 파일:

```console
$ test -f config/instance-runtime.json || cp config/instance-runtime.example.json config/instance-runtime.json
# master_host_runtime = 확인된 에이전트 CLI
$ test -f config/model-tier-policy.json || cp config/model-tier-policy.example.json config/model-tier-policy.json
# version: 2, agents/tiers/fanout_caps/window_seconds, consent
```

- `instance-runtime.json`: `master_host_runtime`, `transcript_globs`, optional `product_repo`. 해석: env 오버라이드 → 파일 → unconfigured(추측 금지).
- `model-tier-policy.json`: 게이트 소비 필드 `version`/`tiers`/`fanout_caps`/`window_seconds`. 해석: `DISPATCH_TIER_POLICY` → `config/model-tier-policy.json` → 템플릿 `master-ops/model-tier-policy.json`.

오케스트레이션이 unbound/`run_required`이거나 legacy coordinator가 write를 떨어뜨리면 `orca orchestration run-create`로 새 Run을 묶고 재측정한다. 앱 재시작만으로는 legacy가 안 지워질 수 있다.
</Step>

<Step title="Generation 1 스폰과 boot smoke">
Step 8 전에 좌석 empty-gate: `ORCA terminal list --worktree <selector> --json`이 **해당 좌석 터미널 0개**. 잔여 seat-check/이전 스폰이 있으면 하드 스톱(두 번째 마스터 금지).

에이전트 전용 감독 경로(소유자에게 붙여넣지 않음; `ORCA`는 해석된 실행 파일):

```bash
G={{RUNTIME_ROOT}}/scripts/dispatch-gate
L=~/.mogui/dispatch-ledger.jsonl
"$G" --ledger "$L" check \
    --runtime <runtime> \
    --model "{{MODEL_ID}}" \
    --contract <contract file> \
    --agents 1 \
    --est-chars <estimated input chars> \
    --completion-channel orchestration
ORCA orchestration run-create --objective "Found and verify the Generation 1 master" --json
ORCA orchestration task-create --spec "Run the byte-identical founding kickoff file and complete Step 9 boot smoke" --json
"{{RUNTIME_ROOT}}/scripts/master-succeed" spawn \
    --workspace-selector <durable placement selector, id: prefixed> \
    --kickoff-file <kickoff file> \
    --root "{{WORKSPACE_ROOT}}" \
    --model "{{MODEL_ID}}" \
    --title "Gen-1 founding boot" \
    --json
ORCA terminal wait --terminal <verified live handle> --for tui-idle --timeout-ms 60000 --json
ORCA orchestration dispatch --task <task id> --to <verified live handle> --inject --json
"$G" --ledger "$L" register \
    --job-id <job id> \
    --probe-cmd "<command proving the job-id appears in an artifact>" \
    --orchestration-task <task id>
ORCA orchestration check --wait --types worker_done,escalation,question --timeout-ms 900000 --json
```

`master-succeed spawn` 주요 플래그:

| 플래그 | 역할 |
| --- | --- |
| `--workspace-selector` | 내구 좌석 선택자 (`id:…` 등) |
| `--kickoff-file` / `--kickoff-text` | 부트 킥오프 (파일 바이트 동일 전달 검증) |
| `--root` | 워크스페이스 루트 |
| `--model` / `--agent` | 모델·런타임 CLI (`claude`/`grok`/`codex`/커스텀) |
| `--title` | 터미널 제목 |
| `--expected-placement` | 독립 기대 worktree; 불일치 시 fail-closed **exit 26** `SPAWN_PLACEMENT_MISMATCH` |
| `--dry-run` / `--json` | 시뮬레이션·JSON 출력 |

게이트 `check`가 `allow: true`여야 스폰/부착. placement 결과는 `MATCH` 또는 유효 `MATCH_REISSUED`(`handle_reissued: true` + 채택 핸들). Codex 워커 전 `scripts/codex-worker-pretrust <worktree-path>`.

Step 9(신생 마스터 안): Role State·callsign 선언, 설정/실측 모델 분리 보고, placement 증거, `docs/lineage/MASTER-LINEAGE.md` Gen-1 append, founding Task에 `worker_done` 1회.
</Step>

<Step title="운영 카드와 설치기 은퇴">
boot smoke 통과 후 Step 10이 운영 카드를 출력하고(Role State / Propose / Approved execute / Dispatch / succession 문구), 신생 마스터가 설치기 핸들·pty/session 신원 재확인 후 `ORCA terminal close --terminal <installer handle> --json`으로 설치기를 닫는다. 신원 모호·불일치 시 아무 터미널도 닫지 않고 보고한다. 마스터 터미널은 유지한다.
</Step>

<Step title="첫 supervised worker">
설치만으로는 긴 채팅과 같다. 시스템이 보이는 지점은 좁은 계약 → 승인 → 워커 → 증거 수락이다.

계약 예시 형태:

```text
In the repository <repo-name>, open README.md and list the top-level section headings.
Do not edit any file. Return the heading list as the artifact.
```

관찰 순서:

1. **Proposal** — 대상 저장소/체크아웃, 허용 표면, 수락 기준, 증거, 커밋 규칙.
2. **Approval** — 비사소 작업은 제안 → 승인 → 실행.
3. **Dispatch** — 보통 저장소 worktree 브랜치 워커; 완료는 orchestration 메일박스(`worker_done` 등), 화면 스크레이프 아님.
4. **Acceptance** — 워커 “done”은 주장; 마스터가 아티팩트·diff·게이트를 재검증.

정상 디스패치 상태 객체 순서(README 측정 경로):

1. 계약 파일 해시가 게이트 결정·태스크와 함께 기록  
2. `scripts/dispatch-gate check` → 티어×fan-out 정책, JSONL ledger (`--ledger`, dry-run은 `--no-record`)  
3. `orca orchestration task-create`  
4. worktree + terminal 격리  
5. `orca orchestration dispatch --task … --to … --inject`  
6. `scripts/dispatch-gate register` (모델 probe: 선언 대비 실측; 더 비싼 모델이면 deny)  
7. `orca orchestration check --wait` (mailbox)  
8. 마스터 독립 수락  

`dispatch-gate` 하위 명령: `check`, `register`, `watch`, `report`. `check`의 `--completion-channel`은 `orchestration` 또는 `sentinel-log`(라이브 마스터 경로는 orchestration).
</Step>
</Steps>

## 검증 신호 체크리스트

Founding이 “done” 한 줄에 의존하지 않는다.

| # | 검사 | 기대 |
| --- | --- | --- |
| 1 | Orca UI / `orca terminal list` | Gen-1 세션이 **워크스페이스 루트 폴더 워크스페이스**(또는 단일 저장소면 primary worktree)에 있음. 멀티 레포에서 제품 레포 worktree에만 걸리면 misplacement |
| 2 | 마스터 트랜스크립트 boot smoke | Role State(+ callsign); configured vs measured 모델 분리; placement 증거가 의도 좌석과 일치 |
| 3 | 좌석 유일성 | 해당 워크스페이스 마스터 정확히 1. `master-succeed check-duplicates` |
| 4 | 폴더 좌석 라벨 | worktree 경로 비어 있음 / “Unavailable worktree” 칩 → **정상**(git worktree가 아님) |
| 5 | 설치기 | 카드 출력 후 설치 터미널 종료; 마스터 터미널 생존 |
| 6 | 첫 워커 | 게이트 allow → mailbox `worker_done` → 마스터 수락(self-report만으로 종료 금지) |

```console
$ orca terminal list
$ scripts/master-succeed check-duplicates --help   # 실제 호출은 ops 마커/셀프 핸들로
```

## 빠른 순수 함수 스모크 (선택)

Orca 없이 코어 진입점만 확인(마스터 세션은 생기지 않음):

```console
$ scripts/master-succeed detect "routine status update" --context-ratio 0.7 --json
$ scripts/dispatch-gate --ledger /tmp/gate.jsonl check \
  --runtime codex --model grok-4.5 --contract README.md --agents 1 --est-chars 1000 \
  --completion-channel orchestration
$ scripts/adapter doctor
```

`adapter doctor`는 런타임 presence JSON 리포트만 반환한다.

## 실패 형태

| 증상 | 확인 | 조치 |
| --- | --- | --- |
| 스폰이 호스트를 못 부름 | `command -v orca`; `orca status` | Shell command 등록 |
| Preflight `BLOCKED` | `FAIL` 줄 | 필수 갭 수정; 의도된 경우만 `PREFLIGHT_WAIVE` |
| 마스터가 제품 레포 아래에만 있음 | 사이드바 vs 워크스페이스 루트 | misplacement. 두 번째 Founding 금지 → succession/recovery |
| Orca 없이 에이전트가 오류에 계속 진행 | 세션 전 `orca status` | 중단. 감독 경로 불가 |
| “Unavailable worktree” | 폴더 워크스페이스 여부 | 예상 동작 |
| 두 번째 세션이 master 주장 | `orca terminal list`; lineage / check-duplicates | 사고. Reverify는 스폰 안 함 |
| `SPAWN_PLACEMENT_MISMATCH` (exit 26) | `--expected-placement` vs 실제 좌석 | 경로 selector로 재시도 금지; 설정 후 **새** 세션 |
| 빈 mailbox / binding 상실 | orchestration Run 바인딩 | `run-create` 후 재측정; empty mailbox ≠ 완료 |
| inject “dispatched”인데 워커가 시작 화면 | Dispatch 상태 vs 셸 프롬프트 도착 | task ready 리셋 후 `orca terminal send`로 브리프 재전달, 프롬프트 착지 확인 |

## 제약 (이 경로에 해당하는 것)

- 로컬 전용: `src/`·`scripts/` 네트워킹 import 없음, API 키 없음. 에이전트 CLI가 각자 제공자에 연결하는 것은 기존 동작.
- 라이브 세션은 Orca 필수. 순수 함수 CLI는 Orca 없이 동작.
- 마스터 기본 스폰 경로는 Claude Code 검증 조합; 워커는 터미널 바이너리 무엇이든 계약 하에 가능.

## Next

<CardGroup>
<Card title="Installation" href="/installation">
Orca·CLI·preflight FAIL/WARN·셸 명령 등록 상세.
</Card>
<Card title="프로그레시브 온보딩" href="/onboarding">
라우터, 1회 1단계·Verify, Founding/Reverify/Upgrade.
</Card>
<Card title="Orca 객체 모델" href="/orca-object-model">
project·folder workspace·worktree·terminal·selector·UI 라벨.
</Card>
<Card title="Supervised dispatch" href="/supervised-dispatch">
check → dispatch → register, ledger, model probe, mailbox.
</Card>
<Card title="마스터 라이프사이클" href="/master-lifecycle">
founding → steady state → succession → lineage.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
BLOCKED, placement, MODEL_PROBE_FAILED, seat 중복, revival.
</Card>
</CardGroup>

---

## 04. 런타임 유닛

> U1–U12 책임 경계, L0/L1 컨텍스트, 부분 구현 유닛, bootstrap·dispatch·succession·lineage 모듈 대응표.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/04-page-4.md
- Generated: 2026-08-07T07:02:16.207Z

### Source Files

- `docs/public/concepts.md`
- `src/master_runtime/core/bootstrap.py`
- `src/master_runtime/core/bootstrap_live.py`
- `src/master_runtime/core/context/resolver.py`
- `src/master_runtime/core/work_ledger.py`
- `src/master_runtime/core/approval/gates.py`
- `src/master_runtime/core/adapter/doctor.py`

---
title: "런타임 유닛"
description: "U1–U12 책임 경계, L0/L1 컨텍스트, 부분 구현 유닛, bootstrap·dispatch·succession·lineage 모듈 대응표."
---

마스터 런타임은 단일 프로세스가 아니라 **U1–U12 책임 단위**로 나뉜다. 유닛 번호는 설계 어휘이고, 실제 구현은 `src/master_runtime/core/` 모듈과 `scripts/` 엔트리포인트에 붙는다. 유닛이 있다고 해서 전부 모듈이 완성된 것은 아니며, U4는 설계만, U5·U7·U11은 부분 구현이다.

## 유닛 개요

| Unit | 이름 | 책임 | 모듈 / 스크립트 | 상태 |
| --- | --- | --- | --- | --- |
| U1 | Bootstrap | 안전 기동에 필요한 최소 L0/L1 로드 | `bootstrap.py`, `bootstrap_live.py` → `scripts/master-bootstrap`, `scripts/master-bootstrap-live` | 구현 |
| U2 | Context Resolver | 경로가 workspace / repo / worktree / folder 중 어디에 속하는지 판정 | `context/` (`resolver.py`, `descriptor.py`, `manifest.py`) | 구현 |
| U3 | Workspace Runtime | 트랙·교차 저장소 상태·장기 실행 기록 | `work_ledger.py` (`JsonlWorkLedger`, `WorkspaceRuntime`) | 구현 |
| U4 | Repository Runtime Loader | 대상 저장소에 필요한 harness만 로드 | 없음 | 설계 전용 |
| U5 | Worker Scheduler | 리스 발급, 격리 선택, dispatch, 예산, reap | 부분: `dispatch_gate.py`, `worker_reap.py` → `scripts/dispatch-gate`, `scripts/worker-reap` | 부분 |
| U6 | Approval Manager | 액션 위험 분류와 승인 상태 바인딩 | `approval/` (`gates.py`, `registry.py`) | 구현 |
| U7 | Role Runtime | 활성 역할 1개, role lock, 전이 상태 | 부분: `RoleState` 파싱은 `bootstrap.py`; lock 자체는 정책 | 부분 |
| U8 | Recovery Manager | reattach / 1회 resume / 상태 재구성 (읽기 전용) | `recovery.py` → `scripts/master-recover` | 구현 |
| U9 | Succession Manager | freeze, thin handoff, successor 검증, predecessor retire | `succession.py` → `scripts/master-succeed` | 구현 |
| U10 | Lineage Recorder | succession 품질 메타데이터 append-only 기록 (부트 소스 아님) | `lineage.py` | 구현 |
| U11 | Observability | 프로브·알림·컨텍스트 품질·모델 신원·acceptance 증거 | 부분: `digest_loop.py`, `watchdog.py`, `acceptance/`, 모델 프로브 스크립트 | 부분 |
| U12 | Adapter Layer | 제품별 CLI·포맷을 공통 계약 뒤로 격리 | `adapter/` → `scripts/adapter` | 구현 (launch 래핑 제거 후 doctor 중심) |

유닛 번호는 책임 자리표이다. “U5가 있다”는 말은 리스 발급·격리·launch·예산·reap이 전부 한 모듈에 있다는 뜻이 아니다.

## L0 / L1 컨텍스트

### 정의

| 계층 | 내용 | 대표 소스 |
| --- | --- | --- |
| **L0** | 안정 운영 프레임: charter, 역할 규칙, 상시 조정 규칙 | charter 파일 (`scripts/master-bootstrap --charter`) |
| **L1** | 활성 작업 컨텍스트: 트랙, handoff, digest 관측, 최근 운영 증거 | handoff, work ledger, live bootstrap 블록, `scripts/l1-digest` |

`bootstrap()`은 L0(charter)을 예산 안에서 먼저 채우고, 남은 예산으로 L1(handoff)을 붙인다. 기본 예산은 `DEFAULT_BUDGET_CHARS = 24_000`이다. 초과 시 마커:

- L0: `[TRUNCATED:BOOTSTRAP_BUDGET_EXCEEDED]` → warning `BUDGET_TRUNCATED:L0`
- L1: `[TRUNCATED:L1_BUDGET_EXCEEDED]` → warning `BUDGET_TRUNCATED:L1`

### Role State (L0 경계에 걸친 상속 상태)

handoff에 `## Role State` 블록이 있으면 파싱한다. 필수 필드:

| 필드 | 의미 |
| --- | --- |
| `Current Role` | `VALID_ROLES` 집합 안 값만 허용 |
| `Role Lock` | `ENABLED` / `DISABLED` |
| `Frozen` | 동결된 역할/작업 설명 |
| `Unlock` | 잠금 해제 조건 |

허용 역할: `Architecture`, `Research`, `Reference Implementation`, `Feature Implementation`, `Release / Operations`, `Incident Response`, `Maintenance`.

### Live bootstrap (`bootstrap_live.py`)

세션 시작용 동적 블록(~1KB 목표, `SELF_BLOCK_CAP = 1_000`). 메모리 본문을 재발행하지 않고 `bd prime --memories-only`를 L0/L1/untagged로 **감사**만 한다.

고정 섹션 순서:

```text
[MASTER-BOOTSTRAP v1]
## Role State
## Active tracks
## Charter
[BD-PRIME-AUDIT] ...
[DUAL-INSTANCE] ...
## Alerts  (있을 때만)
```

내부 예외는 전부 잡아 `[BOOTSTRAP-FALLBACK] <reason>` 한 줄로 낮춘다. boot가 죽지 않는 것이 계약이다.

### L1 관측 루프

`scripts/l1-digest tick --config ...`는 설정된 repo·ledger tail·job log·process 패턴을 읽고 digest를 쓴다. 작업 실행과 acceptance는 digest 밖에 있다.

## 모듈 대응표 (bootstrap · dispatch · succession · lineage)

### Bootstrap (U1 + Role 파싱)

| 표면 | 역할 |
| --- | --- |
| `bootstrap.py` | charter + handoff → budgeted L0/L1, Role State, dual-instance 경고 |
| `bootstrap_live.py` | SessionStart 블록: Role State, tracks, memory audit, dual-instance |
| `scripts/master-bootstrap` | CLI; `--charter`, `--handoff`, `--budget`, `--session-id`, `--strict-lease`, `--json` |
| `scripts/master-bootstrap-live` | CLI; `--handoff-dir`, `--role-state-file`, `--budget`, `--charter-pointer` |

`strict_lease=True`이고 dual-instance 경고가 있으면 `BootstrapError`로 실패한다.

### Dispatch / Worker path (U5 부분 + U11 watchdog)

```text
check -> (host dispatch) -> register -> independent verification -> acceptance
```

| 표면 | 역할 |
| --- | --- |
| `dispatch_gate.py` | 계약 가독성, 문자 예산, tier fan-out, reason code, JSONL ledger, ticket TTL |
| `scripts/dispatch-gate check` | allow/deny 기록 |
| `scripts/dispatch-gate register` | probe exit 0 + job id 출현 후에만 등록 |
| `scripts/dispatch-gate watch` | stall 검사 (`watchdog.check_stall`) |
| `scripts/dispatch-gate report` | ledger 집계 |
| `worker_reap.py` / `scripts/worker-reap` | settled dispatch 터미널 close, worktree 정리, reap ledger |
| `work_ledger.ReapObservability` | unreaped settled lease 탐지 |

**의도적 공백:** typed adapter launch 경로는 제거되었다. 워커 시작은 호스트(Orca 터미널)가 한다. 게이트는 그 단계를 **괄호로 감싸** 기록·검증한다. isolation 선택·전체 lease 발급기는 이 저장소에 없다.

리스 수명 (운영 runbook):

```text
issued → running → submitted → accepted → reaped
```

`scripts/worker-reap`는 settled(`COMPLETED`/`ACCEPTED`/`FAILED`/`ABANDONED`)가 아니면 exit 3으로 거절한다. 잘못된 reap은 비용이 크고, 건너뛴 reap은 싸다.

### Succession (U9) + Recovery (U8)

| 표면 | 역할 |
| --- | --- |
| `succession.py` | trigger 분류, handoff 작성, successor 검증, duplicate 탐지, retire, spawn placement 검증 |
| `scripts/master-succeed` | `detect`, `handoff`, `verify-successor`, `check-duplicates`, `retire`, `spawn` |
| `recovery.py` | Recovery Flow 0–6 **읽기 전용** (파일/프로세스 변경 없음) |
| `scripts/master-recover` | recovery 리포트 CLI |

라이프사이클:

```text
founding spawn → boot measurement → steady state → clean succession → lineage record
```

Successor 검증 상태: `PASS` | `PARTIAL` | `FAILED`.  
Predecessor retire: process/pane/tty 측정; 부분 소멸은 `CLOSED_PARTIAL`로 기록해 full close와 혼동하지 않는다.

### Lineage (U10)

`lineage.append_entry`는 마크다운 원장에 **append-only**로 한 generation을 붙인다. lineage는 런타임 결정을 먹이지 않는다 (부트 소스 금지).

필수 필드:

| 필드 | 제약 |
| --- | --- |
| `generation` | 정수; 이미 있으면 `LineageValidationError` |
| `parent_session`, `successor_session` | 텍스트 |
| `timestamp` | 텍스트 |
| `inherited_role` | 텍스트 |
| `succession_reason` | 텍스트 |
| `recovery_sources` | 텍스트 |
| `inherited_open_tracks` | 텍스트 |
| `verification` | `PASS` \| `PARTIAL` \| `FAILED` |
| `repeated_question_count`, `reopened_decision_count` | 카운트 |
| `context_loss_summary` | 텍스트 |
| `predecessor_retirement_verified` | 텍스트 |
| `notes` | 선택 |

### Context / Workspace (U2 · U3)

| 표면 | 역할 |
| --- | --- |
| `context/resolver.resolve` | manifest 선언 + 파일시스템 관찰 → `ContextDescriptor` |
| `ContextKind` | `folder`, `git-repo`, `git-worktree`, `multi-repo-workspace`, `nested-repo` |
| `JsonlWorkLedger` | track `register` / `update` / `close` JSONL 이벤트 |
| `WorkspaceRuntime` | ledger 위 세션 L1 캐시 |

경로 재귀 스캔은 non-goal이다. 관찰 범위: 선언된 repo, workspace root 직계 자식, 질의 경로의 조상.

### Approval (U6)

`classify(ActionSpec)` → `GateClass`:

| Class | 조건 |
| --- | --- |
| `G0_READ_ONLY` | 읽기 전용 |
| `G1_REVERSIBLE_LOCAL` | 로컬 가역 쓰기 |
| `G2_SHARED_STATE` | 공유 상태 쓰기 (또는 비-read_only 기본) |
| `G3_IRREVERSIBLE` | 비가역 |

`approval/registry.py`는 proposal lifecycle `PENDING → APPROVED|REJECTED → CONSUMED`를 강제한다. 승인 없는 gated 실행은 `ApprovalRequired`.

### Adapter (U12)

| 표면 | 역할 |
| --- | --- |
| `adapter/doctor.py` | git / node / orca / bd 존재 프로브 (`orca status --json` — `--version` 아님) |
| `adapter/profile.py` | sync CLI argv 프로파일 (Claude / Codex / Cursor) |
| `scripts/adapter doctor` | JSON 리포트 |

워커 launch 래핑은 공개 표면에 없다. pretrust 보조: `scripts/codex-worker-pretrust`, `scripts/cursor-worker-pretrust`.

## 부분 구현 유닛 — 정직한 경계

### U4 Repository Runtime Loader

모듈 없음. 저장소별 harness lazy-load는 설계 어휘로만 남아 있다. 현재는 U2 descriptor와 호스트 worktree가 그 자리를 대체한다.

### U5 Worker Scheduler

| 있음 | 없음 / 호스트 |
| --- | --- |
| 계약 check/register, 예산·tier 캡, ledger | 워커 프로세스 launch (Orca) |
| settled reap + unreaped 탐지 | 전체 lease 발급기·격리 정책 엔진 |
| stall watch | “모든 생성 경로가 게이트를 통과한다”는 workspace wiring 증명 |

공개 문서의 “no lease or reap module” 표현은 이후 `worker_reap` 추가로 완화되었다. 그래도 U5 전체 스펙(리스 발급·격리 선택·launch 래핑)은 미완성이다.

### U7 Role Runtime

`RoleState` 파싱·유효 역할 검증·live 블록 주입은 코드에 있다. role lock 강제, 역할 전이 상태 머신, “한 활성 역할” 런타임 락은 **운영 정책**(Role State 파일 + 마스터 규약)이다.

### U11 Observability

| 구현 조각 | 스크립트 |
| --- | --- |
| L1 digest | `scripts/l1-digest` |
| stall watchdog | `dispatch-gate watch` |
| acceptance loop | `scripts/acceptance-loop` |
| model identity / drift | `scripts/model-identity-probe`, `scripts/model-drift-audit` |

통합 observability bus나 중앙 알림 서비스는 없다. 각 프로브가 독립 exit 코드 계약을 가진다 (스크립트마다 0/1/2 의미가 다름).

## 능력 영역 ↔ 유닛

| 능력 영역 | 주 유닛 | 메커니즘 |
| --- | --- | --- |
| Execution environment | 호스트 + 스크립트 | Orca 터미널, worktree, `scripts/*` |
| Context management | U1, U2, U3, U11(부분) | bootstrap L0/L1, resolver, ledger, digest |
| Delegation | U5(부분), U12, U11 acceptance | dispatch-gate, host launch, acceptance-loop |
| Steering | U6, U7(부분) | approval registry, Role State 정책 |
| Filesystem model | U2 | real git paths; sensitive-lane 차단은 워크스페이스 호스트 훅 |

## 라이프사이클에서 유닛이 붙는 지점

```mermaid
flowchart LR
  A[U1 Bootstrap] --> B[Steady state]
  B --> C[U5 Dispatch gate]
  C --> D[Host worker]
  D --> E[U5 Register / Reap]
  E --> F[U11 Acceptance]
  B --> G[U8 Recovery read-only]
  B --> H[U9 Succession]
  H --> I[U10 Lineage append]
  I --> A
```

1. **Boot:** U1 (+ U7 Role State 파싱), 선택적 model probe (U11).  
2. **Operate:** U2 경로 판정, U3 트랙, U5 check/register/reap, U6 승인, U11 digest/probe.  
3. **Recover:** U8 Flow 0–6 읽기 전용 리포트.  
4. **Succeed:** U9 handoff → spawn → verify → retire → U10 lineage. 다음 세대는 다시 U1.

## 검증 신호

| 신호 | 의미 |
| --- | --- |
| `scripts/master-bootstrap --json` | L0/L1 길이, role_state, warnings |
| `scripts/master-bootstrap-live` 출력에 `[MASTER-BOOTSTRAP v1]` | live 블록 조립 성공 |
| `[BOOTSTRAP-FALLBACK]` | live 경로 내부 실패, boot은 계속 |
| `dispatch-gate check` ledger 행 | allow/deny + reason code |
| `dispatch-gate register` 실패 `UNVERIFIED_JOB` | probe가 job id를 증명하지 못함 |
| `worker-reap` exit 3 | dispatch not settled |
| `master-succeed spawn` placement mismatch | successor 좌석이 요청 selector와 불일치 |
| lineage generation 중복 예외 | 같은 generation 재기록 시도 |

단위 테스트 통과는 해당 유닛의 **로컬 실행 증거**일 뿐, 운영 workspace가 모든 경로를 그 유닛으로 강제한다는 증명은 아니다.

## 구현 상태 라벨 (문서 어휘)

| 라벨 | 의미 |
| --- | --- |
| Configured | 파일·스크립트·훅·정적 계약이 repo에 존재 |
| Intended | 설계 계약은 문서화, 라이브 런타임 증거 주장 안 함 |
| Observed | git/로컬 실행/로그/ledger/프로브로 self-report와 독립 관측 |
| Unknown | 현재 증거로 증명 불가, 또는 공개 표면 밖 |

Configured ≠ operating. 훅 파일이나 유닛 번호만으로 “동작 중”이라 쓰지 않는다.

## Related pages

<CardGroup cols={2}>
  <Card title="Overview" href="/overview">
    공개 표면, Orca 전제, 마스터/워커 역할
  </Card>
  <Card title="Master lifecycle" href="/master-lifecycle">
    founding spawn → boot → steady → succession → lineage
  </Card>
  <Card title="Orca object model" href="/orca-object-model">
    project·workspace·worktree·terminal·Run 좌석 규칙
  </Card>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    check → dispatch → register, ledger, model probe
  </Card>
  <Card title="Clean succession" href="/succession">
    handoff, placement spawn, retire, lineage 필드
  </Card>
  <Card title="Evidence and acceptance" href="/evidence-and-acceptance">
    self-report vs 독립 검증, acceptance 판정
  </Card>
  <Card title="Worker reap" href="/worker-reap">
    issued→reaped, settled 검증, dry-run·ledger
  </Card>
  <Card title="Defense inventory" href="/defense-inventory">
    디스패치·placement·redaction·revival 가드 표
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    scripts/ 공개 명령 표
  </Card>
</CardGroup>

---

## 05. Orca 객체 모델

> project·folder workspace·worktree·terminal·Run, 마스터 좌석 규칙, 검증된 selector 형식, 오해하기 쉬운 UI 라벨.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/05-orca.md
- Generated: 2026-08-07T07:02:10.538Z

### Source Files

- `docs/public/orca-concepts.md`
- `src/master_runtime/core/succession.py`
- `master-ops/docs/runbooks/succession-boot-card.md`
- `master-ops/onboarding/04-seat.md`
- `config/workspace-descriptor.example.json`
- `docs/public/master-lifecycle.md`

---
title: "Orca 객체 모델"
description: "project·folder workspace·worktree·terminal·Run, 마스터 좌석 규칙, 검증된 selector 형식, 오해하기 쉬운 UI 라벨."
---

이 런타임은 모든 에이전트를 Orca 안에 앉힌다. project·workspace(worktree/folder)·terminal·Run 계층과 마스터 좌석 규칙이 어긋나면 코드가 정상인데도 실측 misplacement가 난다. 공개 개념 문서는 `docs/public/orca-concepts.md`, 좌석 측정 절차는 `master-ops/onboarding/04-seat.md`, 배치 fail-closed 검증은 `scripts/master-succeed spawn`과 `src/master_runtime/core/succession.py`가 담당한다.

## 객체 계층

Orca가 추적하는 세 층과, 이 런타임이 그 위에 두는 오케스트레이션 문맥은 다음과 같다.

| 객체 | 의미 | 이 런타임에서의 역할 |
| --- | --- | --- |
| **Project** | Orca에 등록한 폴더(단일 Git repo, 여러 repo를 담은 plain folder, 빈 폴더 모두 project가 됨) | 사이드바 그룹은 표시용일 뿐 객체가 아님 |
| **Workspace** | project 안의 seat. terminal·에이전트가 사는 자리 | 종류가 첫 명령의 가능 범위를 결정함 |
| **Terminal** | workspace 안의 라이브 세션 | 마스터·워커 모두 terminal |
| **Run** | terminal에 묶인 내구성 오케스트레이션 문맥 | task·dispatch·mailbox를 세션 재시작 너머로 유지 |

```text
Project (Orca 등록 단위)
├── repository worktree  …  Git checkout 1개, 워커 seat
└── folder workspace     …  plain folder seat, 보통 마스터 seat
    └── Terminal         …  에이전트/셸 세션
        └── Run          …  디스패치·mailbox 바인딩
```

### Workspace 두 종류

프로젝트 형태가 어떤 workspace를 만들 수 있는지 결정한다.

| 종류 | 생성 경로 | 시작 cwd | 판정 필드 |
| --- | --- | --- | --- |
| **Repository worktree** | Create worktree 대화 / 저장소 project | checkout 안. git 즉시 가능 | `worktreeId` = `id:<repoId>::<path>` 계열 |
| **Folder workspace** | Create Folder Workspace / folder project | checkout 밖. git은 먼저 cd 필요 | `worktreeId` = `folder:<uuid>`, `worktreePath` 비어 있음(정상) |

워크스페이스 루트가 plain folder이고 멤버 저장소가 sibling으로 늘어선 형태가 이 런타임의 기본 모델이다. 서브모듈 부모는 지원하지 않는다. 선언형 인벤토리는 `config/workspace-descriptor.json`(예시는 `config/workspace-descriptor.example.json`)에 둔다.

## 마스터 좌석 규칙

마스터는 워크스페이스 전체 저장소를 조율하므로 **workspace-level seat**에 앉는다.

| 워크스페이스 형태 | 마스터 seat | selector 형태(권장 기록) |
| --- | --- | --- |
| multi-repo, root = plain folder project | 그 root의 **folder workspace** | `id:folder:<uuid>` |
| single-repo workspace | 그 저장소의 **primary worktree** | `id:<repoId>::<path>` |

<Warning>
multi-repo 워크스페이스에서 마스터를 개별 저장소 worktree에 앉히면 cwd·hooks·session 파일이 맞아 보여도 misplacement다. 사이드바에서는 그 저장소 아래에 걸리고, 워커용 seat를 점유한다. 2026-08-03 실측 사고가 이 형태였다.
</Warning>

온보딩 Step 3.5(`master-ops/onboarding/04-seat.md`) 절차 요약:

1. ops 저장소를 `orca repo add`로 등록(워커 worktree 소스).
2. `{{WORKSPACE_ROOT}}`를 project로 두고 folder workspace를 연다(단일 저장소여도 parent folder seat).
3. **임시 plain terminal**로 seat id만 측정한다. 마스터를 이 단계에서 만들지 않는다.
4. `orca terminal show --terminal <handle> --json`으로 `worktreeId`를 읽고, durable 기록에는 **`id:` 접두 형태**만 남긴다.
5. 임시 terminal을 닫아 좌석을 비운 뒤 founding spawn(`scripts/master-succeed spawn`)이 유일한 마스터 terminal이 되게 한다.

durable identity는 terminal handle이 아니다. handle은 앱 런타임 스코프이며 재시작과 함께 죽는다. 남는 것은 selector다.

descriptor의 `master_seat` 필드는 보통 `folder-workspace-of-workspace-root` 같은 서술 문자열이며, 실측 selector 문자열과 1:1이 아닐 수 있다. 실행 경로의 권위는 측정된 selector와 spawn 검증이다.

## 워커 좌석

마스터와 반대다.

| 역할 | seat | 금지 |
| --- | --- | --- |
| 마스터 | folder workspace(또는 single-repo primary worktree) | multi-repo에서 개별 product worktree에 안착 |
| 워커(git 필요) | 대상 저장소의 **isolated repository worktree**, 자체 브랜치 | folder workspace 공유, 마스터 checkout 공유 |

folder seat에 워커를 두는 것은 첫 명령이 checkout으로 `cd`할 때만 허용된다. 그렇지 않으면 첫 git 호출에서 실패한다. 생성만 하고 prompt/cwd를 확인하지 않으면 “만든 terminal”과 “동작 중인 worker”를 혼동한다.

워커 수명 끝에서는 `scripts/worker-reap`이 terminal close와 clean/merged worktree 정리를 담당한다. dirty·unmerged·접근 불가 worktree는 제거하지 않고 사유를 남긴다.

## 검증된 selector 형식

placement 명령(`orca terminal create --worktree`, `scripts/master-succeed spawn --workspace-selector`)은 selector 문자열을 받는다. 2026-08-03 실측과 `succession.py` 정규화 규칙이 합쳐진 동작은 다음과 같다.

| Selector | 동작 | 권장 |
| --- | --- | --- |
| `id:<repoId>::<path>` | repository worktree end-to-end | 기본. `orca worktree list --json`의 full `id` 복사 |
| `id:folder:<uuid>` | folder workspace: list precheck·create·spawn match 통과 | durable 기록 표준 |
| `folder:<uuid>` (bare) | `terminal create`는 수용, `terminal list --worktree`는 `selector_not_found`로 거부(하위명령 비대칭). spawn 코드는 list 시 `id:`를 붙인다 | 한 문자열이 모든 소비자에서 통해야 하면 `id:` 형태 |
| `path:/abs/dir` | Orca는 `<repoId>::<path>`로 해석. spawn 비교기가 거부하거나 경로 해석 매칭에만 부분 성공 | durable/요청 문자열로 쓰지 말 것 |

`succession.py`의 정규화:

- `_normalize_worktree_selector`: 선행 `id:`를 벗겨 비교한다. 응답의 `repoId::path`와 요청의 `id:repoId::path`를 동일 좌석으로 본다.
- `_terminal_list_worktree_selector`: bare `folder:`이면 list 전에 `id:`를 붙인다.
- `_path_worktree_matches`: 요청이 `path:`일 때만 actual `repoId::path`와 realpath 비교한다. folder seat에는 해당 없음.

두 규칙:

1. **Selector는 추론하지 말고 측정한다.** `orca terminal show` 또는 `orca worktree list --json`. `--worktree` 생략 시 cwd 추론이 misplacement 원인이 된다.
2. **placement match는 “요청한 것과 같은가”만 본다.** “요청 자체가 올바른 자리인가”는 lineage/descriptor 기대값과 별도로 비교해야 한다. 요청을 녹색이 될 때까지 고쳐 통과시킨 것이 2026-08-03 misplacement 경로였다.

## Spawn placement 검증

`scripts/master-succeed spawn`은 호스트 terminal을 만들고 응답 `worktreeId`를 요청 selector와 대조한다.

```bash
scripts/master-succeed spawn \
  --workspace-selector "id:folder:<uuid>" \
  --kickoff-text "Founding master boot" \
  --root . \
  --model example-model \
  --title "Founding master boot" \
  --expected-placement "id:folder:<uuid>" \
  --json \
  --dry-run
```

| 플래그/필드 | 의미 |
| --- | --- |
| `--workspace-selector` | create에 넘기는 요청 좌석(필수) |
| `--expected-placement` | 독립 기대 `worktreeId`. 불일치 시 fail-closed, exit **26** (`SPAWN_PLACEMENT_MISMATCH`) |
| 요청 vs 응답 worktree 불일치 | exit **22** (`SPAWN_WORKTREE_MISMATCH`), 가능하면 생성 terminal close |
| create 명령 | `orca terminal create --worktree <selector> --title … --command … --json` |
| liveness | create 전 snapshot → 후 list. handle이 live·new·요청 worktree 안이어야 함. 아니면 동일 title 후보 1개만 `MATCH_REISSUED`로 채택 |

```text
requested selector ──create──► worktreeId
        │                         │
        ├─ _worktrees_match? ─────┤
        │                         │
        └─ expected_placement? ───┘
                 fail-closed → close terminal when safe
```

힌트 문자열(오류 메시지 꼬리):  
`Accepted selector forms are full id:<repoId>::<path>, id:folder:<uuid> for folder workspaces; path: is matched by resolved directory.`

placement 증거 three-set(승계 부트 카드):

1. host pane / worktree selector가 의도 workspace와 일치
2. process cwd가 `{{WORKSPACE_ROOT}}` 아래
3. session artifact/log 경로가 기대 namespace

UI pane title·status line은 힌트이지 placement 증거가 아니다.

## 오해하기 쉬운 UI·CLI 라벨

| 표시 | 실제 의미 | 조치 |
| --- | --- | --- |
| **Unavailable worktree** (Agent Session History) | 세션 seat가 Git worktree가 아님 | folder-workspace 마스터에서 **정상**. 크래시 아님 |
| **빈 `worktreePath`** (`orca terminal list --json`) | folder workspace 형태 | `worktreeId`(`folder:<uuid>`)로 판정. path로 기각하지 말 것 |
| **Not a valid worktree folder** (workspace root) | root가 git repo/worktree가 아님 | plain folder 그룹 루트에서 **의도된 경고**. submodule 부모로 만들지 말 것 |
| pane status의 저장소 이름 | shell cwd 반영 | seat identity가 아님 |
| `process_id: null` (folder-workspace pane) | 호스트 list가 pid를 안 줌 | retire 시 pid/tty를 호출자가 실측해 전달 |

## Workspace descriptor와의 관계

| 키 | 값/제약 |
| --- | --- |
| `workspace_root_is_plain_folder` | 이 런타임에서 항상 true로 해석 |
| `workspace_root` | 절대 경로. absolute path lookup용 |
| `master_seat` | 마스터 seat 서술(보통 folder-workspace-of-workspace-root) |
| `repositories[].path` | workspace root 상대 경로 |
| `repositories[].role` | `product` \| `ops` |
| `capabilities` / `prohibited` | 워커 레인 허용·하드 스톱(open set) |

소비자는 `scripts/workspace-descriptor-check`와 워커 라우팅이다. 해석 순서: 환경 오버라이드 → 파일 → unconfigured. submodule shape는 없다.

## 원격·모바일 함의

Orca 세션은 시작한 책상에 묶이지 않는다(SSH remote worktree, headless `orca serve`, mobile companion). 이 런타임에 직접 닿는 결과:

- frozen 마스터는 폰에서도 재개 가능 → retirement 완료는 process·pane·tty **세 소멸 실측** 후에만.
- boot에 lineage session id revival 스캔이 있다(2026-08-03: 은퇴 마스터 4개가 동시 부활).
- 원격 머신 저장소에 대한 주장은 git remote·forge 상태로 따로 측정한다.

Orca 플래그를 추측하지 않는다. `orca --help`, `orca skills get`, `master-ops/docs/orca-docs-grounding.md` 스냅샷 순으로 측정한다.

## 검증 신호

<Check>
좌석이 맞으면 다음이 동시에 성립한다.
</Check>

- `orca terminal show`의 `worktreeId`가 workspace-level seat(`folder:<uuid>` 또는 단일 repo primary worktree)
- durable ops 기록의 selector가 `id:` 접두
- 마스터 founding/successor spawn 전 해당 seat에 임시 terminal이 없음
- `scripts/master-succeed spawn … --json`이 `verified: true`, 요청과 `worktreeId` 일치
- folder 마스터의 빈 `worktreePath` / Unavailable worktree chip을 오류로 취급하지 않음

## 트러블슈팅

| 증상 | 원인 | 대응 |
| --- | --- | --- |
| `terminal list`가 selector 거부 | bare `folder:<uuid>` | `id:folder:<uuid>`로 재기록 |
| spawn 비교 거부 / exit 22 | 요청·응답 worktree 불일치 | 기대 seat 재측정, path 치환 금지 |
| exit 26 `SPAWN_PLACEMENT_MISMATCH` | `--expected-placement`와 실제 `worktreeId` 불일치 | lineage 기대값과 요청을 각각 수정; “통과할 때까지 요청 변경” 금지 |
| `selector_not_found` on create | project 미등록·잘못된 id | `orca repo add`, `worktree list --json` |
| 마스터가 product repo 아래 표시 | repository worktree에 착석 | folder workspace로 재측정·재spawn |
| folder seat 워커 git 실패 | checkout 없는 cwd | repository worktree로 재배치 |

## Related pages

<CardGroup>
  <Card title="마스터 라이프사이클" href="/master-lifecycle">
    founding spawn부터 succession·lineage까지의 세대 루프.
  </Card>
  <Card title="Clean succession" href="/succession">
    handoff, placement 검증 spawn, retire, revival 측정.
  </Card>
  <Card title="Workspace descriptor" href="/workspace-descriptor">
    sibling 저장소 인벤토리, role·prohibited, master_seat.
  </Card>
  <Card title="master-succeed 레퍼런스" href="/succession-cli-reference">
    spawn 플래그, exit 코드, SPAWN_PLACEMENT_MISMATCH.
  </Card>
  <Card title="프로그레시브 온보딩" href="/onboarding">
    seat 측정(Step 3.5)과 founding spawn 경계.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    placement mismatch, seat 중복, revival 복구.
  </Card>
</CardGroup>

---

## 06. 마스터 라이프사이클

> founding spawn → boot measurement → steady state → clean succession → lineage 기록 루프와 Role State·compaction 규칙.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/06-page-6.md
- Generated: 2026-08-07T07:02:38.852Z

### Source Files

- `docs/public/master-lifecycle.md`
- `src/master_runtime/core/bootstrap.py`
- `src/master_runtime/core/bootstrap_live.py`
- `src/master_runtime/core/succession.py`
- `src/master_runtime/core/lineage.py`
- `src/master_runtime/core/recovery.py`
- `master-ops/docs/runbooks/role-state.md`

---
title: "마스터 라이프사이클"
description: "founding spawn → boot measurement → steady state → clean succession → lineage 기록 루프와 Role State·compaction 규칙."
---

마스터 세션은 장수(long-lived)하지만 불멸이 아니다. 이 런타임은 마스터를 **운영 lineage의 한 generation**으로 취급한다. 공개 표면은 `scripts/master-succeed`, `scripts/master-bootstrap`, `scripts/master-bootstrap-live`, `scripts/master-recover`, `scripts/model-identity-probe`, `scripts/model-drift-audit`이며, 핵심 구현은 `src/master_runtime/core/`의 bootstrap·succession·recovery·lineage 모듈이다.

## 루프 한눈에

```text
founding spawn → boot measurement → steady state → clean succession → lineage record
```

| 단계 | 목적 | 주 명령 |
| --- | --- | --- |
| Founding spawn | 설치 대화와 Gen 1 세션 분리, 좌석 placement 검증 | `scripts/master-succeed spawn` |
| Boot measurement | charter·handoff·Role State·budget·dual-instance 측정 | `scripts/master-bootstrap`, `scripts/master-bootstrap-live` |
| Steady state | continue-and-compact, 수락 상태 내구성 승격 | `scripts/l1-digest tick`, issue tracker / Git |
| Clean succession | 명시 트리거 → handoff → successor 검증 → retire | `scripts/master-succeed *` |
| Lineage | generation 메타 append-only 기록 (결정 권한 없음) | `append_entry` → `docs/lineage/MASTER-LINEAGE.md` |

```mermaid
flowchart LR
  A[Founding spawn] --> B[Boot measurement]
  B --> C[Steady state]
  C --> D{Succession trigger}
  D -->|NONE / ADVISORY only| C
  D -->|IMMEDIATE + owner path| E[Thin handoff]
  E --> F[Successor verify]
  F --> G[Predecessor retire]
  G --> H[Lineage append]
  H --> B
```

자동 succession은 허용되지 않는다. `detect`가 `ADVISORY`를 내도 spawn을 시작하지 않으며, 명시 지시(`IMMEDIATE`)와 운영 절차가 있어야 한다.

## Founding spawn

첫 실행 절차의 전체 소유권은 온보딩 가이드에 있다. 인스톨러 대화와 Generation 1 마스터 세션을 분리해, 마스터가 **깨끗한 컨텍스트**와 **감사 가능한 placement**로 시작하도록 한다.

### Placement

| 워크스페이스 형태 | 마스터 좌석 | Selector 형태 |
| --- | --- | --- |
| Multi-repository | workspace root의 folder workspace | `id:folder:<uuid>` |
| Single repository | primary worktree | 해당 worktree selector |

Multi-repository 안의 repository worktree는 **워커 좌석**이다. 마스터를 그 자리에 두면 placement 사고다.

### Spawn 진입점

실제 spawn 진입점은 `scripts/master-succeed spawn`이다. dry-run으로 호스트 명령을 먼저 확인한다.

```bash
scripts/master-succeed spawn \
  --workspace-selector "id:folder:<uuid>" \
  --kickoff-text "Founding master boot" \
  --root . \
  --model example-model \
  --title "Founding master boot" \
  --json \
  --dry-run
```

| 옵션 | 역할 |
| --- | --- |
| `--workspace-selector` | 생성 대상 worktree / folder workspace (필수) |
| `--kickoff-text` / `--kickoff-file` | 상호 배타, 하나 필수 |
| `--root` | 에이전트 cwd (필수) |
| `--title` | pane title (필수) |
| `--model` | 런치 모델 플래그 |
| `--agent` | 기본 `claude`; `grok`, `codex`, 커스텀 실행 파일명 가능 |
| `--expected-placement` | 독립 기대 worktree id; 불일치 시 exit **26** (`SPAWN_PLACEMENT_MISMATCH`) |
| `--dry-run` | 생성 없이 명령 스냅샷만 |

### Fail-closed 검증

호스트가 managed terminal 생성을 지원하면 non-dry-run spawn은:

1. 반환 worktree id가 요청 selector와 일치하는지 확인한다. 불일치 시 fail-closed이며, 가능하면 방금 만든 터미널을 닫는다.
2. 반환 handle이 **live**인지 확인한다. 생성 전 terminal list 스냅샷 → 생성 → 재조회.
3. 보고된 handle이 live·new·요청 worktree 안이면 `MATCH`.
4. 그렇지 않으면 요청 worktree에서 **요청 pane title을 가진 새 터미널이 정확히 하나**일 때만 대체 handle을 채택하고 `MATCH_REISSUED` / `handle_reissued=true`로 보고한다.
5. 후보 0개 또는 2개 이상은 fail-closed. 이때 생성된 터미널이 관리되지 않은 채 남을 수 있으므로, 재시도 전 호스트 terminal list로 조정한다.

관련 exit 상수: `SPAWN_CREATE_ERROR` 20, `SPAWN_PARSE_ERROR` 21, `SPAWN_WORKTREE_MISMATCH` 22, `SPAWN_CLOSE_ERROR` 23, `SPAWN_HANDLE_STALE` 24, `SPAWN_LIST_ERROR` 25, `SPAWN_PLACEMENT_MISMATCH` 26.

## Boot measurement

Boot는 추측이 아니라 **측정**이다. Charter(필수), optional handoff, Role State, budget 사용량, dual-instance 경고를 한 스냅샷으로 읽는다.

### `master-bootstrap` (측정 스냅샷)

```bash
scripts/master-bootstrap \
  --charter master-ops/docs/MASTER-OPERATIONS.md \
  --handoff ./handoffs/latest.md \
  --session-id example-session \
  --json
```

| 플래그 | 기본 | 의미 |
| --- | --- | --- |
| `--charter` | (필수) | L0 본문 소스 |
| `--handoff` | 없음 | L1 + Role State 소스 |
| `--budget` | `24000` | L0 우선 적재 후 잔여를 L1에 |
| `--session-id` | 없음 | dual-instance 프로브 키 |
| `--strict-lease` | off | `DUAL_INSTANCE:*` 있으면 `BootstrapError` (exit 2) |
| `--json` | off | `BootstrapResult` 직렬화 |

적재 순서:

1. Charter 전체 읽기 (없으면 fail).
2. Handoff가 있으면 Role State 파싱; 블록 없으면 `ROLE_STATE_MISSING`, 파일 없으면 `HANDOFF_MISSING`.
3. L0 = charter를 budget에 맞춤. 잘리면 `BUDGET_TRUNCATED:L0` + `[TRUNCATED:BOOTSTRAP_BUDGET_EXCEEDED]`.
4. 잔여 budget으로 L1 = handoff. 잘리면 `BUDGET_TRUNCATED:L1` + `[TRUNCATED:L1_BUDGET_EXCEEDED]`.
5. `session_id`가 있으면 프로세스 목록에서 동일 session id를 담은 `claude` 명령을 찾고, 자기 PID를 제외한 중복에 `DUAL_INSTANCE:<pid>`를 붙인다.

### `master-bootstrap-live` (SessionStart 배선)

라이브 부트는 세션 시작 훅용이다. 메모리 본문을 재발행하지 않고, ~1KB self-block과 audit 라인만 낸다. 내부 예외는 `[BOOTSTRAP-FALLBACK] …` 한 줄로 접어 **부트를 죽이지 않는다**.

```bash
scripts/master-bootstrap-live \
  --handoff-dir ./handoffs \
  --role-state-file master-ops/docs/runbooks/role-state.md
```

| 동작 | 규칙 |
| --- | --- |
| Role State 로드 | `--role-state-file` 우선, 실패 시 handoff dir의 사전순 최대 `*.md` |
| Tracks | `bd` 수집, self-block cap **1000** chars (tracks 우선 절단) |
| `bd prime --memories-only` | 본문 재출력 금지; L0/L1/untagged 카운트 audit만 |
| Dual-instance | 동일 프로브, `[DUAL-INSTANCE] none` 또는 경고 목록 |
| Charter pointer | `--charter-pointer` → `CHARTER_POINTER` env → 워크스페이스 중립 기본 문구 |
| Memory budget 기본 | **12000** chars |

### 모델 신원 측정

런치 플래그의 모델과 **세션이 실제로 쓰는 모델**은 분리한다. 측정 불가면 unavailable로 보고하고, 플래그로 추론하지 않는다.

```bash
# 최근 턴 샘플 — 지금 무엇인가
scripts/model-identity-probe \
  --transcript ./sessions/example-session.jsonl \
  --expect example-model

# 전체 트랜스크립트 — 중간에 바뀌었는가
scripts/model-drift-audit \
  --transcript ./sessions/example-session.jsonl \
  --expect example-model
```

| Exit | 의미 |
| --- | --- |
| 0 | 전이 없음 / 기대와 일치 (도구별 계약) |
| 1 | 전이 또는 기대 불일치 |
| 2 | undecidable (파싱 불가·스키마 불명) — pass로 접지 말 것 |

트랜스크립트 워커가 읽는 스키마는 빌드 대상(예: Claude Code JSONL)에 묶인다. Codex 등 다른 저장 형태는 exit 2 → **unsupported**로 보고한다. CLI 간 portable 키는 process argv의 **session id**이며, revival 스캔도 이 키를 쓴다.

Boot 측정은 **한 순간의 스냅샷**이다. 재측정 시점은 workspace master-operations 문서가 정한다. 현장 원인에는 미전파 런치 플래그뿐 아니라 quota·credit 소진 중 모델 전환이 포함되며, 선언 모델과 측정 모델이 어긋난 generation이 succession 감사에서야 발견된 사례가 있다. 안전한 대응: 측정값을 기록하고, 필요 시 민감 레인에서 마스터를 빼고, 신뢰할 수 없으면 clean successor를 연다.

## Role State

Role State는 handoff(또는 `role-state.md`)의 고정 필드 블록이다. Bootstrap 파서는 `## Role State` 근처 fenced block 또는 `Current Role:`…`Unlock:` 폴백을 읽는다.

캐논 템플릿 (`master-ops/docs/runbooks/role-state.md`):

```text
Current Role: (not booted; declare on first master start)
Role Lock: ENABLED
Frozen: all other roles
Unlock: explicit user instruction only
```

| 필드 | 허용 값 / 규칙 |
| --- | --- |
| `Current Role` | `Architecture`, `Research`, `Reference Implementation`, `Feature Implementation`, `Release / Operations`, `Incident Response`, `Maintenance` 만 |
| `Role Lock` | `ENABLED` / `DISABLED` (그 외 `BootstrapError`) |
| `Frozen` | 보통 `all other roles` |
| `Unlock` | 보통 `explicit user instruction only` |
| Generation | 0 → 첫 부트 시 1; 역할 전환은 Proposal→Approval 직후 또는 succession boot 때만 |
| 감사 | Git history가 transition audit trail |

Succession handoff 작성 시 `freeze_roles`가 현재 역할만 남기고 lock을 `ENABLED`로 고정한다. 필수 필드 누락·알 수 없는 역할은 bootstrap이 예외로 거부한다.

**역할 침식 함정:** 마스터가 대상 저장소의 agent instruction 파일을 읽은 뒤 그 저장소의 paired developer 역할을 함께 주장하는 구조적 유인. 대상 repo 지침은 **조정 대상에 대한 지식**이며, 그 역할 채택은 금지. 운영 규칙은 workspace master-operations에 둔다.

## Steady state (continue-and-compact)

정상 운영은 continue-and-compact다. 컨텍스트 압력이 급해지기 **전에** 수락된 지식, active tracks, open decisions를 issue tracker 또는 Git 등 **내구성 저장소**로 승격한다. Compaction·clear 직후에는 issue-tracker 컨텍스트를 다시 읽고 active tracks를 재확인한다.

| 표면 | 역할 |
| --- | --- |
| `scripts/master-bootstrap-live` | 세션 시작 시 bounded Role State / tracks / audit |
| `scripts/l1-digest tick --config …` | repo·ledger·job log·process pattern 관찰 후 digest 기록 (작업·acceptance 자체는 외부) |
| issue tracker / Git | 승격된 SSOT; 휘발 context만 잃도록 |

의도적 context-limit 실험에서 마스터는 한도 근처까지 조정을 이어갔지만, 다음 compacted 턴에서 회상·훅 커버 공백이 드러났다. 교훈: full window가 안전하다는 뜻이 아니라, 압력은 측정하고, 수락 상태는 이미 내구성 저장소에 있어야 하며, compaction vs succession은 낙관이 아니라 증거로 고른다.

사고 복구(프로세스 사망, 호스트 재시작, stale UI handle, 실수 pane 닫힘)는 succession이 아니다. 동일 session의 중복 live 프로세스가 없음을 증명한 뒤 same-session resume을 먼저 시도한다 (`master-ops/docs/runbooks/succession-boot-card.md`).

## Clean succession

### 트리거 분류

```bash
scripts/master-succeed detect \
  "succession now" \
  --context-ratio 0.65 \
  --json
```

| Status | 조건 | 자동 spawn |
| --- | --- | --- |
| `IMMEDIATE` | 문구 포함: `succession now`, `handoff to successor`, `승계해줘`, `다음 마스터로 넘기자`, `승계 진행해` | 아니오 — 절차 시작 신호일 뿐 |
| `ADVISORY` | `context_ratio >= 0.60` 또는 `milestone` 비어 있지 않음 | 아니오 — propose only |
| `NONE` | 위 아님 | — |

### 절차

<Steps>
  <Step title="Promotion audit">
    현재 마스터가 수락 지식·active tracks·open decisions·미해결 acceptance 증거를 내구성 저장소로 올린다. audit 없이 successor를 spawn하지 않는다.
  </Step>
  <Step title="Thin handoff 작성">
    ```bash
    scripts/master-succeed handoff --spec ./ops/handoff-spec.json --json
    ```
    출력 섹션: Role State (frozen), Current Objective, Active/Open Tracks, Accepted Artifacts, Deferred Work, Open Questions, Recommended Next Role, Observed Baseline.
  </Step>
  <Step title="Successor spawn">
    founding과 동일하게 `spawn` + placement 검증. kickoff·model·root·title 명시. Boot card: successor cwd는 workspace root 또는 승인된 orchestrator root.
  </Step>
  <Step title="Recovery 측정 + verify-successor">
    `scripts/master-recover`가 Recovery Flow 0–6을 **읽기 전용**으로 실행한다 (파일 쓰기·프로세스 변이 없음). 보고 형태를 검증:

    ```bash
    scripts/master-succeed verify-successor \
      --report ./ops/recovery-report.json \
      --json
    ```

    | 판정 | 조건 |
    | --- | --- |
    | `FAILED` | 어떤 step status라도 `MISS` |
    | `PASS` | open tracks 낭독(step 6 OK) + baseline(step 2-3 OK/없음) + monitors rearmed(step 5 OK) 세 체크 |
    | `PARTIAL` | 일부 체크만 |
  </Step>
  <Step title="Predecessor retire (handshake 후)">
    검증 후에만 retire. Freeze ≠ retirement. `scripts/master-succeed retire`는 후보 정확히 1개, self-handle 거부, 기본 dry-run, pane 생존 시 거부.
  </Step>
  <Step title="Lineage append">
    검증·retirement 결과를 `docs/lineage/MASTER-LINEAGE.md`에 append. lineage로 다음 행동을 결정하지 않는다.
  </Step>
</Steps>

### Placement evidence three-set (boot card)

1. host pane / worktree selector가 의도 workspace와 일치
2. process cwd가 workspace root 아래
3. session artifact / log path가 기대 namespace

UI pane title·status line은 **힌트**이지 placement 증거가 아니다.

## Retirement handshake

Retirement는 두 live 에이전트 사이의 teardown이다. 도구의 `close`만 호출하는 것은 TCP `RST`에 가깝다 — 에이전트 합의 없이 unflushed 작업을 잃는 경로이며, owner 승인 없이 쓰지 않는다.

| TCP | Master retirement | 행위자 |
| --- | --- | --- |
| `FIN →` | 작업 수락 중지·flush 지시 | successor |
| `← ACK` | 인지 후 CLOSE_WAIT | predecessor |
| half-close | send 측 유지: commit·보고 가능 | predecessor |
| `← FIN` | 합의된 FIN 한 줄, 이후 무출력 | predecessor |
| `ACK →` | 수신 확인 | successor |
| TIME_WAIT | 호스트 last-output + pane read로 quiet 측정 | successor |
| `CLOSED` | `retire --execute` + 삼중 disappearance | successor |
| `RST` | 합의 없는 abortive close | successor (owner 승인) |

CLOSE_WAIT에서 predecessor는:

1. 신규 dispatch 없음  
2. successor가 drain할 peer-mailbox ack 없음 (또는 양쪽 이중 drain)  
3. orchestration에 추가 송신 없음 — pane close가 Run binding 해제  
4. uncommitted 상태 commit, 불가 시 경로·내용 보고  
5. 소유 live worker와 미완료 track 이름 나열  
6. 합의 FIN 한 줄만 출력  

FIN은 idle 상태 live prompt에 전달한다. mailbox만 쓰고 idle 에이전트가 안 읽으면 배달이 아니다. send 성공 ≠ 소비 — pane 읽기로 확인. FIN 마커는 successor 지시 문자열에 그대로 들어가면 안 된다(또는 출현 횟수 ≥ 2 요구).

TIME_WAIT: handle 회전·transcript replay 중 dual process가 있을 수 있다. FIN 직후 즉시 close하면 재생성 중인 pane을 닫을 수 있다.

```bash
scripts/master-succeed retire \
  --self-handle <successor-handle> \
  --target-handle <predecessor-handle> \
  --target-pid <measured-pid> \
  --target-tty <measured-tty> \
  --json \
  --execute
```

Folder-workspace pane은 host list에서 `process_id: null`인 경우가 많다. pid·tty는 호출자가 측정해 넘긴다. 도구가 null 레코드에서 pid를 지어내지 않는다.

| `disappearances` 값 | 의미 |
| --- | --- |
| `measured` | 해당 축 소멸 확인 |
| `still_present` | 잔존 → 전체 `REFUSED` |
| `skipped:…` | 측정 입력 없음 |

| Status | 조건 |
| --- | --- |
| `DRY_RUN` | `--execute` 없음 |
| `REFUSED` | 후보 0/다수, self match, still_present, pane 미측정 |
| `CLOSED` | pane·process·tty 모두 `measured` |
| `CLOSED_PARTIAL` | pane `measured`, still_present 없음, process/tty 중 skip |

CLOSE 명령 반환값만으로 완료를 판단하지 않는다. Full `CLOSED` 전 predecessor scratchpad를 비-git 내구성 위치에 복사하고 trunk와 diff 판정을 남긴다.

### Revival

동결된 세션은 에이전트 CLI가 도는 어떤 터미널(폰·원격 포함)에서도 resume 가능하다. 측정 사고: 은퇴 마스터 4개가 모바일 resume으로 동시 부활. Boot card 규칙:

1. boot 시 및 owner가 stray를 보고할 때, `MASTER-LINEAGE.md`의 session id를 **process argv**에서 스캔  
2. hit 시: 동결 이후 활동 읽기 → 미응답 owner 지시 회수 → 에이전트 종료 → host terminal chain hangup → tty 소멸 확인  
3. 동일 삼중 disappearance로 닫기  

Handoff 문구의 “predecessor gone”과 runtime state를 분리한다. 클린 succession = handoff 읽기만이 아니라 role·track·process ownership·verification 책임의 측정된 이전.

## Lineage

Lineage는 **append-only observability**다. bootstrap 소스가 아니고 runtime 결정 입력이 아니다. 공개 규칙: 검증 후 append, 다음 행동 권한으로 쓰지 않음.

`append_entry(path, entry)` 필수 필드:

| 필드 | 타입 / 제약 |
| --- | --- |
| `generation` | non-negative int; 동일 generation 재기록 거부 |
| `parent_session` | str |
| `successor_session` | str |
| `timestamp` | str |
| `inherited_role` | str |
| `succession_reason` | str |
| `recovery_sources` | str |
| `inherited_open_tracks` | str |
| `verification` | `PASS` \| `PARTIAL` \| `FAILED` |
| `repeated_question_count` | non-negative int |
| `reopened_decision_count` | non-negative int |
| `context_loss_summary` | str |
| `predecessor_retirement_verified` | str |
| `notes` | optional str |

쓰기는 원본 bytes + 새 섹션 동등성 검사; 실패 시 원본 복구 후 `LineageAppendError`. 렌더 형태: `## Gen <n> — <timestamp>` 마크다운 섹션.

## 공개 CLI 요약

| 명령 | 하위 명령 / 용도 |
| --- | --- |
| `scripts/master-succeed` | `detect`, `handoff`, `verify-successor`, `check-duplicates`, `retire`, `spawn` |
| `scripts/master-bootstrap` | L0/L1 budgeted snapshot + Role State + dual-instance |
| `scripts/master-bootstrap-live` | SessionStart bounded block, fail-open fallback |
| `scripts/master-recover` | Recovery Flow 0–6 read-only report |
| `scripts/model-identity-probe` | 최근 턴 모델 측정 |
| `scripts/model-drift-audit` | 전체 트랜스크립트 모델 전이 감사 |
| `scripts/l1-digest` | 관찰 전용 L1 digest loop |

공통: 대부분 `--json` 지원. 하드 가드 위반은 exit **2** 계열 (`SuccessionError`, `BootstrapError`). Spawn placement mismatch는 **26**.

## 실패 모드 체크리스트

| 증상 | 우선 확인 |
| --- | --- |
| `SPAWN_PLACEMENT_MISMATCH` / exit 26 | selector vs 생성 worktree; multi-repo에서 worker worktree에 앉혔는지 |
| `MATCH_REISSUED` / 미해결 handle | title 유일성; orphan terminal list 정리 후 재시도 |
| `DUAL_INSTANCE:*` / strict-lease fail | 동일 session id 프로세스; `check-duplicates` |
| `ROLE_STATE_MISSING` / step 0 WARN | handoff Role State 블록 필드 네 개 |
| model probe exit 2 | 에이전트 transcript 스키마 unsupported → unavailable 보고, 추측 금지 |
| `CLOSED_PARTIAL` | `--target-pid` / `--target-tty` 측정 후 재실행 |
| `still_present` after retire | TIME_WAIT·동의 경로 재확인; RST는 owner 승인 |
| 은퇴 후 세션 재개 | lineage session id revival 스캔 |
| 역할 이중 주장 | 대상 repo AGENTS/Claude 지침을 역할로 채택했는지 |

## Related pages

<CardGroup cols={2}>
  <Card title="프로그레시브 온보딩" href="/onboarding">
    ONBOARDING 라우터, founding spawn, Stage 1/2, 1회 1로드 Verify
  </Card>
  <Card title="Orca 객체 모델" href="/orca-object-model">
    project·folder workspace·worktree·terminal·Run, 마스터 좌석, selector
  </Card>
  <Card title="Clean succession" href="/succession">
    handoff·placement·verify-successor·retire·revival·lineage 필드 상세
  </Card>
  <Card title="master-succeed 레퍼런스" href="/succession-cli-reference">
    하위 명령 옵션, exit 코드, SPAWN_PLACEMENT_MISMATCH, JSON 출력
  </Card>
  <Card title="런타임 유닛" href="/runtime-units">
    U1–U12, L0/L1, bootstrap·dispatch·succession·lineage 대응
  </Card>
  <Card title="방어 인벤토리" href="/defense-inventory">
    placement·empty-seat·duplicate·revival·onboarding 가드 표
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    placement mismatch, undecidable exit 2, seat 중복, revival 복구
  </Card>
  <Card title="Quickstart" href="/quickstart">
    Gen 1 부트부터 첫 supervised worker까지 최단 경로
  </Card>
</CardGroup>

---

## 07. 증거와 수락

> 워커 self-report와 독립 검증 구분, 계약 필드, 리뷰 렌즈, acceptance 판정 규칙과 측정 가능한 증거 형태.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/07-page-7.md
- Generated: 2026-08-07T07:02:57.909Z

### Source Files

- `docs/public/delegation-and-review.md`
- `docs/public/concepts.md`
- `src/master_runtime/core/acceptance/loop.py`
- `src/master_runtime/core/acceptance/verdict.py`
- `src/master_runtime/core/approval/gates.py`
- `master-ops/docs/runbooks/contract-conventions.md`
- `master-ops/docs/charter/04-worker-routing-review.md`

---
title: "증거와 수락"
description: "워커 self-report와 독립 검증 구분, 계약 필드, 리뷰 렌즈, acceptance 판정 규칙과 측정 가능한 증거 형태."
---

수락(acceptance)은 워커 완료 보고와 별개의 마스터 결정이다. 운영 경로에서는 계약·디스패치 게이트·독립 검증이 선행되고, 기계 경로는 `scripts/acceptance-loop`와 `src/master_runtime/core/acceptance/`가 casebook 대비 pass count를 순수 비교한다. 워커 self-report는 증거가 아니다.

## Self-report와 독립 검증

마스터는 위임 산출을 신뢰하지 않는다. 채터 규칙(`master-ops/docs/charter/03-execution-principles.md`)과 공개 가이드(`docs/public/delegation-and-review.md`)가 같은 경계선을 고정한다.

| 구분 | 의미 | 수락 기여 |
| --- | --- | --- |
| Worker self-report | "완료", "통과", "READY" 등 워커가 쓴 주장 | 없음 — 조사 시작 신호만 됨 |
| Independent verification | 마스터가 재실행·재검사한 결과 | 있음 — 수락 전제 |
| Configured | 파일·훅·스크립트·정적 계약이 존재 | 배선 증거 아님 |
| Observed | git·로컬 실행·로그·ledger·프로브가 self-report 밖에서 확인 | 운영 증거 |

공개 문서의 증거 라벨(`Configured` / `Intended` / `Observed` / `Unknown`)은 "파일이 있다"를 "동작한다"로 승격하지 않는다. 통과한 테스트 스위트는 **그 유닛의 로컬 실행 증거**일 뿐, 워크스페이스가 실경로에서 그 유닛을 강제한다는 증명은 아니다.

### 측정 가능한 증거 형태

마스터가 직접 검사할 수 있는 것만 증거다.

| 형태 | 예 | 비고 |
| --- | --- | --- |
| Diff / 변경 표면 | `git diff`, 워커가 선언한 `surfaces` | 계약 허용 표면과 대조 |
| 테스트·게이트 실행 | pytest 전체 스위트, redaction scan exit | **숫자 + 명령** 쌍으로 기록 |
| 로그·프로브 | dispatch ledger, probe stdout, model probe | 파이프 exit가 아닌 명령 자체 exit |
| 생성 파일 | 리포트, 산출 아티팩트 | 경로·해시 또는 내용 대조 |
| 권위 문서 | 승인된 스펙, 핸드오프, Role State | 채팅 기억보다 우선 |
| 배치 판정 | `in_expected_worktree` / `is_master_checkout` / `branch` | 절대 경로는 하향만, 상향은 boolean |

<Warning>
표면(surface)이 초록이어도 사물(the thing)이 아닐 수 있다. 파이프의 exit, 잘못된 Run의 mailbox, 디스패치 기록의 주입 플래그는 각각 "보고"이지 본체가 아니다. 초록 신호를 믿기 전에 **무엇을 읽었는지**를 먼저 확인한다.
</Warning>

검증 절(`contract-conventions` §8) 규칙:

- 형용사 금지 — `"Gates pass"` 대신 `"448 passed, redaction scan OK, inventory exit 1 with baseline 445"`.
- 카운트 옆에 **실행한 명령**을 붙인다. 명령 없는 숫자는 검증 불가.
- empty check conclusion은 pending이지 pass가 아니다.
- 파이프를 끼우면 exit는 파이프 것일 수 있다. 명령 자체의 exit를 읽는다.

## 계약 필드 (worker contract)

계약은 워커의 전체 세계다. 침묵한 자리에서 워커는 합리적이지만 마스터와 다른 선택을 한다. 재사용 절은 `master-ops/docs/runbooks/contract-conventions.md`에 있으며, 가능하면 **복붙**하고 의역하지 않는다.

공개 가이드가 요구하는 관찰 가능 축:

| 필드 | 역할 |
| --- | --- |
| target repository / checkout | 어느 트리에서 작업하는지 |
| allowed work surface | 편집·조회 허용 범위 |
| acceptance criteria | 마스터가 재현할 통과 조건 |
| required evidence | 어떤 측정 산출을 제출할지 |
| commit / push / branch rules | 커밋·푸시·브랜치 권한 (추론 금지) |
| known exclusions / forbidden edits | 금지 표면과 제외 |

### 필수에 가까운 운영 절

<AccordionGroup>
  <Accordion title="Workspace / placement">
    계약 **하향**에는 절대 경로를 명시한다. 워커 **상향 보고**는 경로 대신 판정만:

    ```text
    FIRST ACTION (report these three, and no absolute path):
    - in_expected_worktree: yes|no
    - is_master_checkout:   yes|no   # yes stops immediately
    - branch:               `git branch --show-current`
    ```

    `is_master_checkout: yes`면 즉시 중단. 마스터 체크아웃 오염을 막기 위한 비대칭(경로는 아래, 판정은 위)이다.
  </Accordion>
  <Accordion title="Commit 권한">
    계약에 없으면 보수적으로 uncommitted 상태로 보고한다. 예:

    ```text
    Local commit allowed. Push forbidden. Commit only files changed for this job.
    Evidence file stays uncommitted.
    ```
  </Accordion>
  <Accordion title="Verification / merge stewardship">
    워커가 PR을 열면 merge-ready까지 보유한다. 보고 시점 네 측정:

    1. 모든 체크 non-empty conclusion
    2. unresolved review thread 0 (스레드별 개별 회신)
    3. `origin/main` fetch·merge, conflict 없음
    4. merge 후 게이트 재실행 + 카운트

    `READY` 또는 정확한 블로커. **머지 자체는 마스터**.
  </Accordion>
  <Accordion title="Takeover / Writing / redaction">
    - takeover는 **새 계약 파일** — `dispatch-gate`는 contract hash ledger로 최근 동일 해시를 중복으로 거절할 수 있다.
    - Writing 블록(conventional commits, PR 템플릿 섹션명, 절대 경로·username 금지)은 계약에 그대로 싣는다.
    - 공개 forge 표면에는 absolute path / identity string 금지; 인용은 `~/path`, `<home>` 형태.
  </Accordion>
</AccordionGroup>

계약 해석이 갈리면 부분 수락하지 않는다. 마스터가 리스를 수정하고, 개정 이유를 기록한 뒤 redispatch하거나 변경을 요청한다. 워커 완료와 수락은 분리된 상태로 남긴다.

## 디스패치 경계와 수락 순서

```text
check -> dispatch -> register -> independent verification -> acceptance
```

| 단계 | 역할 | 증거 |
| --- | --- | --- |
| `check` | 계약 읽기, ledger에 허가 기록 | dispatch ledger 결정 |
| `dispatch` | 런타임에 워커 기동 (게이트가 래핑하지 않음) | 호스트/세션 배치 |
| `register` | job id가 기대 아티팩트에 있는지 **probe** | probe exit 0 + stdout에 id |
| independent verification | 계약 기준 재검사 | 마스터 실행 결과 |
| acceptance | 마스터 판정 | 수락 리포트 / 게이트 결정 |

`register`의 probe는 예시처럼 `grep <job-id> ./worker.log`처럼 exit 0과 id 출력을 동시에 만족해야 한다. 경고 부재를 허가로 읽지 않는다.

## 리뷰 렌즈

비중대한 머지·직접 shared-state 변경의 기본은 **세 렌즈 분할**이다 (charter §4, `delegation-and-review`).

| 렌즈 | 질문 |
| --- | --- |
| general correctness | 결과가 동작하는가 |
| regression disproof | 기존 동작이 깨지지 않았음을 반증할 수 있는가 |
| contract and scope | 워커가 계약을 지켰는가 |

규칙:

- **다수 판결**을 쓰되, 소수 **P1 `FIX_FIRST`** 는 처리하거나 증거와 함께 명시 기각한다.
- 인기 투표가 아니다. contract/regression 렌즈의 blocking 이슈는 다른 렌즈가 긍정적이어도 수락 전에 처리한다.
- 렌즈 분리는 **운영 규율**이다. 현재 코드에 별도 합의 엔진은 없다 (`concepts.md` Steering: Intended).

PR review-bot 스레드는 라운드마다 오너 지시 없이 워커가 처리하고, 마스터가 검증·기각만 판정한다. bot finding은 코드 대비 재측정한 뒤 행동한다. 읽지 않고 resolve한 스레드는 리뷰가 아니다.

머지 직전 재측정: `review_measured_at` 이후 `submittedAt`이 더 늦은 bot review가 있으면 전량 읽고 나서 머지한다. 깨끗한 워커 보고는 이후 bot review가 생기는 순간 소모된다.

## Approval 게이트 (steering)

`src/master_runtime/core/approval/gates.py`와 `registry.py`는 **Proposal → Approval → Execution** 을 강제한다.

### GateClass

| 클래스 | 조건 |
| --- | --- |
| `G0_READ_ONLY` | 읽기만 |
| `G1_REVERSIBLE_LOCAL` | 로컬 가역 쓰기 |
| `G2_SHARED_STATE` | 공유 상태 쓰기 |
| `G3_IRREVERSIBLE` | 비가역 |

`classify(ActionSpec)`는 가장 엄격한 게이트를 고른다. `read_only`이면서 write/irreversible이면 `ValueError`.

### ProposalRegistry

- `propose` → `proposal-N` id
- `decision(verdict, authority)` — 한 번만 (`AlreadyDecided`)
- `guard(action, proposal_id)` — G0 제외 시 승인된 동일 `ActionSpec` 필요; 성공 시 `CONSUMED`
- G2/G3 **승인**은 `ApprovalAuthority.HUMAN` 필수 (`POLICY` 불가)

이 레지스트리는 수락 루프와 직교한다: 위험 행동 실행 권한과, 산출물 품질 수락은 다른 축이다.

## 기계적 acceptance loop

Deterministic loop: `scripts/acceptance-loop` → `run_acceptance_loop` / `decide`. **모델 판단·휴리스틱 점수·자연어 근거는 판정에 참여하지 않는다.** 비교 단위는 gated split의 **combined pass count** 뿐이다.

### Case split

| Split | 별칭 | 가시성 | 역할 |
| --- | --- | --- | --- |
| `train` | `visible` | proposer 가시 | 수정 대상 실패 목록 |
| `holdout` | `private` | 비공개 | overfitting 방지 게이트 |
| `scorecard` | `acceptance` | 비공개 메타 | 게이트 외 참고 점수 |

- `GATED_SPLITS = (train, holdout)` — `combined_passed` / `decide` 비교 대상
- 가시성 단일 술어: `is_visible_split` (`VISIBLE_SPLITS = {train}`)
- casebook validation: 최소 1 case, train·holdout 각각 1+, **seed strata 집합 동일**, `case_id` 중복 금지
- 누락 case 결과는 fail-closed (`MISSING_RESULT_DETAIL`) — evaluator skip으로 pass count 상승 불가
- split/stratum 소유권은 **casebook**, evaluator 아님

### 판정 규칙 (`decide`)

| `AcceptanceReason` | 조건 | `accepted` |
| --- | --- | --- |
| `PASS_COUNT_INCREASED` | `candidate_combined > current_combined` 이고 surface 변경 | `true` |
| `NO_PASS_COUNT_INCREASE` | 변경했으나 combined pass 증가 없음 | `false` |
| `NO_CANDIDATE_CHANGE` | `surfaces` 비어 변경 없음 (`candidate.changed == false`) | `false` |

동일 pass면 거부. "아무 것도 안 바꿨다"도 `decision.json`에 남겨 감사 추적을 유지한다.

### 루프 동작 요약

1. baseline 평가 → 현재 scorecard
2. 완전 통과(`is_complete`)면 중단
3. proposer workspace에 **visible failures만** 기록 (`visible_failures.json`, `casebook_visible.json`, `task.md`)
4. proposer가 `candidate.json` (`surfaces`, `summary`) 작성; 없으면 iteration 종료
5. 변경 시 gated 재평가 → `decide` → 수락 시 current 교체, 거부 시 `on_reject` restore
6. in-place mutator + `max_iterations >= 2` 이면 restore hook 필수 (`--restore-cmd`)
7. `report.json` / `report.md` 기록

### 산출물 레이아웃

```text
<run_dir>/
  manifest.json
  split.json
  split.md
  report.json
  report.md
  history/
    visible/          # proposer에 복사 가능
      iterations/NNN/decision.json|md
      iterations/NNN/proposer_workspace/
      train/<label>/result.json
    private/          # proposer 금지
      holdout/<label>/result.json
```

쓰기 경로는 원자적 replace (`write_json` / `write_text`)라 중단된 run도 잘린 JSON을 사실로 읽히지 않게 한다.

### CLI

| 하위 명령 | 입력 | exit |
| --- | --- | --- |
| `validate --config` | casebook 로드 검증, 카운트 JSON | 0 / 설정 오류 2 |
| `split --config [--output-dir]` | split manifest 기록 | 0 |
| `run --config [--max-iterations] [--baseline-ref] [--restore-cmd]` | 루프 실행, Markdown 리포트 stdout | 완전 통과 0, 미완전 1, 설정/가드 2 |
| `inspect --run-dir` | `report.json` 출력 | 없음 2 |

설정 JSON 핵심 키: `name`, `workspace_root`, `run_dir`, `max_iterations`(기본 3), `proposer.runtime` / `model` / `timeout_seconds`, `cases[]`, optional `regression_log`.

케이스 필드:

```json
{
  "case_id": "t1",
  "split": "train",
  "stratum": "unit",
  "command": ["pytest", "-q"]
}
```

`command` 없는 케이스는 `command_evaluator`에서 fail (`case has no command`).

<RequestExample>
```bash
scripts/acceptance-loop validate --config ./acceptance.json
scripts/acceptance-loop run \
  --config ./acceptance.json \
  --max-iterations 3 \
  --restore-cmd 'git checkout -- .'
scripts/acceptance-loop inspect --run-dir ./runs/example
```
</RequestExample>

## 운영 수락 체크리스트

마스터 수동 수락(일반 디스패치)과 기계 루프를 같은 회의 테이블에 둔다.

| 단계 | 수동 경로 | 기계 경로 |
| --- | --- | --- |
| 계약 연결 | 리스·contract hash·job id | casebook + baseline ref |
| 변경 표면 | diff vs allowed surface | `candidate.surfaces` |
| 필수 검사 재실행 | 계약 named commands, 카운트 | gated case commands |
| 범위/회귀 | 3-lens review | train+holdout pass delta |
| 잔여 위험 | 미실행 검사·열린 스레드 명시 | incomplete scorecard |
| 사후 | `worker-reap`, ledger | `report.json` 보존 |

수락 리포트는 **통과한 것**, **돌리지 않은 것**, **남은 위험**을 적는다. 검증 불가면 아직 미수락이다.

## 실패 모드

| 증상 | 원인 | 대응 |
| --- | --- | --- |
| 워커 "pytest 2 passed" | 전체 스위트 대신 부분 파일 | 계약에 명령·기대 카운트 명시, 마스터 재실행 |
| holdout 누락 config | casebook invalid | `validate` exit 2, strata 정합 |
| 반복 거부 후 다음 후보가 더 나쁨 | in-place 미복원 | `--restore-cmd` 또는 `max_iterations=1` |
| `NO_CANDIDATE_CHANGE` | empty `surfaces` | proposer가 `candidate.json` 작성 규칙 준수 |
| register 실패 | probe에 job id 없음 | 아티팩트·probe-cmd 수정, 미등록 디스패치를 사후 인정하지 않음 |
| 머지 후 bot 재발견 | stale worker report | merge-time re-measure (`review_measured_at`) |
| placement 오염 | 모호한 worktree 절 | 절대 경로 쌍 + boolean 보고 절 |

## 관련 런타임 유닛

| Unit | 관련 |
| --- | --- |
| U5 Worker Scheduler | `dispatch_gate` check/register, 계약 해시 ledger |
| U6 Approval Manager | `approval/gates.py`, `approval/registry.py` |
| U11 Observability | `acceptance/` 점수·리포트, digest/watchdog 부분 |

## Next

<CardGroup>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    check → dispatch → register, 계약 해시·ledger, model probe
  </Card>
  <Card title="acceptance-loop 레퍼런스" href="/acceptance-loop-reference">
    validate·split·run·inspect, holdout, max-iterations, 산출물
  </Card>
  <Card title="Worker reap" href="/worker-reap">
    수락 후 lease·terminal·worktree 정리
  </Card>
  <Card title="방어 인벤토리" href="/defense-inventory">
    디스패치·프로브·placement·redaction 가드 표
  </Card>
</CardGroup>

---

## 08. 프로그레시브 온보딩

> ONBOARDING 라우터, 단계 파일 1회 1로드·Verify 후 진행, Stage 1/2, founding spawn, 템플릿 치환 경계.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/08-page-8.md
- Generated: 2026-08-07T07:02:39.548Z

### Source Files

- `master-ops/ONBOARDING.md`
- `master-ops/onboarding/00-orientation.md`
- `master-ops/onboarding/01-preflight.md`
- `master-ops/onboarding/09-spawn.md`
- `master-ops/onboarding/10-card-and-retire.md`
- `scripts/onboarding-preflight.sh`
- `scripts/generate-manifest`
- `tests/test_onboarding_structure.py`

---
title: "프로그레시브 온보딩"
description: "ONBOARDING 라우터, 단계 파일 1회 1로드·Verify 후 진행, Stage 1/2, founding spawn, 템플릿 치환 경계."
---

프로그레시브 온보딩은 `master-ops/ONBOARDING.md` 라우터와 `master-ops/onboarding/` 단계 파일, `scripts/onboarding-preflight.sh` 게이트, `scripts/generate-manifest` / `MANIFEST.json` 설치 목록, founding spawn 경로(`scripts/dispatch-gate` · `scripts/master-succeed spawn` · Orca orchestration)로 구성된다. 라우터만 전체 설치 동안 유지 로드되고, 단계 파일은 **한 턴에 하나**만 읽으며 현재 단계의 **Verify**가 통과하기 전에는 다음 파일을 열지 않는다.

## 구현 표면

| 경로 | 역할 |
| --- | --- |
| `master-ops/ONBOARDING.md` | 세션 모드 분류, 에이전트 로드 규칙, 플레이스홀더 allowlist, 단계 인덱스 |
| `master-ops/onboarding/00`–`10-*.md` | Founding 경로 단계 (Owner script + Verify; 일부에 If fail) |
| `master-ops/onboarding/reverify.md` | 이미 설립된 워크스페이스 헬스 체크; **spawn 금지** |
| `master-ops/onboarding/upgrade.md` | 템플릿 레이어 드리프트 감지·적용; **spawn 금지** |
| `scripts/onboarding-preflight.sh` | Step 0 호스트 측정; exit 0 ready / 1 blocked |
| `scripts/generate-manifest` | Stage 1 스켈레톤 → `master-ops/MANIFEST.json` 생성; 설치 제외 목록 SSOT |
| `master-ops/MANIFEST.json` | 설치 파일 목록 + `template_version` (현재 스탬프 `v0.4.4`) |
| `master-ops/scripts/template-check` · `template-apply` | Upgrade/Reverify 템플릿 비교·적용 |
| `config/instance-runtime.example.json` · `config/model-tier-policy.example.json` | 인스턴스 설정 예제; 채워진 복사본은 커밋하지 않음 |
| `tests/test_onboarding_structure.py` | 인덱스·next 포인터·Verify/Owner script·플레이스홀더 allowlist 고정 |

```text
[installer session / Herald]
        |
        v
 master-ops/ONBOARDING.md  (router, stays loaded)
        |
   mode? ---- Founding ----> 00..10 one file per turn
        |                      |
        |                      +-- preflight (scripts/onboarding-preflight.sh)
        |                      +-- ops repo from MANIFEST.json (Stage 1 skeleton)
        |                      +-- founding spawn (dispatch-gate + master-succeed + orchestration)
        |                      +-- master closes installer terminal
        |
        +---- Reverify ----> reverify.md only (no spawn)
        +---- Upgrade  ----> upgrade.md only (template layer; no spawn)
        +---- Template improve -> stop onboarding; ordinary task
```

## 세션 모드 (라우터 첫 질문)

온보딩·orientation·측정 **이전**에 모드를 분류한다. 모드를 섞지 않는다. 증거가 선택과 모순되면 중단하고 증거와 함께 다시 묻는다.

| 모드 | 경로 | spawn | 요약 |
| --- | --- | --- | --- |
| **Founding** | `00` → `10` | 예 (Step 8–9) | 신규 워크스페이스: ops 저장소 구축 + Generation 1 마스터 |
| **Reverify** | `reverify.md` 단독 | **차단** | 이미 ops + 마스터가 있는 환경 헬스만 |
| **Upgrade** | `upgrade.md` 단독 | **차단** | 설립된 ops가 템플릿보다 뒤처짐; 템플릿 레이어만, 소유자 확인 후 적용 |
| **Template improve** | 온보딩 중단 | — | 이 오케스트레이터 저장소 자체 작업; 일반 작업으로 라우트 |

<Warning>
Founding 가드는 **라이브 마스터 유무만**이 아니라 **기존 ops 저장소 또는 lineage 파일**에도 걸린다. 마스터가 죽었거나 설치가 반쯤 끝난 상태는 Founding이 아니다. 트리가 있고 템플릿만 올려야 하면 **Upgrade**로 보내고, 죽은/미완 Gen-1 또는 살아있는 마스터 승계는 ops의 `docs/runbooks/succession-boot-card.md`가 소유한다. Founding을 다시 돌리면 lineage·거버넌스 기록을 오염시킨다.
</Warning>

## 프로그레시브 로드 규칙

라우터의 에이전트 규칙 (모든 호스트·모델 강제):

1. **한 턴에 단계 파일 하나** — 현재 단계 파일만. 전부 한 번에 읽지 않는다.
2. **Verify 통과 전 다음 파일 금지** — 다음 파일은 Verify 리스트가 통과한 뒤에만.
3. **Owner script** — 각 번호 단계의 소유자 대면 오프닝 템플릿. 친절·비급함 ELI5; 명령 블록·차터 전문을 소유자에게 덤프하지 않는다.
4. **If fail** — 단계에 있으면 디스크에서 다시 읽고 따른다. 없으면 즉흥 복구 금지; 중단하고 묻는다.
5. **질문** — 측정 후보가 있으면 번호 옵션 + 추천 1개(이유) + free-form. 측정 가능한데 “값만 달라”고 하지 않는다. **예외:** 워크스페이스 루트는 소유자가 선택·붙여넣기; 디스크 스캔·순위 단축 목록 금지 (`02-workspace-facts.md`).
6. **모델 강도 예외 없음** — 강한 모델도 모놀리스 읽기 권한 없음. 요약이 잘 되어도 단계 파일 스킵 권한 없음. 2026-08-03: 모놀리스 과부하·단계 생략 즉흥이 관측됨.

`tests/test_onboarding_structure.py`는 인덱스↔디스크 일치, 번호 단계 next 포인터 순서, 마지막 단계에 next 없음, 번호 단계의 `Verify`/`Owner script`, `reverify`/`upgrade`의 `Checklist`/`Report`, 플레이스홀더 allowlist, founding kill-switch 문구를 고정한다. **에이전트 읽기 순서 자체는 런타임이 강제하지 않는다** — 방어 인벤토리의 progressive-load 가드와 동일.

### 소유자 대면 언어 (라우터 standing rules)

| 기술 라벨 (에이전트/파일) | 소유자 대면 표현 |
| --- | --- |
| probe / 탐침 | 임시 터미널 또는 seat-check 터미널 |
| placement | 마스터가 Orca에서 앉는 자리 |
| selector | 기록하는 내구성 seat id |
| dispatch (첫 사용) | 워커 세션에 일을 넘김 |
| Role Lock (첫 사용) | 활성 역할 하나; 다른 역할은 소유자 잠금 해제까지 동결 |

기술 값·경로·id·명령·모델 이름·이슈 id·파일 이름은 그대로 둔다. 한 턴에 질문 도구 화면이 3개 결정을 넘으면 턴을 나눈다.

## Stage 1 / Stage 2

| 레이어 | 의미 | 위치 |
| --- | --- | --- |
| **Stage 1 skeleton** | 설치되는 ops 스켈레톤 (차터, 런북, 스크립트, `workspace-card/` 등) | `master-ops/` 중 `MANIFEST.json` `files` 목록 |
| **Stage 2 온보딩** | 스켈레톤을 동작하는 워크스페이스/오케스트레이터 ops로 만드는 설치 흐름 | 라우터 + `onboarding/` |

`scripts/generate-manifest`가 설치 제외 목록의 실행 SSOT다. 제외 예:

- **파일:** `TEMPLATE-VERSION`, `CHANGELOG.md`, `ONBOARDING.md`, …
- **디렉터리:** `onboarding/`, `docs/lineage/`, `.beads/`, 캐시 디렉터리 등

생성된 ops에는 `TEMPLATE-VERSION` · `CHANGELOG.md` · `ONBOARDING.md` · `onboarding/`이 **없어야** 한다. `MANIFEST.json` 자체는 설치되며 `template_version` 스탬프를 담는다. 프레임 위생: 설치 파일에 ADE 프레임 경로 `master-ops/` 또는 authoring 이름 `mogui-master-ops` 누출 금지 (`docs/blame/` 예외).

## Founding 단계 인덱스

시작 경로는 `00-orientation.md` → `01-preflight.md`만. 이후 파일은 해당 단계 시작 시까지 연기.

| # | 파일 | 단계 | 결정 / 산출 |
| --- | --- | --- | --- |
| 00 | `00-orientation.md` | Orientation | 시스템·3계층·종료 상태 전달 |
| 01 | `01-preflight.md` | Step 0 | preflight 통과; agent CLI 확정 |
| 02 | `02-workspace-facts.md` | Step 1 | 목적, root, 인벤토리, 이름 / monitor ns / model |
| 03 | `03-ops-repo.md` | Steps 2–3 | ops 저장소 선택·생성 (MANIFEST 복사) |
| 04 | `04-seat.md` | Step 3.5 | ops Orca 등록; durable workspace seat |
| 05 | `05-placeholders.md` | Step 4 | 플레이스홀더 치환; 세션 카드 root 배포 |
| 06 | `06-tracker.md` | Step 5 | 워크스페이스 root에서 tracker 해석 |
| 07 | `07-user-rules.md` | Step 6 | 소유자 규칙·마스터 callsign |
| 08 | `08-settings-and-skills.md` | Steps 7–7.6 | 훅·스킬 스택·publish-gate 범위 (기본 on) |
| 09 | `09-spawn.md` | Steps 8–9 | Gen-1 orchestration spawn + boot smoke |
| 10 | `10-card-and-retire.md` | Step 10 | operating card; 마스터가 installer 종료 |

### Orientation (00)

설치 세션(Herald)만 묶는 Orca Context Charter: 스냅샷 인덱스 우선, 최소 페이지 로드, 프로바이더 가정 금지. 3계층:

1. **Orchestrator** — 이 저장소 (런타임·템플릿·온보딩)
2. **Ops** — 생성되는 운영 저장소 (거버넌스)
3. **Session** — Orca 호스트 마스터 1개

종료 상태: 채워진 ops + 워크스페이스 root에서 닿는 tracker + 소유자 규칙 + **검증된 Generation 1 마스터 정확히 하나**.

### Preflight (01 / Step 0)

```console
$ cd "{{RUNTIME_ROOT}}"
$ ORCA_AGENT_CLI="<agent CLI>" bash scripts/onboarding-preflight.sh
```

`--fix`는 승인 후에만; 글로벌 Orca 스킬 추가/갱신 가능, 앱 설치는 수동.

| 결과 | 의미 |
| --- | --- |
| `READY` | 필수 체크 전부 통과, exit 0 |
| `READY WITH WAIVERS` | `PREFLIGHT_WAIVE`로 강등된 필수 있음 (만족이 아님), exit 0 |
| `BLOCKED` | FAIL 존재, exit 1 — founding 진행 금지 |

주요 라벨 (스크립트 기준):

| 라벨 | 대략 심각도 | 메모 |
| --- | --- | --- |
| `orca` | FAIL | `status --json` `ok:true`; basename `orca` / `orca-dev` / `orca-ide` |
| `orchestration` | FAIL | non-legacy Run이 이 터미널에 바인딩. legacy_read_only / run null / legacy=1 거부 |
| `skills` | FAIL | `orca-cli` + `orchestration` 아티팩트 또는 글로벌 목록 |
| `agent-cli` | FAIL | `ORCA_AGENT_CLI` 설정 + PATH |
| `worker-runtime` | FAIL/WARN | `codex` 또는 `cursor-agent` 최소 1개; 하나만 있으면 나머지는 WARN |
| `bd` · `python3` · `git` · `gh` | FAIL | 바이너리 필수 |
| `gitleaks` · `ctx` | WARN | 발행/히스토리 실무에 필요; 마스터 기동 자체는 비차단 |
| `gh-auth` | WARN | 미인증 또는 `workflow` 스코프 없음 |
| `redaction-extra` | FAIL | 조직 규칙 파일 사용 가능해야 함 |

통과 후 인스턴스 파일 (커밋 금지):

- `config/instance-runtime.json` — `master_host_runtime` = 확정 agent CLI; 해석 순서 env → 파일 → unconfigured
- `config/model-tier-policy.json` — `version: 2`; agent 인벤토리 동의 후 측정 또는 소유자 지정 목록; 게이트 해석: `DISPATCH_TIER_POLICY` → 인스턴스 파일 → `master-ops/model-tier-policy.json`

### Workspace facts → seat (02–04)

- **Root:** 소유자 붙여넣기 absolute path만 검증. 스캔·단축 목록 금지.
- **Inventory:** root 직하 Git 자식 측정 후 기본 전부 `{{REPO_LIST}}`; 외부 레인은 명시 opt-in.
- **Ops 생성:** `MANIFEST.json` 경로만 복사. 기존 ops면 Founding 중단 → Upgrade 또는 succession-boot-card.
- **Seat:** 워크스페이스 레벨 folder seat (`id:folder:<uuid>`). bare `folder:` / `path:` 는 소비자 비대칭·placement 거부. seat-check 임시 터미널 측정 후 **반드시 닫아** empty-seat 확보.

### 플레이스홀더 (05) 및 allowlist

허용 토큰만 (라우터 + structure 테스트):

`{{WORKSPACE_NAME}}` · `{{WORKSPACE_ROOT}}` · `{{OPS_REPO}}` · `{{MONITOR_NS}}` · `{{MODEL_ID}}` · `{{REPO_LIST}}` · `{{RUNTIME_ROOT}}` · `{{TEMPLATE_VERSION}}`

- `{{RUNTIME_ROOT}}` · `{{TEMPLATE_VERSION}}`은 측정; 사용자에게 묻지 않음.
- ops 아래 **남은 `{{...}}` 없음** (deferral 없음; 삭제해서 검사 통과 금지).
- `workspace-card/` 채운 뒤 root `CLAUDE.md` / `AGENTS.md`에 배포; ops root 쌍과 workspace-card 쌍은 **교차 비교하지 않음**.

### Tracker · rules · settings (06–08)

- Beads: `bd init` (승인 후), root → ops `.beads` 링크, `bd where`가 ops를 가리킴.
- 사용자 규칙·callsign 시드.
- 훅/스킬 **기본 on** (opt-out 메뉴 없음). `product-path-guard`는 `product_repo` 설정 후에 의미 있음; Bash fail-closed는 `MOGUI_PRODUCT_GUARD_FAIL_CLOSED=1` opt-in.

## Founding spawn (09) 과 installer 종료 (10)

Supervised dispatch = **Orca orchestration only**. 파일시스템 path selector 재시도, installer 안 마스터 부팅, 두 번째 세션 생성 금지.

### Step 8 게이트

1. `ORCA terminal list --worktree <selector> --json` → seat **터미널 0개**
2. `dispatch-gate check` → `allow: true` (`--completion-channel orchestration`)
3. `orchestration run-create` / `task-create`
4. `master-succeed spawn` — placement `MATCH` 또는 유효 `MATCH_REISSUED` (`handle_reissued: true`)
5. Dispatch attach + `dispatch-gate register` (동일 Task ID)
6. `orchestration check --wait` → `worker_done` / escalation / question

Kickoff 파일 (바이트 동일 전달): Gen-1, Herald 기원, callsign, 부트 시퀀스, **warm resume note**, **installer retirement switch** (`ORCA terminal close --terminal <installer handle> --json`). 측정 불가 필드는 `unavailable`; 발명 금지. Codex 워커 전 `scripts/codex-worker-pretrust`.

### Step 9 (새 마스터 세션 내부)

Role State + Role Lock, 모델 measured/unavailable/unsupported, placement 3세트, lineage Gen-1 append, `worker_done` 1회. Installer가 대신 부팅하지 않음.

### Step 10

1. 대화 검증 (파일만 있고 답 없으면 추측 설치)
2. Operating card 전문 인쇄 (플레이스홀더 치환; declined 라인 명시 또는 `none`)
3. 마스터가 identity recheck 후 installer 터미널 close + process/Orca terminal/tty 소멸 검증
4. 마스터 터미널은 유지

Installer가 스스로 닫지 않는다. “please close this installer terminal” 소유자 요청 문구는 테스트로 금지.

## Reverify / Upgrade

### Reverify

읽기 전용 (예외: 분실 operating card 재인쇄만 `10-card-and-retire.md` read-only 오픈). 체크: seat 1 마스터, tracker, role-state↔lineage, placeholders 없음, workspace-card `cmp`, `template-check` **보고만**. Spawn 금지.

### Upgrade

```console
"{{RUNTIME_ROOT}}/master-ops/scripts/template-check" --ops "{{OPS_REPO}}" --template "{{RUNTIME_ROOT}}/master-ops"
"{{RUNTIME_ROOT}}/master-ops/scripts/template-apply" --ops "{{OPS_REPO}}" --template "{{RUNTIME_ROOT}}/master-ops"
# 확인 후 --write + 확인 문구 + --placeholder KEY=VALUE ...
```

**이름 거부 (instance-owned):** `docs/lineage/`, `docs/runbooks/role-state.md`, `.beads/`, `config/`, `contracts/`, manifest 미포함 경로. Dry-run `planned` / write 후 recheck `template-compare`. Spawn 금지.

## 검증 신호

| 신호 | 기대 |
| --- | --- |
| Preflight summary | `READY` 또는 의도된 `READY WITH WAIVERS`; `BLOCKED`면 stop |
| Ops 생성 후 | 제외 디렉터리/파일 부재; `MANIFEST.json` `template_version` 일치 |
| Placeholder scan | `rg '\{\{[^}]+\}\}' "{{OPS_REPO}}"` 무히트 |
| Seat | durable `id:` selector; spawn 직전 empty seat |
| Spawn | placement MATCH/MATCH_REISSUED; 마스터 1; kickoff 바이트 일치; `worker_done` |
| Step 10 | card 인쇄; 마스터가 installer close 검증; 마스터 세션 생존 |
| Structure tests | `pytest tests/test_onboarding_structure.py` 등 |

## 트러블슈팅

| 증상 | 조치 |
| --- | --- |
| Preflight `BLOCKED` | `FAIL` 라인 수정. 합법적 예외만 `PREFLIGHT_WAIVE=<label>` (요약에 명시; 오타는 미매칭 NOTE) |
| `legacy_read_only` / unbound Run | `orca orchestration run-create` 후 preflight 재실행; 앱 재시작만으로 legacy 제거 안 됨 |
| Founding 가드 (ops 이미 존재) | Upgrade 또는 succession-boot-card; Founding 재실행 금지 |
| Seat에 기존 터미널 | 닫고 empty 재측정; 재진입 세션이 이전 spawn 실패를 가정하지 않음 |
| `SPAWN_PLACEMENT_MISMATCH` | path selector 금지; seat 단계 selector 재측정; 새 세션 |
| Residual `{{...}}` | 값 채우기 또는 중단; deferral/삭제 금지 |
| Installer close 실패 | 다른 터미널 추측 금지; 소유자에게 보고; idle |
| Template improve 혼동 | 온보딩 중단; 이 저장소 작업을 일반 작업으로 처리 |

## 관련 제약 (요약)

- Orca 필수. non-Orca 폴백 제안 금지.
- 네트워크·API 키를 이 흐름이 요구하지 않음; 프로바이더 중립 (로컬 측정·파일·셸).
- 채워진 `instance-runtime.json` / `model-tier-policy.json`은 인스턴스 소유, 예제만 템플릿에 커밋.
- Progressive load 가드: 라우터 규칙 + structure 테스트 인벤토리; 에이전트 순서는 정책 준수에 의존.

## Next

<CardGroup>
  <Card title="Quickstart" href="/quickstart">
    클론 → Orca 준비 → wake-up 온보딩 → Gen-1 → 첫 supervised worker 최단 경로.
  </Card>
  <Card title="Installation" href="/installation">
    전제조건, preflight FAIL/WARN, 셸 명령 등록, 측정 신호.
  </Card>
  <Card title="마스터 라이프사이클" href="/master-lifecycle">
    founding spawn 이후 steady state · succession · lineage.
  </Card>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    check → dispatch → register, 계약 해시, model probe, pretrust.
  </Card>
  <Card title="Clean succession" href="/succession">
    handoff, placement spawn, retire, revival — Founding 재실행 대체 경로.
  </Card>
  <Card title="방어 인벤토리" href="/defense-inventory">
    empty-seat, placement, progressive onboarding load 가드 표.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    BLOCKED, placement mismatch, seat 중복, revival.
  </Card>
  <Card title="인스턴스 설정" href="/configure-instance">
    instance-runtime · model-tier-policy · env 우선순위.
  </Card>
</CardGroup>

---

## 09. Supervised dispatch

> check → dispatch → register 흐름, 계약 해시·ledger, model probe, Codex/Cursor pretrust, 완료 채널 orchestration.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/09-supervised-dispatch.md
- Generated: 2026-08-07T07:04:38.243Z

### Source Files

- `docs/public/delegation-and-review.md`
- `scripts/dispatch-gate`
- `src/master_runtime/core/dispatch_gate.py`
- `master-ops/docs/charter/05-dispatch-gate.md`
- `master-ops/scripts/dispatch`
- `scripts/codex-worker-pretrust`
- `scripts/cursor-worker-pretrust`
- `tests/test_dispatch_gate.py`

---
title: "Supervised dispatch"
description: "check → dispatch → register 흐름, 계약 해시·ledger, model probe, Codex/Cursor pretrust, 완료 채널 orchestration."
---

Supervised dispatch는 `scripts/dispatch-gate`와 `master-ops/scripts/dispatch`가 강제하는 **기록된 허가 경계**다. 워커 계약이 게이트를 통과하고(`check`), Orca orchestration으로 작업이 기동되며(`dispatch`), job id·모델·완료 채널이 ledger에 독립 검증된 뒤에야(`register`) 디스패치가 성립한다. 자체 보고(self-report)만으로는 성립하지 않는다.

## 표면 요약

| 표면 | 역할 |
|------|------|
| `scripts/dispatch-gate` | 게이트 CLI: `check` · `register` · `watch` · `report` |
| `src/master_runtime/core/dispatch_gate.py` | 판정·티켓·ledger·티어 정책 코어 |
| `master-ops/scripts/dispatch` | 원샷 파이프라인: gate → task-create → inject → register → delivery |
| `scripts/model-identity-probe` | transcript JSONL에서 assistant 모델 id 실측 |
| `scripts/codex-worker-pretrust` | Codex 계정 `config.toml` project trust 선기입 |
| `scripts/cursor-worker-pretrust` | Cursor `.workspace-trusted` 마커 선기입 |
| `master-ops/model-tier-policy.json` (또는 `config/model-tier-policy.json`) | v2 티어 × fan-out 정책 |

<Note>
`dispatch` 단계는 게이트가 워커를 직접 기동하지 않는다. 마스터(또는 `master-ops/scripts/dispatch`)가 계약이 지정한 런타임/터미널에 워커를 붙인다. 예전에 존재하던 typed adapter 래핑 경로는 제거되었다.
</Note>

## 파이프라인: check → dispatch → register

```text
check  →  (pretrust)  →  Orca task-create / worker inject  →  register  →  delivery 확인
         ticket 발급                                              job 검증
         ledger ALLOW                                            model 검증
```

| 단계 | 누가 | 무엇을 증명하는가 |
|------|------|------------------|
| `check` | `dispatch-gate check` | 계약 가독성, 완료 채널, 모델·티어 정책, 문자 예산, 고비용 런타임 규칙 |
| `dispatch` | Orca orchestration (또는 래퍼) | Task 생성, terminal attach, `dispatch --inject` |
| `register` | `dispatch-gate register` | job id가 아티팩트에 존재, (해당 시) orchestration task 존재, 선언 모델 vs 실측 모델 |
| delivery | `master-ops/scripts/dispatch` | inject 전후 pane 분류 — register 성공 ≠ 스펙 전달 |

### 원샷 래퍼 (`master-ops/scripts/dispatch`)

측정된 운영 경로:

1. top 티어면 `--top-approved "<reason>"` 없으면 즉시 거부 (ledger 행 없음)
2. `check --no-record`로 사전 평가 → `--check-only`면 여기서 종료
3. Codex면 `codex-worker-pretrust` 실행 (`Summary:`에 `skipped` 있으면 fail-closed)
4. 기록형 `check` (ticket 발급 + ledger 행)
5. `orca orchestration task-create --spec …`
6. terminal 재사용 또는 `orca terminal create --worktree …`
7. `orca orchestration dispatch --task … --to … --inject`
8. `register` (`dispatch-show`로 job id 프로브, 가능하면 model probe)
9. pane 분류로 delivery class 판정 (`agent-started` / `hook-trust` / `limit` / `unknown`)

```bash
# 템플릿 자리표시자는 인스턴스 치환 후 사용
G={{RUNTIME_ROOT}}/scripts/dispatch-gate
L=~/.mogui/dispatch-ledger.jsonl

"$G" --ledger "$L" check \
  --runtime codex --model gpt-5.3-codex \
  --contract ./contracts/job.md \
  --agents 1 --est-chars 3000 \
  --completion-channel orchestration

# … Orca task-create / terminal / dispatch --inject …

"$G" --ledger "$L" register \
  --job-id <dispatch-id> \
  --probe-cmd 'orca orchestration dispatch-show --task <task-id> --json | grep -o <dispatch-id> | head -1' \
  --orchestration-task <task-id> \
  --declared-model gpt-5.3-codex \
  --model-probe-cmd '<scoped probe that prints model id>' \
  --contract-sha <sha-from-check> \
  --runtime codex
```

<Warning>
stdout는 JSON 판정, stderr는 사람용 진단이다. `2>&1`로 합치면 JSON 파서가 깨진다. 기계 사용 시 `2>/dev/null`로 stderr를 분리한다.
</Warning>

## `check`

### 필수 입력

<ParamField body="--runtime" type="string" required>
소문자 런타임 id (`^[a-z0-9][a-z0-9_-]{0,31}$`).
</ParamField>

<ParamField body="--contract" type="path" required>
워커 계약 파일. 내용은 SHA-256 `contract_sha`로 해시된다.
</ParamField>

<ParamField body="--agents" type="int" required>
에이전트 수 (`>= 1`). cost proxy = `n_agents * est_input_chars`.
</ParamField>

<ParamField body="--model" type="string" required>
선언 모델 id. 공백-only/미지정은 `NO_MODEL`.
</ParamField>

<ParamField body="--completion-channel" type="orchestration | sentinel-log" required>
완료 채널. 없거나 미허용 값이면 `NO_COMPLETION_CHANNEL`.
</ParamField>

<ParamField body="--est-chars" type="int">
예상 입력 문자. 생략 시 completion channel이 있으면 계약 파일 길이, 없으면 `0`. 측정 불가(`None`)면 `CONTRACT_UNREADABLE`로 fail-closed.
</ParamField>

<ParamField body="--tier-policy" type="path">
정책 파일 명시. 없으면 아래 해석 순서.
</ParamField>

<ParamField body="--tier-override" type="string">
cap/deny 초과 시 한 요청을 통과시키는 사유 문자열.
</ParamField>

<ParamField body="--no-record" type="flag">
ledger append·ticket 발급 없이 평가만. dry-run/`--check-only`용. 기록하면 fan-out 예산을 소모한다.
</ParamField>

### 티어 정책 해석 순서

1. CLI `--tier-policy`
2. 환경 변수 `DISPATCH_TIER_POLICY`
3. 인스턴스 `config/model-tier-policy.json` (온보딩 후 존재 시)
4. 템플릿 `master-ops/model-tier-policy.json`

### v2 정책 동작 (현재 템플릿)

| 규칙 | 동작 |
|------|------|
| 모델 → 티어 | `tiers` 목록 casefold 매칭; 미등록은 `unknown` |
| fan-out | 티어별 `fanout_caps` 창(`window_seconds`, 기본 86400초) 누적 agent 수 |
| 키 없음 | 해당 티어 **무제한** (top 포함). 템플릿은 `unknown: 8`만 캡 |
| top 운영 | 게이트 cap이 아니라 `master-ops/scripts/dispatch`가 `--top-approved`를 요구 |
| 창 의미 | 동시성이 아니라 **누적**. 순차 1명×10회 = fan-out 10과 동일 소모 |
| override | `--tier-override`는 한 요청만 통과; 이미 센 창 예산은 환불하지 않음 |

기본 문자 한도: 단일 디스패치 `500_000`, 배치(cost proxy) `1_000_000`. 고비용 런타임 집합 기본값은 `fable` (2+ agents면 `ROUTING_VIOLATION`).

### 성공 시 부수 효과

- JSON `GateDecision` (`allow`, `reason`, `warnings`, `contract_sha`, …)을 stdout
- `record=True`면 JSONL ledger에 ALLOW 행 + `~/.mogui/dispatch-tickets/{runtime}-{sha12}.json` 티켓 발급
- 티켓 TTL 기본 **600초**; 만료 후 grace(기본 24h) 지나면 GC

## `register`

선행 성공 `check` 없이 유효한 registration은 없다. 티켓·pending ledger 행을 매칭하고, 프로브가 job id를 증명한 뒤에만 등록한다.

### 필수/핵심 플래그

| 플래그 | 의미 |
|--------|------|
| `--job-id` | 등록할 dispatch/job id |
| `--probe-cmd` | shell 명령: **exit 0** 이고 **stdout에 job id 문자열** 포함 |
| `--contract-sha` | check 산출 sha (최소 12 hex; 접두 매칭). 다중 후보 시 `AMBIGUOUS_TICKET` |
| `--runtime` | 티켓/pending 필터 |
| `--orchestration-task` | 완료 채널이 `orchestration`이면 **필수** |
| `--declared-model` | check 때 선언한 모델 |
| `--model-probe-cmd` | 워커가 쓴 아티팩트에서 실측 모델 id를 stdout으로 출력 |
| `--tier-policy` | 실측 티어 escalation 비교용 (check와 동일 해석) |

### 프로브 계약

```text
probe 성공  ⇔  returncode == 0  AND  job_id ∈ stdout
```

<Warning>
파일명만 출력하는 프로브(내용 미독)는 검증 스탬프만 남긴다. `grep &lt;job-id&gt; logfile`, `cat evidence.txt`처럼 **내용**을 stdout에 내도록 한다. 타임아웃: orchestration 프로브·model 프로브 각 30초.
</Warning>

### orchestration 검증

완료 채널이 `sentinel-log`가 아니면:

1. `--orchestration-task` 누락 → `ORCHESTRATION_UNVERIFIED` (`task_omitted`)
2. `orca` 부재 / 타임아웃 / 파싱 실패 / task 미존재 → 동일 deny 계열, ledger에 실패 원인 기록

### 모델 검증 등급

| 상황 | 코드 | 결과 |
|------|------|------|
| 선언·실측 일치 (정책 관점 허용) | — | `model_verified=true` |
| 실측 티어가 선언보다 엄격(더 비싼 쪽으로 승격) | `MODEL_TIER_ESCALATION` | **DENY** |
| 실측 ≠ 선언이지만 escalation 아님 | `MODEL_MISMATCH` | 경고 후 등록 가능 |
| 선언 없음 / 실측 없음 | `MODEL_UNVERIFIED` | 경고 후 등록 |
| 프로브 실패·비0·타임아웃 | `MODEL_PROBE_FAILED` | 경고 후 등록 |

ledger 필드: `model_declared`, `model_measured`, `model_verified`. 미검증 등록과 검증 등록을 구분한다.

## 계약 해시 · ledger · 티켓

```text
contract bytes  --sha256-->  contract_sha (64 hex)
check ALLOW  -->  ledger JSONL row + ticket {runtime, contract_sha, issued_ts, count}
register     -->  ticket 소모(매칭 시 unlink) + registered 행 + attempt 카운트
```

| 저장소 | 기본 경로 | 비고 |
|--------|-----------|------|
| Ledger | `.dispatch-gate-ledger.jsonl` 또는 `DISPATCH_GATE_LEDGER` / `--ledger` | 운영 래퍼는 `~/.mogui/dispatch-ledger.jsonl` 사용 |
| Tickets | `~/.mogui/dispatch-tickets/` | 파일명 `{runtime}-{sha[:12]}.json` |
| Lock | 티켓 디렉터리 `.dispatch-gate.lock` | Unix `fcntl` |

`dispatch-gate report` (옵션 `--today`)는 모델별 디스패치 수·cost proxy, deny reason, 티어 누적, 정책 path+`sha256`, tier override를 집계한다. 스팬에 **정책 행이 둘 이상**이면 단일 정책으로 판단되지 않은 구간이다.

## 완료 채널: orchestration

허용 값: `orchestration` | `sentinel-log`.

규정 준수 디스패치는 **벤더 중립 Orca orchestration**이다.

1. Run에 바인딩
2. `orca orchestration task-create`
3. `orca orchestration worker-start` 또는 `dispatch --inject`
4. 이벤트 대기: `check --wait --types worker_done,escalation,question`
5. 응답의 camelCase `deliveryId`를 다음 wait의 ack에 연결 — 미ack 백로그는 같은 배치를 반복 반환

<Info>
원시 terminal polling·벤더 직행 CLI는 task/Dispatch 출처와 `worker_done` 권한을 우회한다. 비규정 경로다. 2차 의견 리뷰 등 비디스패치 용도의 벤더 플러그인은 허용된다. 실수로 orchestration 밖에서 한 작업은 **non-orchestrated**로 기록하고 소급 재라벨하지 않는다.
</Info>

## Model probe

`scripts/model-identity-probe`가 참조 구현이다.

- 입력: 세션 transcript JSONL (`--transcript`) 또는 instance config / `MOGUI_TRANSCRIPT_GLOB`으로 최신 매칭
- 출력: 최근 assistant 모델 분포; `--expect`/`MODEL_IDENTITY_EXPECT` 있으면 OK/DRIFT
- TUI status line 스크래핑 금지 — 렌더러가 그린 문자열이 아니라 **에이전트가 쓴 아티팩트**만

`master-ops/scripts/dispatch`는 worktree-scoped glob이 가능한 런타임(현재 파생: `claude`)에서만 model probe를 켠다. 스코프 불가 런타임은 `model=unavailable`로 표기하고 **false pass를 만들지 않는다**. 호출자가 확인한 경로만 `--transcript-glob`으로 넘긴다.

## Pretrust: Codex / Cursor

| 런타임 | 스크립트 | 동작 | 성공 조건 |
|--------|----------|------|-----------|
| Codex | `scripts/codex-worker-pretrust <abs-worktree>` | Orca codex-accounts `*/home/config.toml`에 `[projects."<path>"]` trust | `Summary:`에 `skipped` 없음. 래퍼는 skipped를 fail-closed |
| Cursor | `scripts/cursor-worker-pretrust <abs-worktree>` | `~/.cursor/projects/<key>/.workspace-trusted` | exit 0, summary가 `added`/`updated`/`already trusted` (not `skipped`) |

절대 경로 필수. Codex 계정이 없으면 “nothing to pre-trust”로 0 종료할 수 있으나, tomllib 없는 Python이면 `Summary: skipped` — 디스패치 래퍼는 이를 거절한다. Cursor launch 플래그 `--force --trust`는 보조이며, worktree attach 전 pretrust를 대체하지 않는다.

## 워커 계약 필드 (게이트 밖 규율)

계약은 다른 세션이 추측 없이 실행할 수 있을 만큼 좁혀 쓴다.

- 대상 저장소·checkout
- 허용 작업면 / 금지 편집
- acceptance 기준·필수 증거
- commit / push / branch 규칙 (권한 없으면 미커밋이 보수 기본)
- 알려진 제외 항목

워커 “Done”은 클레임일 뿐이다. 수락은 마스터의 독립 검증이다 → [증거와 수락](/evidence-and-acceptance).

## Delivery vs register

`register ✓`는 ledger에 job이 잡혔다는 뜻이다. inject가 trust 메뉴·start screen에 떨어지면 스펙이 소비되지 않은 채 전 단계가 성공으로 보일 수 있다.

`master-ops/scripts/dispatch` exit 요약:

| 코드 | 의미 |
|------|------|
| 0 | 정상 (delivery 경고 포함 가능) |
| 1 | 인자/계약 파일 문제 |
| 2 | gate DENY, top 미승인, model/runtime 불일치, pretrust 실패 등 |
| 3 | task-create / terminal 실패 |
| 4 | register DENY (워커는 이미 돌 수 있음 — 수동 정리) |
| 5 | delivery FAILED (`hook-trust` / `limit`) |

## 주요 reason code

| 코드 | 단계 | 의미 |
|------|------|------|
| `OK` | check/register | 허용 |
| `NO_COMPLETION_CHANNEL` | check | 완료 채널 누락/불법 |
| `NO_MODEL` | check | 모델 미지정 |
| `CONTRACT_UNREADABLE` | check | 계약 읽기/크기 측정 실패 |
| `BUDGET_EXCEEDED` | check | 문자/배치 한도 |
| `TIER_POLICY` / `TIER_POLICY_UNAVAILABLE` | check | v1 거부 또는 정책 파일 불능 |
| `TIER_FANOUT_CAP` | check | v2 창 누적 cap 초과 |
| `TIER_UNKNOWN_MODEL` | check | 경고: 미등록 모델 → `unknown` |
| `UNVERIFIED_JOB` | register | probe 실패 |
| `NO_MATCHING_TICKET` / `AMBIGUOUS_TICKET` | register | 티켓 0개/다수 |
| `ORCHESTRATION_UNVERIFIED` | register | task 미검증 |
| `MODEL_TIER_ESCALATION` | register | 실측 티어 승격 **거부** |
| `MODEL_MISMATCH` / `MODEL_UNVERIFIED` / `MODEL_PROBE_FAILED` | register | 경고 기록 |

CLI: check/register 거부 시 exit **2**. `watch` stalled는 **3**, missing은 **2**.

## 검증 신호

<Check>
- `check` stdout `allow: true` + 비어 있지 않은 `contract_sha`
- 기록 모드에서 ticket 파일 존재, ledger에 ALLOW 행 (policy path + `tier_policy_sha256`)
- Codex/Cursor: pretrust `Summary:`가 skipped가 아님
- `register` allow + (가능하면) `model_verified` 또는 명시적 unverified 경고
- orchestration 경로: task id와 dispatch id가 `dispatch-show` 등으로 교차 확인
- 래퍼 사용 시 `delivery … class=agent-started` 또는 수동 pane 확인
</Check>

## 자주 나는 실패

| 증상 | 원인 | 조치 |
|------|------|------|
| dry-run 후 cap 초과 | `--no-record` 없이 check | `--check-only` / `--no-record` |
| `MODEL_PROBE_FAILED` | transcript 미생성·비범위 glob | worktree-scoped 경로; 없으면 미검증으로 기록 후 수동 확인 |
| inject 성공·일 안 함 | trust/start 메뉴에 idle | pretrust, pane 분류, 메뉴에 재주입 금지 |
| 잘못된 master host 가드 | `master_host_runtime` fallback | `MOGUI_MASTER_HOST_RUNTIME` 또는 instance-runtime 설정 |
| top 거부 | `--top-approved` 없음 | 오너 사유를 플래그로 전달 (절차 증거, 인증 경계 아님) |

## 관련 표면 경계

- **수락 루프·리뷰 렌즈**: 이 페이지의 register 이후. 게이트는 “출발 허가 + 등록”만 담당.
- **Worker reap**: lease `issued→reaped`, terminal/worktree 정리 — [Worker reap](/worker-reap).
- **플래그·스키마 전체 표**: [dispatch-gate 레퍼런스](/dispatch-gate-reference).

## Next

<CardGroup>
  <Card title="dispatch-gate 레퍼런스" href="/dispatch-gate-reference">
    check·register·watch·report 플래그, ledger 스키마, reason code, TTL, 문자 한도.
  </Card>
  <Card title="증거와 수락" href="/evidence-and-acceptance">
    self-report와 독립 검증, 리뷰 렌즈, acceptance 판정.
  </Card>
  <Card title="방어 인벤토리" href="/defense-inventory">
    디스패치 게이트·model probe·pretrust를 포함한 가드 표.
  </Card>
  <Card title="인스턴스 설정" href="/configure-instance">
    instance-runtime, model-tier-policy, transcript glob, host runtime.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    MODEL_PROBE_FAILED, placement, preflight BLOCKED 복구.
  </Card>
  <Card title="Worker reap" href="/worker-reap">
    등록 이후 lease 회수와 worktree 정리.
  </Card>
</CardGroup>

---

## 10. Clean succession

> handoff 작성, placement 검증 spawn, successor 검증, predecessor retire, revival 측정, lineage 스키마 필드.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/10-clean-succession.md
- Generated: 2026-08-07T07:04:10.085Z

### Source Files

- `docs/public/master-lifecycle.md`
- `src/master_runtime/core/succession.py`
- `src/master_runtime/core/lineage.py`
- `src/master_runtime/core/recovery.py`
- `master-ops/docs/runbooks/succession-boot-card.md`
- `master-ops/docs/charter/06-succession.md`
- `scripts/master-succeed`
- `tests/test_succession.py`

---
title: "Clean succession"
description: "handoff 작성, placement 검증 spawn, successor 검증, predecessor retire, revival 측정, lineage 스키마 필드."
---

Clean succession은 `scripts/master-succeed`와 `master_runtime.core.succession` / `lineage` / `recovery`가 구현하는 **명시적 마스터 세대 전환**이다. 자동 승계는 없고, 선행 마스터가 thin handoff를 만든 뒤 placement가 검증된 후계자를 spawn하고, 후계자가 recovery 보고서로 검증된 다음에야 predecessor를 측정 기반 retire하며, 결과는 append-only lineage에만 남긴다. 사고 복구(같은 세션 resume)는 succession이 아니다.

## 한눈에 보는 흐름

```text
detect (IMMEDIATE only auto-acts as policy)
  → promotion audit (durable SSOT)
  → handoff (thin markdown from JSON spec)
  → spawn (placement + handle liveness fail-closed)
  → successor boot / placement three-set / Role State
  → verify-successor (recovery report PASS|PARTIAL|FAILED)
  → retirement handshake → retire --execute (three disappearances)
  → revival scan (lineage session ids in process argv)
  → append lineage (observability only)
```

| 단계 | 공개 CLI | 핵심 모듈 | 판정 단위 |
|------|----------|-----------|-----------|
| 트리거 | `master-succeed detect` | `detect_trigger` | `IMMEDIATE` / `ADVISORY` / `NONE` |
| handoff | `master-succeed handoff` | `build_handoff` | thin markdown 섹션 |
| spawn | `master-succeed spawn` | `spawn_successor` | worktree·placement·handle liveness |
| 중복 | `master-succeed check-duplicates` | `detect_duplicate_instances` | same-marker 세션 목록 |
| 후계 검증 | `master-succeed verify-successor` | `verify_successor` | recovery step 상태 |
| retire | `master-succeed retire` | `retire_predecessor` | pane·process·tty 소멸 |
| lineage | (런타임 호출) | `lineage.append_entry` | 고정 스키마 섹션 |

운영 절차 원문은 `master-ops/docs/charter/06-succession.md`와 `master-ops/docs/runbooks/succession-boot-card.md`이다. CLI 플래그·exit 코드 표는 [master-succeed 레퍼런스](/succession-cli-reference)를 본다.

## 전제와 불변식

- **트리거는 명시적 사용자 지시만 실행 경로를 연다.** 컨텍스트 비율 ≥ 0.60 또는 마일스톤은 `ADVISORY`이며 메시지에 auto-succession 금지 문구를 붙인다.
- **정상 운영은 continue-and-compact.** 수락 지식·활성 트랙·열린 결정은 이슈 트래커/Git에 먼저 promote한다. handoff는 얇은 스냅샷이지 SSOT가 아니다.
- **Placement three-set** (spawn 후 후계자가 측정):
  1. host pane/worktree selector가 의도 workspace와 일치
  2. process cwd가 workspace root(`{{WORKSPACE_ROOT}}`) 아래
  3. session artifact/log path가 기대 workspace/session namespace
- **UI 제목·상태줄은 placement 증거가 아니다.**
- **Lineage는 관측 메타데이터만.** 부트스트랩 입력·런타임 결정에 쓰지 않는다.
- **사고 복구 ≠ succession.** 프로세스 사망·host 재시작·stale handle·우발 pane 종료는 먼저 동일 세션 resume을 시도하고, 라이브 중복 프로세스가 없음을 증명한다.

## 절차

<Steps>
<Step title="Promotion audit">
수락 지식, 활성/열린 트랙, 미해결 acceptance 증거를 durable store에 올린다. 부트 카드 규칙: promotion audit 없이 successor를 spawn하지 않는다.
</Step>
<Step title="트리거 분류 (선택)">
```bash
scripts/master-succeed detect "succession now" --context-ratio 0.65 --json
```
`IMMEDIATE` 마커: `succession now`, `handoff to successor`, `승계해줘`, `다음 마스터로 넘기자`, `승계 진행해`.
</Step>
<Step title="Thin handoff 작성">
구조화 JSON spec에서 handoff markdown을 생성한다.

```bash
scripts/master-succeed handoff --spec ./ops/handoff-spec.json --json
```

필수 섹션: `## Role State`, `## Current Objective`, `## Active/Open Tracks`, `## Accepted Artifacts`, `## Deferred Work`, `## Open Questions`, `## Recommended Next Role`, `## Observed Baseline`. Role State는 freeze되어 현재 역할만 유지하고 나머지는 lock된다 (`unlock: explicit user instruction only`).
</Step>
<Step title="Placement 검증 spawn">
```bash
scripts/master-succeed spawn \
  --workspace-selector "id:folder:<uuid>" \
  --kickoff-file ./ops/kickoff.md \
  --root . \
  --model example-model \
  --title "Successor master boot" \
  --expected-placement "folder:<uuid>" \
  --json \
  --dry-run
```
비 dry-run 시 `orca terminal create` 후 worktreeId가 selector(및 `--expected-placement`)와 일치해야 한다. 불일치 시 생성 terminal을 close하고 fail-closed한다. 보고 handle은 스냅샷 이후 **live·new·connected** 여야 하며, 그렇지 않으면 동일 worktree·동일 title 후보가 정확히 1개일 때만 `MATCH_REISSUED`로 채택한다.
</Step>
<Step title="Successor boot와 검증">
후계자는 Role State 선언, 측정 model field, placement three-set, (지원 시) coordinator Run 바인딩을 수행한다. recovery 보고서 형태로:

```bash
scripts/master-succeed verify-successor --report ./ops/recovery-report.json --json
```

`verify_successor`는 recovery step 중 하나라도 `MISS`면 `FAILED`. 그 외 step `6`=OK(트랙 낭독), `2-3`=OK/없음(baseline), `5`=OK(모니터) 세 체크가 모두 있으면 `PASS`, 일부면 `PARTIAL`.
</Step>
<Step title="Retirement handshake 후 retire">
Freeze는 retirement가 아니다. 합의된 FIN/ACK 핸드셰이크 후 후계자가 close한다. 기본은 dry-run; 실제 close는 `--execute`.

```bash
scripts/master-succeed retire \
  --self-handle <successor-handle> \
  --target-handle <predecessor-handle> \
  --target-pid <measured-pid> \
  --target-tty <measured-tty> \
  --json \
  --execute
```

`CLOSED`는 **pane·process·tty 세 소멸이 모두 `measured`**일 때만. process/tty를 건너뛰면 `CLOSED_PARTIAL`. 생존은 `REFUSED`. self-handle 매치·후보 0/2+는 거부.
</Step>
<Step title="Revival 측정과 lineage 기록">
부트 시 및 소유자 보고 시 `docs/lineage/MASTER-LINEAGE.md`에 기록된 session id를 **프로세스 argv**에서 스캔한다(CLI resume 플래그가 아니라 session id가 이식 키). 히트 시 활동 복구 → 미응답 지시 흡수 → 프로세스 종료 → tty chain hang-up → 소멸 재측정. 검증 후 lineage 항목을 append한다.
</Step>
</Steps>

## Trigger 분류

| status | 조건 | 정책 의미 |
|--------|------|-----------|
| `IMMEDIATE` | 명시 마커 문자열 포함 | 사용자 지시로 succession 절차 진행 가능 |
| `ADVISORY` | `context_ratio >= 0.60` 또는 non-empty `milestone` | 제안만; 자동 spawn 금지 |
| `NONE` | 그 외 | 트리거 없음 |

`detect`는 분류만 하며 spawn을 호출하지 않는다.

## Handoff 스펙

`build_handoff`는 mapping/JSON에서 markdown을 조립한다.

| 입력 키 | 타입 | 용도 |
|---------|------|------|
| `role_state` 또는 `current_role` | RoleState / str | Role Lock 블록 |
| `current_objective` | str | 현재 목표 |
| `active_tracks`, `open_tracks` | list | 활성/열린 트랙 |
| `accepted_artifacts` | list | 수락 산출물 |
| `deferred_work` | list | 보류 작업 |
| `open_questions` | list | 열린 질문 |
| `recommended_next_role` | str | 권장 다음 역할(기본: frozen current) |
| `observed_baseline` | str | 관측 baseline |

Handoff 본문에 `Current Role:` / `Role Lock:` 블록이 있어야 recovery step 1이 Role State를 인식한다.

## Placement 검증 spawn

### 실패·성공 상태

| 결과 | 의미 |
|------|------|
| `DRY_RUN` | create 없이 명령·startup 문자열만 반환 |
| `CREATED` + `verification.result=MATCH` | 보고 handle이 live+new+connected |
| `CREATED` + `MATCH_REISSUED` | handle 재발급; 유일 title 후보 채택, `handle_reissued=true` |

### Exit 코드 (spawn 경로)

| 코드 | 상수 | 조건 |
|------|------|------|
| 20 | `SPAWN_CREATE_ERROR` | `orca terminal create` 실패 |
| 21 | `SPAWN_PARSE_ERROR` | create JSON 파싱/필드 누락 |
| 22 | `SPAWN_WORKTREE_MISMATCH` | 반환 worktreeId ≠ `--workspace-selector` |
| 23 | `SPAWN_CLOSE_ERROR` | mismatch cleanup close 실패 |
| 24 | `SPAWN_HANDLE_STALE` | handle 신뢰 불가, 재발급 후보 ≠ 1 |
| 25 | `SPAWN_LIST_ERROR` | precheck/liveness list 실패 |
| 26 | `SPAWN_PLACEMENT_MISMATCH` | `--expected-placement` 불일치 |

`--expected-placement`는 독립적으로 기대하는 worktree id다. 형식: `id:<repoId>::<path>`, `folder:<uuid>` / `id:folder:<uuid>`. `path:`는 repository worktree에만 매칭된다.

### Agent 기본 모델 (`scripts/master-succeed`)

| `--agent` | 생략 시 기본 `--model` |
|-----------|------------------------|
| `claude`, `claude-code` | `claude-fable-5` |
| `grok`, `grok-build` | `grok-4.5` |
| `codex` | `gpt-5.6-sol` |
| `cursor` / `agy` 등 | 기본값 없음 → `--model` 필수 (exit 2) |

Startup 커맨드는 agent별로 다르며(예: claude `--dangerously-skip-permissions`, grok `--always-approve`, codex는 launch approval 없음, cursor는 `cursor-agent --force --trust`), 미측정 agent는 이름만 executable로 취급하고 approval 플래그를 추측하지 않는다.

### 셀렉터 힌트

수락 형태: full `id:<repoId>::<path>`, folder workspace `id:folder:<uuid>`; `path:`는 resolved directory로 매칭. 마스터 좌석은 multi-repo에서 folder workspace, single-repo에서는 primary worktree다.

## Successor 검증과 recovery 단계

`verify_successor` 입력은 `RecoveryReport` 또는 동일 shape JSON이다. recovery executor(`recover`)는 **파일 쓰기·프로세스 변경 없이** step 0–6을 관측한다.

| step | 내용 | 실패 시 |
|------|------|---------|
| 0 | charter + bootstrap Role State | `MISS` → 이후 fail-closed skip |
| 1 | handoff 존재·Role State 블록 | missing handoff → `MISS` |
| 1-ledger | work ledger active tracks (옵션) | path 없으면 `SKIP` |
| 2-3 | 저장소 git 관측 | dirty 등 `WARN` |
| 4 | miss 수집 + Trace Archive 수동 | 항상 manual note |
| 5 | monitor pgrep 패턴 | 매치 시 재장착 수동 action |
| 6 | successor checklist (트랙 낭독, PID 비교, lineage append) | 항상 `OK` + manual_actions |

체크리스트 고정 문구에는 “Append the succession lineage entry after verification.”가 포함된다.

## Predecessor retire

### 상태 머신

| status | 조건 |
|--------|------|
| `DRY_RUN` | `--execute` 없음; close 미호출 |
| `REFUSED` | 후보 없음/모호, self 매치, close 실패, 소멸 실패(`still_present`) |
| `CLOSED_PARTIAL` | pane `measured`, 생존 없음, process/tty 중 `skipped:*` |
| `CLOSED` | pane·process·tty 모두 `measured` |

### `disappearances` 값

| 키 | 값 |
|----|-----|
| `pane` | `measured` \| `still_present` |
| `process` | `measured` \| `still_present` \| `skipped:…` |
| `tty` | `measured` \| `still_present` \| `skipped:…` |

Folder-workspace pane은 host list에서 `process_id: null`인 경우가 많다. 호출자가 측정한 `--target-pid` / `--target-tty`를 넘겨야 full `CLOSED`가 된다. 도구는 null 레코드에서 pid를 발명하지 않는다.

### Handshake 요약 (운영)

| TCP 비유 | 행위 | 주체 |
|----------|------|------|
| FIN → | 신규 dispatch 중단·flush 요청 | successor |
| ← ACK / half-close | CLOSE_WAIT 규칙 준수·커밋 | predecessor |
| ← FIN | 합의 FIN 한 줄 | predecessor |
| TIME_WAIT | 조용 구간 측정 후 close | successor |
| CLOSED | `retire --execute` + 삼중 소멸 | successor |
| RST | 동의 없는 abortive close | 소유자 승인 필요 |

CLOSE_WAIT에 포함되는 약속: 신규 dispatch 금지, peer-mailbox 이중 drain 또는 무ack, orchestration 추가 송신 없음, 미커밋 상태 커밋 또는 경로 보고, 소유 worker·트랙 목록, 합의 FIN 한 줄. FIN은 idle prompt에 보내고 pane read로 소비를 검증한다.

CLOSED 전 predecessor scratchpad를 durable(비-git) 위치로 스윕하고 trunk와 diff 판정을 남긴다.

## Revival 측정

동결 세션은 agent CLI가 돌아가는 어떤 단말에서도 영구 resume 가능하다. 측정 사건(2026-08-03): 모바일 resume으로 retired master 네 개가 동시에 부활. 대응:

1. lineage에 적힌 session id를 실행 중 agent 프로세스 argv에서 스캔
2. 히트 세션 자체 기록에서 freeze 이후 활동 확인
3. 미응답 소유자 지시를 현 마스터로 회수
4. agent 프로세스 종료 → hosting terminal chain hang-up → tty 소멸 확인
5. 다른 자식이 있는 셸은 자식을 먼저 측정

Revival 소멸도 retirement와 동일한 삼중 측정 기준을 따른다. handoff 텍스트의 “predecessor gone”과 런타임 상태는 별개로 검사한다.

## Lineage 스키마

`master_runtime.core.lineage.append_entry(path, entry)`는 markdown ledger에 세대 섹션을 append-only로 추가한다. 기본 경로 관례: `docs/lineage/MASTER-LINEAGE.md`.

### 필수 필드

| 필드 | 타입 | 제약 |
|------|------|------|
| `generation` | int | ≥ 0; 기존 Generation 마커와 중복 불가 |
| `parent_session` | str | 선행 세션 식별 |
| `successor_session` | str | 후계 세션 식별 |
| `timestamp` | str | 승계 시각 서술 |
| `inherited_role` | str | 상속 역할(승계가 역할을 바꾸지 않음이 일반) |
| `succession_reason` | str | 사유 |
| `recovery_sources` | str | 복구 소스 목록 서술 |
| `inherited_open_tracks` | str | 상속 트랙 요약 |
| `verification` | str | `PASS` \| `PARTIAL` \| `FAILED` only |
| `repeated_question_count` | int | ≥ 0 |
| `reopened_decision_count` | int | ≥ 0 |
| `context_loss_summary` | str | 컨텍스트 손실 요약 |
| `predecessor_retirement_verified` | str | retirement 검증 서술 |
| `notes` | str | 선택 |

렌더 헤더: `## Gen {generation} — {timestamp}`. 검증 실패 시 파일 바이트 불변; write 후 prefix 불일치 시 `LineageAppendError`와 원본 복원.

Lineage는 회고·승계 품질 지표용이다. 실행 메모리로 쓰지 않는다.

## 검증 신호

| 신호 | 기대 |
|------|------|
| `detect … --json` | `status` IMMEDIATE/ADVISORY/NONE |
| `handoff --json` | `handoff`에 Role State 포함 8섹션 |
| `spawn --dry-run --json` | `status=DRY_RUN`, create command·startup_command |
| spawn success | `status=CREATED`, `verified=true`, `MATCH` 또는 `MATCH_REISSUED` |
| placement 실패 | stderr + exit **26**, 생성 terminal close 시도 |
| `verify-successor` | `PASS` / `PARTIAL` / `FAILED` |
| `retire` without `--execute` | `DRY_RUN` |
| full retire | `CLOSED`, disappearances 세 키 모두 `measured` |
| partial measure | `CLOSED_PARTIAL` (skip을 pass로 읽지 말 것) |
| revival | lineage session id 프로세스 없음 + tty 소멸 |
| lineage append | 새 `## Gen N` 섹션, 기존 prefix 바이트 동일 |

## 실패 모드와 복구

| 증상 | 원인 | 조치 |
|------|------|------|
| `SPAWN_PLACEMENT_MISMATCH` (26) | create 위치 ≠ expected | selector·expected-placement 재측정, unmanaged terminal reconcile 후 재시도 |
| `SPAWN_HANDLE_STALE` (24) | handle 재사용/지연 | `orca terminal list`로 정리, title 유일성 확보 |
| retire `REFUSED` ambiguous | 후보 2+ | `--target-handle` / pty / session-id로 단일화 |
| retire self match | self_handle = 후보 | 측정 handle 교환 확인 |
| `CLOSED_PARTIAL` | pid/tty 미공급 | 라이브 측정 후 `--target-pid`/`--target-tty` 재실행 |
| process/tty still_present | close 반환만 신뢰 | 측정 재시도; RST는 소유자 승인 |
| verify FAILED | recovery MISS | charter/handoff 복구 후 recover 재실행 |
| dual revival | 모바일/원격 resume | session id 스캔 → 삼중 소멸 |
| model drift at audit | mid-session model 변경 | `model-drift-audit` 전체 walk; exit 2는 undecidable ≠ pass |

추가 트러블슈팅 표는 [Troubleshooting](/troubleshooting), 방어 장치 목록은 [방어 인벤토리](/defense-inventory)를 본다.

## CLI 요약

```bash
scripts/master-succeed detect TEXT [--context-ratio F] [--json]
scripts/master-succeed handoff --spec PATH [--json]
scripts/master-succeed verify-successor --report PATH [--json]
scripts/master-succeed check-duplicates --self-handle H --marker M [--json]
scripts/master-succeed retire --self-handle H [--expected S]
  [--target-handle H] [--target-pty-id ID] [--target-session-id ID]
  [--target-pid PID] [--target-tty TTY] [--execute] [--json]
scripts/master-succeed spawn --workspace-selector SEL
  (--kickoff-text T | --kickoff-file F) --root PATH --title TITLE
  [--model M] [--agent A] [--expected-placement P] [--dry-run] [--json]
```

공통: 성공 시 JSON payload를 stdout에 출력(기본 pretty, `--json`은 compact). `SuccessionError`는 stderr 메시지 + `exit_code`(기본 2, spawn 전용 20–26).

## Related pages

<CardGroup cols={2}>
  <Card title="마스터 라이프사이클" href="/master-lifecycle">
    founding spawn → boot → steady state → succession → lineage 루프
  </Card>
  <Card title="master-succeed 레퍼런스" href="/succession-cli-reference">
    하위 명령·플래그·exit 코드·SPAWN_PLACEMENT_MISMATCH
  </Card>
  <Card title="Orca 객체 모델" href="/orca-object-model">
    folder workspace·worktree·terminal selector 형식
  </Card>
  <Card title="프로그레시브 온보딩" href="/onboarding">
    Generation 1 founding spawn과 템플릿 경계
  </Card>
  <Card title="방어 인벤토리" href="/defense-inventory">
    placement·duplicate·revival 가드 표
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    placement mismatch, seat 중복, revival 복구 프로브
  </Card>
</CardGroup>

---

## 11. 인스턴스 설정

> instance-runtime·model-tier-policy 작성, env 우선순위, transcript glob, master host runtime, 티어×fan-out 캡.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/11-page-11.md
- Generated: 2026-08-07T07:04:10.287Z

### Source Files

- `config/instance-runtime.example.json`
- `config/model-tier-policy.example.json`
- `src/master_runtime/core/instance_runtime_config.py`
- `src/master_runtime/core/dispatch_gate.py`
- `master-ops/model-tier-policy.json`
- `scripts/model-identity-probe`
- `tests/test_instance_runtime_config.py`

---
title: "인스턴스 설정"
description: "instance-runtime·model-tier-policy 작성, env 우선순위, transcript glob, master host runtime, 티어×fan-out 캡."
---

인스턴스 소유 런타임 사실은 `config/instance-runtime.json`과 `config/model-tier-policy.json`에 기록되며, 템플릿은 각각 `config/instance-runtime.example.json`·`config/model-tier-policy.example.json`만 제공한다. 소비자는 환경 변수 → 인스턴스 파일 → 정직한 unconfigured(또는 티어 정책 템플릿 폴백) 순으로 값을 해석하고, 호스트·모델 식별자를 코드에 구워 넣지 않는다. 채워진 파일은 `.gitignore` 대상이며 git에 커밋하지 않는다.

## 인스턴스 파일 경계

| 파일 | 역할 | 기본 경로 해석 |
| --- | --- | --- |
| `config/instance-runtime.json` | 마스터 호스트 CLI, transcript glob, 선택적 primary product | `INSTANCE_RUNTIME_CONFIG` → `<repo>/config/instance-runtime.json` |
| `config/model-tier-policy.json` | 티어×fan-out 정책 (게이트 소비) | `DISPATCH_TIER_POLICY` → 인스턴스 파일(존재 시) → `master-ops/model-tier-policy.json` |
| `config/instance-runtime.example.json` | 스키마·문서용 예시 (커밋됨) | 복사 원본만 |
| `config/model-tier-policy.example.json` | v2 정책 예시 (커밋됨) | 복사 원본만 |

<Warning>
채워진 `config/instance-runtime.json` / `config/model-tier-policy.json`은 인스턴스 소유다. 예제 파일을 저장소에 덮어쓰거나 커밋하지 않는다. onboarding preflight가 없을 때 `cp …example…` 한 번 후 로컬에서만 채운다.
</Warning>

```text
config/
  instance-runtime.example.json   # 템플릿 (커밋)
  instance-runtime.json           # 인스턴스 작성 (gitignore)
  model-tier-policy.example.json  # 템플릿 (커밋)
  model-tier-policy.json          # 인스턴스 작성 (gitignore)
master-ops/
  model-tier-policy.json          # 게이트 템플릿 폴백 (커밋)
```

로더는 `src/master_runtime/core/instance_runtime_config.py`와 `src/master_runtime/core/dispatch_gate.py`의 `_default_tier_policy_path`다. 기본 경로 계산은 `Path.cwd()`가 아니라 모듈 기준 repo root를 쓰므로, 다른 디렉터리에서 프로브해도 `config/`를 찾는다.

## instance-runtime 작성

### 스키마

| 키 | 타입 | 필수 | 의미 |
| --- | --- | --- | --- |
| `master_host_runtime` | string \| null | 마스터/프로브 시 필요 | 마스터 세션을 호스팅하는 agent CLI 이름 (`claude`, `codex`, `grok` 등) |
| `transcript_globs` | object string→string | 프로브 시 런타임별 필요 | 런타임 이름 → 세션 JSONL 파일시스템 glob |
| `product_repo` | string \| null | 선택 | primary product 저장소 절대 경로; 멀티 제품·미지정 시 omit/null |
| `_docs` 등 `_` 접두 키 | any | 무시 | 문서/주석; 로더가 제거 |

`transcript_globs` 키는 **런타임(agent CLI) 이름**이다. 머신 별명·호스트 닉네임이 아니다. 최소한 `master_host_runtime`에 해당하는 엔트리가 측정되거나 소유자가 명시해야 한다.

### 작성 절차

<Steps>
  <Step title="예제 복사 (없을 때만)">
```console
$ cd "{{RUNTIME_ROOT}}"
$ test -f config/instance-runtime.json || cp config/instance-runtime.example.json config/instance-runtime.json
```
  </Step>
  <Step title="master_host_runtime 설정">
preflight/측정으로 확인된 agent CLI 이름을 쓴다. 측정값과 소유자 기대가 다르면 확인 후 기록한다. 측정이 없으면 추측하지 않는다.
  </Step>
  <Step title="transcript_globs 측정·기록">
각 프로브 대상 런타임에 대해 이 머신의 세션 JSONL 트리를 측정해 glob을 넣는다. 다른 워크스페이스 경로를 그대로 붙여 넣지 않는다.
  </Step>
  <Step title="product_repo (선택)">
소유자가 primary product를 확인한 경우에만 절대 경로를 쓴다. 폴더 레이아웃만으로 추측하지 않는다.
  </Step>
</Steps>

예제 형태:

```json
{
  "master_host_runtime": "claude",
  "transcript_globs": {
    "claude": "~/.claude/projects/-Users-example-workspace/*.jsonl",
    "codex": "~/.codex/sessions/**/*.jsonl",
    "grok": "~/.grok/sessions/**/*.jsonl"
  },
  "product_repo": "/absolute/path/to/primary-product-repo"
}
```

### 환경 변수 우선순위

모든 값의 공통 규칙:

1. 환경 오버라이드  
2. 인스턴스 설정 파일  
3. 정직한 unconfigured (`InstanceRuntimeConfigError`) — 베이크된 기본값 없음  

| 환경 변수 | 파일 키 | 비고 |
| --- | --- | --- |
| `INSTANCE_RUNTIME_CONFIG` | (경로 자체) | 설정 파일 위치 오버라이드 |
| `MOGUI_MASTER_HOST_RUNTIME` | `master_host_runtime` | 1순위 |
| `MASTER_HOST_RUNTIME` | `master_host_runtime` | 일부 래퍼용 대체 이름; `MOGUI_*` 다음 |
| `MOGUI_TRANSCRIPT_GLOB` | `transcript_globs.<runtime>` | **런타임 비종속** 단일 glob; 활성 프로브 한 번에 적용 |
| `MOGUI_PRODUCT_REPO` | `product_repo` | — |

`require_*` 헬퍼:

- `require_master_host_runtime()` — 비어 있으면 에러  
- `require_transcript_glob(runtime?)` — env glob이 있으면 즉시 반환; 없으면 `transcript_globs[name]`  
- `require_product_repo()` — 비어 있으면 에러  

파일 부재는 로드 오류가 아니다. 값이 env로도 없으면 `require_*`가 unconfigured를 낸다. 잘못된 타입(예: `master_host_runtime`이 배열)은 hard error다. JSON 파싱 실패도 `InstanceRuntimeConfigError`다.

### Transcript glob과 model-identity-probe

`scripts/model-identity-probe`는 세션 JSONL에서 최근 assistant `model` 필드를 읽는다. 경로 해석 순서:

1. `--transcript` (명시 경로)  
2. `MOGUI_TRANSCRIPT_GLOB` (최신 mtime 매치)  
3. 인스턴스 설정: `--runtime` 또는 `master_host_runtime` → `transcript_globs`  
4. unconfigured → exit `2`, `MODEL-PROBE DRIFT: unconfigured …`

추가 플래그/환경:

| 입력 | 기본 | 효과 |
| --- | --- | --- |
| `--expect` / `MODEL_IDENTITY_EXPECT` | 없음 | 있으면 최근 N개 모델이 전부 일치해야 OK; 없으면 INFO만, exit 0 |
| `--limit` | `10` | 최근 assistant 모델 개수; `<1`이면 exit 2 |
| `--config` | `INSTANCE_RUNTIME_CONFIG` 또는 `config/instance-runtime.json` | 설정 경로 |
| `--runtime` | `master_host_runtime` | glob 키 선택 |

성공/실패 신호:

- `MODEL-PROBE OK <model> N/N` — exit 0  
- `MODEL-PROBE INFO … nothing asserted` — expect 없음, exit 0  
- `MODEL-PROBE DRIFT: …` — exit 2 (불일치·transcript 오류·unconfigured)

모델 이름은 코드에 하드코딩되지 않는다. expect는 호출자/환경이 공급한다.

## model-tier-policy 작성

### 해석 순서 (dispatch gate)

`dispatch_gate._default_tier_policy_path`:

1. `DISPATCH_TIER_POLICY`  
2. `config/model-tier-policy.json` (파일 존재 시)  
3. `master-ops/model-tier-policy.json` (템플릿 폴백)  

CLI `--tier-policy`는 이 헬퍼 위에 있다 (`DispatchGateConfig.tier_policy_path` 직접 설정).

### 스키마 (version 2 — 게이트 소비 필드)

| 키 | 타입 | 필수 | 의미 |
| --- | --- | --- | --- |
| `version` | int | 예 (`2`) | v2 = 티어×fan-out; v1은 identity allow/deny 레거시 |
| `tiers` | object string→string[] | 예 (비어 있지 않은 객체) | 티어 이름 → model id 목록; 목록에 없는 id → 예약 티어 `unknown` |
| `fanout_caps` | object string→int | 예 (객체; 키는 선택) | 티어별 rolling window 안 agent 수 상한; **키 없음 = uncapped** (`top`·`unknown` 포함) |
| `window_seconds` | int | 기본 `86400` | fan-out 카운트 창(초); 양수 정수 |
| `agents` | array | 게이트 무시 | 측정/수동 인벤토리 (사람·재측정용) |
| `consent` | string | 게이트 무시 | `yes` \| `no` \| `manual-only` 등 온보딩 기록 |
| `_docs` / `_notes` | any | 무시 | 문서 |

제약 (파서):

- 티어 이름 `unknown`은 예약 — `tiers`에 넣을 수 없음  
- 한 model id는 한 티어에만 (casefold 후 중복 금지)  
- `fanout_caps` 키는 존재하는 티어이거나 `unknown`  
- cap 값은 non-negative integer (`bool` 불가)  
- model id는 비어 있지 않은 문자열, 앞뒤 공백 없음; 비교는 casefold  

### 온보딩: agent-inventory consent

Preflight 단계에서 소유자에게 인벤토리 프로브 동의를 묻는다.

| consent | 동작 |
| --- | --- |
| yes | PATH에 있는 후보 CLI 측정; `runtime`/`version`/`model_ids`는 측정값만; 측정 불가 필드는 문자열 `unknown`; 강도 구분이 명확할 때만 `tiers.top` / `tiers.efficient`에 배치 |
| no | 프로브 금지; 소유자가 명시한 런타임·모델만 기록; 빈 티어 리스트 허용 → 미등재 모델은 `unknown` |

```console
$ test -f config/model-tier-policy.json || cp config/model-tier-policy.example.json config/model-tier-policy.json
# version:2, agents[], tiers, fanout_caps, window_seconds, consent 채움
```

모델 id를 추측하지 않는다.

### 티어 × fan-out 캡

```mermaid
flowchart LR
  subgraph inputs [요청]
    M[request.model]
    N[n_agents]
  end
  subgraph policy [ModelTierPolicy v2]
    T[tier_of casefold]
    C[cap_for tier]
    W[window_seconds ledger tally]
  end
  subgraph outcomes [판정]
    OK[OK / ledger]
    CAP[TIER_FANOUT_CAP deny]
    UNK[TIER_UNKNOWN_MODEL warning]
  end
  M --> T
  T --> C
  C -->|cap set and used+n_agents > cap| CAP
  C -->|no key uncapped| OK
  T -->|tier == unknown| UNK
  N --> W
  W --> C
```

동작 요약:

- 모델 → 티어 매핑 후, 해당 티어의 `fanout_caps`가 있으면 ledger 창 안 사용량 + 요청 `n_agents`가 cap을 넘으면 `TIER_FANOUT_CAP` deny  
- cap 키 부재 = uncapped (모든 티어 동일 규칙)  
- 미등재 모델 = `unknown` 티어; 차단이 아니라 경고 `TIER_UNKNOWN_MODEL` + `fanout_caps.unknown`이 있으면 그 캡 적용  
- 템플릿/예제 기본: `fanout_caps.unknown: 8`, `window_seconds: 86400`, **`top` 캡 없음**

### Top-tier: cap이 아닌 소유자 승인

2026-08-05 소유자 지침: top-tier fan-out 숫자 캡을 제거하고, 디스패치마다 소유자에게 묻는다. `master-ops/scripts/dispatch`는 top-tier 모델에 `--top-approved "<reason>"`이 없으면 거부하고, reason을 런 로그에 남긴다. 게이트 정책 파일에서 `fanout_caps.top` 키를 빼 두면 top은 uncapped로 해석된다.

레거시 v1 정책(`worker_allowed` / `worker_denied_tiers` / `unknown_model: deny|warn`)은 그대로 로드된다. 런타임만 올려도 기존 설치의 v1 판정이 바뀌지 않는다.

### 정책 파일 부재·손상

`_load_tier_policy` 실패 시 게이트는 `TIER_POLICY_UNAVAILABLE`로 deny하고 `tier_policy=<path>` 메시지를 붙인다. 정상 결정은 ledger에 `tier_policy_path`와 `tier_policy_sha256`(파일 raw bytes의 sha256)을 기록한다.

## 검증 신호

| 확인 | 기대 |
| --- | --- |
| 인스턴스 파일 존재 | onboarding Verify: `config/instance-runtime.json`에 확인된 `master_host_runtime`; `config/model-tier-policy.json`에 `version: 2` |
| 로드 단위 테스트 | `tests/test_instance_runtime_config.py` — env > file > unconfigured, `_` 키 무시, probe glob 해석 |
| 프로브 | `scripts/model-identity-probe --expect <id>` → `MODEL-PROBE OK` 또는 DRIFT exit 2 |
| 게이트 | `scripts/dispatch-gate check …` — 정책 경로/digest, `TIER_FANOUT_CAP` / `TIER_UNKNOWN_MODEL` / `TIER_POLICY_UNAVAILABLE` |
| git 경계 | `git status`에 채워진 인스턴스 JSON이 스테이징되지 않음 (`.gitignore`) |

```console
$ # env만으로 transcript 프로브 (master_host 없이)
$ MOGUI_TRANSCRIPT_GLOB='~/.claude/projects/.../*.jsonl' \
    python3 scripts/model-identity-probe --expect claude-fable-5
```

## 실패 모드

| 증상 | 원인 | 조치 |
| --- | --- | --- |
| `master_host_runtime is unconfigured` | env·파일 모두 비어 있음 | preflight 후 파일 작성 또는 `MOGUI_MASTER_HOST_RUNTIME` |
| `transcript_glob for runtime '…' is unconfigured` | 해당 런타임 glob 없음 | 측정 후 `transcript_globs` 또는 `MOGUI_TRANSCRIPT_GLOB` |
| `no transcript files matched configured glob` | glob 오타·다른 머신 경로 복사 | 이 호스트에서 세션 경로 재측정 |
| `MODEL-PROBE DRIFT` | expect 불일치 또는 transcript 오류 | succession 검토; expect/모델 드리프트 확인 |
| `TIER_POLICY_UNAVAILABLE` | 정책 경로 없음·JSON 오류·version 오류 | `DISPATCH_TIER_POLICY` 또는 인스턴스/템플릿 파일 복구 |
| `TIER_FANOUT_CAP` | 창 안 agent 합이 cap 초과 | 대기·창 조정·정당한 `tier_override`(ledger됨) |
| `fanout_caps names a tier that does not exist` | 오타 티어 키 | `tiers`에 있는 이름 또는 `unknown`만 사용 |
| 미등재 모델이 항상 막힘 | v1 `unknown_model: deny` 또는 top과 동일 취급 착각 | v2로 전환 시 unlisted → `unknown`+선택 캡; top은 `--top-approved` |

## Next

<CardGroup>
  <Card title="Configuration reference" href="/configuration-reference">
    JSON 키·기본값·`INSTANCE_RUNTIME_CONFIG`·`DISPATCH_TIER_POLICY`·`MOGUI_*` 해석 순서 전체 표.
  </Card>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    check → dispatch → register, model probe, 완료 채널.
  </Card>
  <Card title="dispatch-gate 레퍼런스" href="/dispatch-gate-reference">
    check·register 플래그, ledger, reason code, 티어 정책 해석.
  </Card>
  <Card title="프로그레시브 온보딩" href="/onboarding">
    preflight에서 인스턴스 파일 작성·consent·Verify 게이트.
  </Card>
  <Card title="Workspace descriptor" href="/workspace-descriptor">
    sibling 인벤토리·`product_repo`와 맞물리는 workspace 경계.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    MODEL_PROBE_FAILED, preflight BLOCKED, 정책 복구 프로브.
  </Card>
</CardGroup>

---

## 12. Workspace descriptor

> sibling 저장소 인벤토리, role·capabilities·prohibited, master_seat, workspace-descriptor-check 액션과 해석 순서.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/12-workspace-descriptor.md
- Generated: 2026-08-07T07:03:56.462Z

### Source Files

- `config/workspace-descriptor.example.json`
- `src/master_runtime/core/workspace_descriptor.py`
- `scripts/workspace-descriptor-check`
- `docs/public/orca-concepts.md`
- `tests/test_workspace_descriptor.py`
- `master-ops/onboarding/02-workspace-facts.md`

---
title: "Workspace descriptor"
description: "sibling 저장소 인벤토리, role·capabilities·prohibited, master_seat, workspace-descriptor-check 액션과 해석 순서."
---

Workspace descriptor는 워크스페이스 루트 아래 sibling 저장소의 **선언적 인벤토리**다. 로더는 `src/master_runtime/core/workspace_descriptor.py`에 있고, 공개 가드 CLI는 `scripts/workspace-descriptor-check`다. 템플릿은 `config/workspace-descriptor.example.json`만 배송하며, 인스턴스 파일은 온보딩이 쓰는 `config/workspace-descriptor.json`(커밋하지 않음)이다. 소비자는 하드코딩된 product 경로 목록 대신 이 파일의 `prohibited`를 읽는다.

<Info>
워크스페이스 루트는 **plain folder**다. git parent가 아니며 submodule parent도 아니다. Orca가 해당 폴더에 "not a valid worktree folder"류 라벨을 붙여도 folder project 기준으로 정상이다.
</Info>

## 역할과 경계

| 표면 | 경로·식별자 | 책임 |
| --- | --- | --- |
| 스키마·로더 | `master_runtime.core.workspace_descriptor` | 해석 순서, 검증, 경로 매칭, `is_prohibited` |
| 예제(템플릿) | `config/workspace-descriptor.example.json` | 필드 문서(`_docs`)와 예시 인벤토리 |
| 인스턴스 파일 | `config/workspace-descriptor.json` | 온보딩이 측정·기록한 실측 인벤토리 |
| CLI | `scripts/workspace-descriptor-check` | path×action 금지 여부 질의 |
| 작성 절차 | `master-ops/onboarding/02-workspace-facts.md` | REPO_LIST 확정 후 파일 착지 |
| 운영 규칙 | `master-ops/docs/charter/04-worker-routing-review.md` | 디스패치 전 금지 액션 측정 |

로더는 기본 인벤토리를 **발명하지 않는다**. 파일이 없으면 `source_path=None`, `repositories=()`인 honest unconfigured다.

## 해석 순서

경로 결정은 `resolve_config_path` / `load_workspace_descriptor`와 CLI가 공유한다.

1. **명시 경로** — CLI `--config` 또는 API `path=` 인자 (env보다 우선)
2. **환경 변수** — `WORKSPACE_DESCRIPTOR`, 없으면 `MOGUI_WORKSPACE_DESCRIPTOR` (비어 있지 않은 값만)
3. **기본 파일** — `<repo>/config/workspace-descriptor.json`
4. **unconfigured** — 파일이 없으면 에러 없이 빈 디스크립터 (`source_path is None`)

```text
--config  >  WORKSPACE_DESCRIPTOR  >  MOGUI_WORKSPACE_DESCRIPTOR
         >  config/workspace-descriptor.json  >  unconfigured
```

`action_is_prohibited`와 CLI는 unconfigured일 때 `WorkspaceDescriptorError`를 내고, CLI exit **2**로 판정 불가를 표시한다. 권한을 추정하지 않는다.

## JSON 스키마

루트는 JSON object다. 키가 `_`로 시작하면 로더가 무시한다(`_docs` 등 문서 전용 필드).

### 워크스페이스 레벨

<ParamField body="workspace_root_is_plain_folder" type="boolean" required>
기본값 `true`. `true`가 아니면 파싱 실패. submodule parent·비 plain 루트를 거부한다.
</ParamField>

<ParamField body="workspace_root" type="string | null">
확인된 워크스페이스 루트 **절대 경로**. 절대 path 조회 시 이 루트 아래 relative remainder로만 매칭한다. 절대 조회를 쓰지 않으면 생략/`null` 가능.
</ParamField>

<ParamField body="master_seat" type="string">
마스터 세션 좌석 기록. 보통 multi-repo면 folder workspace of workspace root, 단일 저장소 워크스페이스면 primary worktree. 문자열이며 비어 있어도 파싱은 통과한다(온보딩 Verify는 비어 있지 않은 값을 요구).
</ParamField>

<ParamField body="repositories" type="array">
멤버 저장소 배열. `null`/생략이면 빈 튜플. `require_repositories()` 호출 시 비어 있으면 에러.
</ParamField>

### 저장소 엔트리

| 필드 | 타입 | 필수 | 규칙 |
| --- | --- | --- | --- |
| `name` | string | 예 | 비어 있지 않음. 보통 폴더 basename. 인벤토리 내 중복 불가 |
| `path` | string | 예 | 워크스페이스 루트 상대. 단일 저장소 워크스페이스면 `.`. 인벤토리 내 중복 불가 |
| `remote` | string | 아니오 | 기본 `""`. 측정된 origin URL; 없으면 빈 문자열 |
| `role` | string | 예 | **`product` \| `ops`만** 허용 |
| `capabilities` | string[] | 아니오 | 생략 시 `[]`. open set |
| `prohibited` | string[] | **예** | 누락/`null` 무효. 금지 없음은 명시적 `[]`만 |

**role**

| 값 | 의미 | 온보딩 기본 `prohibited` |
| --- | --- | --- |
| `product` | 워커가 조율하는 코드 저장소 | `["direct-main-commit", "force-push"]` |
| `ops` | 워크스페이스 거버넌스/ops 저장소 | `["force-push"]` |

**capabilities / prohibited (open set, documented floor)**

로더 상수:

- `KNOWN_CAPABILITIES`: `pr`, `dispatch-target`
- `KNOWN_PROHIBITIONS`: `direct-main-commit`, `force-push`

추가 문자열을 넣어도 스키마 범프 없이 허용된다. floor 값 밖 토큰을 거부하지 않는다. 현재 CLI/가드가 실행 시 검사하는 축은 **`prohibited` 멤버십**이다.

### 예제 페이로드

```json
{
  "workspace_root_is_plain_folder": true,
  "workspace_root": "/absolute/path/to/workspace-root",
  "master_seat": "folder-workspace-of-workspace-root",
  "repositories": [
    {
      "name": "example-product",
      "path": "example-product",
      "remote": "https://github.com/example/example-product.git",
      "role": "product",
      "capabilities": ["pr", "dispatch-target"],
      "prohibited": ["direct-main-commit", "force-push"]
    },
    {
      "name": "example-ops",
      "path": "example-ops",
      "remote": "https://github.com/example/example-ops.git",
      "role": "ops",
      "capabilities": ["pr", "dispatch-target"],
      "prohibited": ["force-push"]
    }
  ]
}
```

## 경로 매칭 (`repository_for_path`)

유일 hit가 필요하다. 모호하면 `WorkspaceDescriptorError`.

| 후보 | 매칭 조건 |
| --- | --- |
| 선언 `path`와 완전 일치 | 상대 경로, 포함 `"."` |
| 선언 `name`과 완전 일치 | 후보에 path separator 없음 (`widget` ↔ name `widget`) |
| 절대 경로 | `workspace_root`가 바인딩된 경우에만; 루트 하위 relative remainder가 선언 path와 일치 |
| 실패 | `workspace_root` 없는 절대 경로; 루트 밖 절대 경로; 비정확 multi-segment 상대 (`other/app` ↛ `services/app`) |

basename만으로 다른 parent 아래 동일 이름을 훔치지 않는다.

## 금지 판정 (`is_prohibited`)

```text
action_key = strip(action)   # 빈 문자열이면 에러
repo = repository_for_path(path)
if repo is None:
    return default_when_unknown_repo   # 기본 True = fail closed
return action_key in repo.prohibited
```

| 상황 | 기본 동작 |
| --- | --- |
| 매칭된 repo, action ∈ `prohibited` | prohibited |
| 매칭된 repo, action ∉ `prohibited` | allowed |
| 미매칭 경로 | fail closed (`default_when_unknown_repo=True`) |
| CLI `--allow-unknown-repo` | 미매칭 시 allowed (`default_when_unknown_repo=False`) |
| descriptor unconfigured | 판정 거부 (에러 / CLI exit 2) |

## CLI: `workspace-descriptor-check`

```console
$ scripts/workspace-descriptor-check \
    --path <repository-path> \
    --action <action-name> \
    [--config <descriptor.json>] \
    [--allow-unknown-repo] \
    [--json]
```

### 플래그

| 플래그 | 필수 | 설명 |
| --- | --- | --- |
| `--path` | 예 | 조회 대상 저장소 경로(절대 또는 루트 상대) |
| `--action` | 예 | 검사할 액션 (예: `direct-main-commit`, `force-push`) |
| `--config` | 아니오 | 명시 descriptor 경로 (env·기본 경로 무시) |
| `--allow-unknown-repo` | 아니오 | 미매칭 경로를 allowed로 처리 (기본은 fail closed) |
| `--json` | 아니오 | 기계 판독 decision object를 stdout에 출력 |

### Exit 코드

| 코드 | 의미 |
| --- | --- |
| `0` | ALLOWED — 매칭 repo의 `prohibited`에 action 없음 |
| `1` | PROHIBITED — action이 금지 목록에 있음 (또는 기본 fail-closed 미매칭) |
| `2` | undecidable — unconfigured, invalid JSON/UTF-8, 잘못된 스키마, 빈 action 등 |

### 출력

**텍스트 (기본)**

```text
PROHIBITED: action=direct-main-commit repo=app path=app
ALLOWED: action=pr repo=app path=app
```

**JSON (`--json`)**

성공 시:

```json
{
  "allow": false,
  "decidable": true,
  "path": "app",
  "action": "direct-main-commit",
  "prohibited": true,
  "repository": {
    "name": "app",
    "path": "app",
    "role": "product",
    "prohibited": ["direct-main-commit"]
  },
  "source_path": "/abs/path/to/workspace-descriptor.json"
}
```

판정 불가 시:

```json
{
  "allow": false,
  "decidable": false,
  "path": "app",
  "action": "direct-main-commit",
  "reason": "unconfigured_or_invalid",
  "message": "workspace descriptor is unconfigured"
}
```

### 운영 호출 예

워커 라우팅 전 product main 직접 커밋·force-push 측정:

```bash
"{{RUNTIME_ROOT}}/scripts/workspace-descriptor-check" \
  --path <repository-path> \
  --action direct-main-commit
```

exit `1` → 금지. exit `0` → 해당 repo 금지 목록에 없음. exit `2` → fail closed, 권한 발명 금지. 동일하게 `--action force-push`를 사용한다.

## 온보딩에서 쓰는 방법

Step 1 (`02-workspace-facts.md`)에서 `{{REPO_LIST}}` 확정 후:

<Steps>
  <Step title="예제 복사 (없을 때만)">
    `config/workspace-descriptor.json`이 없으면 example을 복사한다. filled 파일은 커밋하지 않는다.

```console
$ cd "{{RUNTIME_ROOT}}"
$ test -f config/workspace-descriptor.json \
  || cp config/workspace-descriptor.example.json config/workspace-descriptor.json
```
  </Step>
  <Step title="멤버별 측정·기록">
    각 확정 멤버에 `name`, 루트 상대 `path`, `git remote get-url origin`으로 측정한 `remote`, `role`, `capabilities`, `prohibited`를 쓴다. remote는 `{{WORKSPACE_ROOT}}/<path>` 기준이다.
  </Step>
  <Step title="워크스페이스 필드">
    `workspace_root_is_plain_folder: true`, `workspace_root` = 확인된 절대 `{{WORKSPACE_ROOT}}`, `master_seat` = 좌석 단계에서 쓸 seat form.
  </Step>
  <Step title="소유자 확인">
    초안을 읽어 확인한 뒤에만 final로 취급한다. 측정되지 않은 저장소를 발명하지 않는다.
  </Step>
</Steps>

**기본 값 (온보딩 권장, 소유자 수정 가능)**

| role | capabilities 기본 | prohibited 기본 |
| --- | --- | --- |
| `product` | `["pr", "dispatch-target"]` | `["direct-main-commit", "force-push"]` |
| `ops` | `["pr", "dispatch-target"]` | `["force-push"]` |

## 검증 실패 모드

| 조건 | 결과 |
| --- | --- |
| 파일 없음 | unconfigured (`source_path=None`) |
| 비 UTF-8 / 비 JSON / root ≠ object | `WorkspaceDescriptorError` |
| `workspace_root_is_plain_folder` ≠ true | 거부 |
| `role` ∉ {`product`,`ops`} | 거부 |
| `prohibited` 누락/`null` | 거부 (실수로 기본 금지가 사라지는 것 방지) |
| 중복 `path` 또는 중복 `name` | 거부 |
| path 다중 매칭 | 거부 |
| unconfigured에서 `action_is_prohibited` / CLI | 에러 / exit 2 |

## 아키텍처 위치

```mermaid
flowchart TB
  subgraph Onboarding["온보딩 Step 1"]
    RL["{{REPO_LIST}} 측정"]
    WD["config/workspace-descriptor.json"]
    RL --> WD
  end

  subgraph Resolve["해석 순서"]
    ENV["WORKSPACE_DESCRIPTOR / MOGUI_WORKSPACE_DESCRIPTOR"]
    FILE["config/workspace-descriptor.json"]
    UNC["unconfigured"]
    ENV --> FILE --> UNC
  end

  subgraph Consumers["소비자"]
    LDR["workspace_descriptor.py"]
    CLI["workspace-descriptor-check"]
    RT["worker routing / charter §4"]
  end

  WD --> Resolve
  Resolve --> LDR
  LDR --> CLI
  CLI --> RT
```

마스터 좌석 규칙(folder workspace vs primary worktree)과 selector 형식은 descriptor 스키마 밖이며 Orca 객체 모델 문서에 속한다. descriptor의 `master_seat`는 그 선택을 **기록**하는 필드다.

## 관련 모듈 API

| 심볼 | 용도 |
| --- | --- |
| `load_workspace_descriptor(path=None, *, repo_root=None, environ=None)` | 디스크 로드; 없으면 unconfigured |
| `resolve_config_path(explicit=None, *, repo_root=None, environ=None)` | 경로만 결정 |
| `action_is_prohibited(path, action, *, config_path=None, …)` | 로드 + 금지 검사; unconfigured면 raise |
| `WorkspaceDescriptor.repository_for_path` | 인벤토리 조회 |
| `WorkspaceDescriptor.is_prohibited` | 금지 멤버십 |
| `WorkspaceDescriptor.require_repositories` | 비어 있으면 raise |
| `WorkspaceDescriptorError` | 설정·스키마·매칭 실패 |

## Related pages

<CardGroup>
  <Card title="Orca 객체 모델" href="/orca-object-model">
    folder workspace, master 좌석, selector 형식, plain folder 라벨.
  </Card>
  <Card title="프로그레시브 온보딩" href="/onboarding">
    Step 1 workspace facts와 descriptor 착지 절차.
  </Card>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    디스패치 전 금지 액션 가드와 워커 라우팅 맥락.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    WORKSPACE_DESCRIPTOR·MOGUI_* 및 다른 인스턴스 설정 해석 순서.
  </Card>
  <Card title="CLI 레퍼런스" href="/cli-reference">
    공개 scripts/ 명령 표와 workspace-descriptor-check 요약.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    unconfigured·exit 2·placement 관련 복구 신호.
  </Card>
</CardGroup>

---

## 13. Redaction gates

> redaction-scan 범위·exit, REDACTION_REQUIRE_EXTRA, inventory 역검사, gitleaks 설정, 스테이징 전제와 pre-push 훅.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/13-redaction-gates.md
- Generated: 2026-08-07T07:06:29.517Z

### Source Files

- `scripts/redaction-scan.sh`
- `scripts/redaction-inventory`
- `scripts/redaction-allowlist.txt`
- `config/gitleaks.toml`
- `docs/internal/tooling/redaction-scan.md`
- `hooks/pre-push`
- `tests/test_redaction_scan_native_script.py`
- `SECURITY.md`

---
title: "Redaction gates"
description: "redaction-scan 범위·exit, REDACTION_REQUIRE_EXTRA, inventory 역검사, gitleaks 설정, 스테이징 전제와 pre-push 훅."
---

`scripts/redaction-scan.sh`는 gitleaks를 매칭 엔진으로 쓰는 fail-closed 게이트다. 스캔 범위를 tracked·staged·commit range로 한정하고, 커밋 메시지를 별도 루프로 읽으며, 조직 규칙(`REDACTION_EXTRA_PATTERNS`)을 런타임에 병합한 뒤 실제로 무엇을 읽었는지 한 줄로 선언한다. 역검사 도구 `scripts/redaction-inventory`와 선택 훅 `hooks/pre-push`가 같은 방어면을 보완한다.

## 구성 요소

| 경로 | 역할 |
| --- | --- |
| `scripts/redaction-scan.sh` | 범위 선택, org 규칙 번역, 엔진 자가검사, 파일·커밋 메시지 스캔, exit 계약 |
| `config/gitleaks.toml` | 커밋된 규칙 세트 (`useDefault = true` 확장) + 클래스 단위 allowlist |
| `scripts/redaction-inventory` | 규칙이 커버하지 않는 토큰 역검사 (blind-spot 좁히기) |
| `scripts/redaction-allowlist.txt` | 구 포맷 allowlist — **비어 있어야 함**. 항목이 있으면 exit 2 |
| `hooks/pre-push` | push 직전 range 스캔 (클론마다 opt-in) |
| `.github/workflows/gates.yml` | PR/main에서 커밋된 규칙만으로 scan 실행 |

<Note>
매칭 엔진은 gitleaks다. 스크립트는 gitleaks가 하지 않는 일을 한다: tracked 범위 강제, 커밋 메시지 스캔, org 규칙 로더, 커버리지 선언, fail-closed exit.
</Note>

## 명령과 모드

```bash
scripts/redaction-scan.sh                 # tracked 전체 (기본)
scripts/redaction-scan.sh --staged        # index / staged only
scripts/redaction-scan.sh --range A..B    # range로 변경된 파일 + 해당 커밋 메시지
scripts/redaction-scan.sh --commit-messages A..B
scripts/redaction-scan.sh --require-extra # 또는 REDACTION_REQUIRE_EXTRA=1
scripts/redaction-scan.sh -v|--verbose
scripts/redaction-scan.sh --help
```

| 모드 | 파일 열거 | 커밋 메시지 |
| --- | --- | --- |
| `tracked` (기본) | `git ls-files` | `not-scanned` |
| `staged` | `git diff --cached --name-only --diff-filter=ACMR` | `not-scanned` |
| `range` | `git diff --name-only --diff-filter=ACMR A..B` | range와 동일 구간 자동 스캔 |

`--range` 없이 `--commit-messages`만 주면 메시지 스캔을 임의의 모드에 추가할 수 있다. 파일은 경로당 한 번 `gitleaks dir`로 스캔한다(복수 경로 인자가 디렉터리 전체로 확장되며 untracked를 끌어들이는 것을 피함).

### 출력 계약 (scope 선언)

성공 시 stdout 한 줄이 범위를 명시한다.

```console
$ scripts/redaction-scan.sh
redaction-scan: OK — 0 findings (mode=tracked, files=144, commit-messages=not-scanned, org-rules=10)
```

| 필드 | 의미 |
| --- | --- |
| `mode` | `tracked` / `staged` / `range` |
| `files` | 실제로 연 파일 수 |
| `commit-messages` | 스캔한 메시지 수, 또는 `not-scanned` |
| `org-rules` | **로드되어 엔진에 들어간** 조직 규칙 수 (파일 줄 수가 아님) |

<Warning>
초록 결과가 “전체 감사 완료”를 의미하지 않는다. org 규칙이 없으면 stderr에 generic-only 경고가 뜨고, `org-rules=0`으로 끝난다. 공개 push/CI 게이트에서는 `REDACTION_REQUIRE_EXTRA=1`로 그 갭을 실패로 바꿔야 한다.
</Warning>

## Exit 코드

| 코드 | 의미 | 대표 원인 |
| --- | --- | --- |
| `0` | clean | 매칭 0건 |
| `1` | findings (fail-closed) | 면제되지 않은 시크릿/식별자 |
| `2` | cannot decide | `gitleaks` 없음, `config/gitleaks.toml` 없음, 필수 org 규칙 미로드, 구 allowlist 항목 잔존, range 미해석, RE2 비호환 병합 설정, 엔진 오류, 사용법 오류 |

판정 불가(2)와 발견(1)을 합치지 않는다. 엔진 실패를 clean으로 읽히게 두지 않기 위해 `gitleaks`에 `--exit-code 0`을 쓰고, 엔진 non-zero는 스크립트가 2로 승격한다.

## 조직 규칙 (`REDACTION_EXTRA_PATTERNS`)

공개 저장소이므로 회사·제품·개인 식별자 패턴은 커밋하지 않는다. 체크아웃마다 외부 파일로 공급한다.

```bash
cat > ~/.config/redaction-extra.txt <<'RULES'
company_acme|Company identifier acme|(?i)acme
personal_handle|Personal handle|(?i)myhandle
person_x_ko|Personal identifier native|가나다
RULES

export REDACTION_EXTRA_PATTERNS=~/.config/redaction-extra.txt
```

### 라인 형식

```text
id|description|regex
```

- 빈 줄과 `#` 주석 무시
- 구분자는 **앞 두 개의** `|`만 사용 (regex 안 `|` 허용)
- Python `re.compile`로 1차 검증 후 gitleaks RE2로 병합
- 형식 오류·컴파일 실패 줄은 건너뛰고 WARNING (`N of M organization rule lines are unusable`)

### 필수화

```bash
export REDACTION_REQUIRE_EXTRA=1
# 또는
scripts/redaction-scan.sh --require-extra
```

org 규칙이 0개면:

1. WARNING: generic patterns only  
2. `REQUIRE_EXTRA=1`이면 FAIL + exit **2**

### RE2 엔진 자가검사

병합 설정을 쓰기 전에 `gitleaks stdin` canary를 돌린다. lookaround 등 RE2 비호환 구문은 설정 로드 패닉 → 전 스캔 no-op → 거짓 초록이 된다. canary 실패 시 규칙 **id만** 나열하고 exit 2한다 (패턴 본문은 출력하지 않음).

### 네이티브 스크립트 경고

로드된 org 규칙에 ASCII 초과 문자가 하나도 없으면:

```text
WARNING — organization rules contain no native script pattern; romanization only identifier rules miss native spellings
```

리터럴 비ASCII만 인정한다. `\u…`, `\p{Hangul}` 같은 이스케이프/속성 클래스는 로더 또는 canary에서 걸러진다.

## `config/gitleaks.toml`

커밋된 베이스 설정. `[extend] useDefault = true`로 gitleaks 기본 시크릿 세트를 포함하고, 저장소 규칙을 추가한다.

| `id` | 대상 |
| --- | --- |
| `private_key` | PEM/OpenSSH private key 헤더 |
| `aws_access_key` | `AKIA…` |
| `github_token` | `gh[pousr]_…` |
| `slack_token` | `xox[baprs]-…` |
| `openai_sk` / `anthropic_key` | `sk-…` / `sk-ant-…` |
| `bearer_token` | `Bearer …` |
| `assignment_secret` / `dotenv_export` | 하드코딩 할당·export |
| `home_path` | `/Users/<name>` (fixture 접두사 일부 allow) |
| `internal_ip` | RFC1918 |
| `jira_hf` | `HF-*` |
| `slack_url` / `internal_host` | Slack URL, `*.internal`/`*.corp`/`*.local` |

전역 allowlist는 placeholder 라인 regex(`example.com`, `YOUR_API_KEY`, `sk-test-` 등)와 경로 제외(`scripts/redaction-scan.sh`, `config/gitleaks.toml`, `__pycache__/`, `.orca/` 등)를 담는다.

### 면제 (exemption)

| 방법 | 용도 |
| --- | --- |
| `.gitleaksignore` fingerprint | 단일 finding |
| `config/gitleaks.toml` allowlist | 규칙/클래스 단위 |
| ~~`scripts/redaction-allowlist.txt`~~ | **퇴역**. 주석이 아닌 항목이 있으면 exit 2 |

진짜 시크릿은 allowlist 하지 말고 로테이션 후 히스토리에서 제거한다.

## 스테이징 전제

스캐너는 **tracked(또는 staged/range에 포함된) 파일만** 읽는다.

- 아직 `git add` 하지 않은 신규 파일은 보이지 않고, 결과만 보면 clean이다.
- 워크트리에만 있는 시크릿·식별자는 게이트를 통과할 수 있다.
- CONTRIBUTING 운영 순서: **먼저 stage, 그다음 scan**.

```bash
git add <paths>
scripts/redaction-scan.sh --staged
# 또는 push 전 full tracked
scripts/redaction-scan.sh --require-extra
```

SECURITY.md가 문서화한 알려진 한계와 동일하다: unstaged 신규 파일 비가시, inventory의 바이너리 무음 스킵.

## pre-push 훅

커밋된 훅: `hooks/pre-push`. husky/lefthook 없음. 클론마다 한 번 켠다.

```bash
git config core.hooksPath hooks
```

동작 요약:

1. stdin의 각 push ref에 대해 local/remote SHA를 읽는다.
2. remote 삭제(`local_sha` = zero)는 스킵.
3. 가능하면 base를 잡고 `scripts/redaction-scan.sh --range "$base..$local_sha"` 실행 (중간 커밋·커밋 메시지 포함 — tip-only 스캔이 놓치는 형태를 막기 위함).
4. range를 하나도 못 잡으면 tracked 전체 스캔으로 fall back (silent pass 방지).
5. 호출자 env를 그대로 사용: `REDACTION_EXTRA_PATTERNS` / `REDACTION_REQUIRE_EXTRA`가 있으면 적용. **훅 자체가 org 규칙을 강제하지 않는다** (공개 저장소에 커밋된 훅이 비공개 규칙을 요구할 수 없음).
6. 테스트 스위트는 훅에 넣지 않는다 (느린 훅 → `--no-verify` 우회).

## redaction-inventory (역검사)

`redaction-scan`이 “규칙이 이름 붙인 것”을 찾는다면, inventory는 **규칙이 전혀 건드리지 않는 토큰**을 보고한다. 빈 결과 ≠ 안전 증명. blind-spot을 좁힐 뿐이다.

```bash
REDACTION_EXTRA_PATTERNS=~/.config/redaction-extra.txt scripts/redaction-inventory
scripts/redaction-inventory --baseline .redaction-inventory-baseline
scripts/redaction-inventory --json
```

| 항목 | 값 |
| --- | --- |
| 범위 | 현재 커밋의 tracked 파일 (+ 파일명 결합) |
| 필수 env | `REDACTION_EXTRA_PATTERNS` (없으면 exit 2) |
| 후보 종류 | kebab-case, 한글 인접 Latin 단어, `/Users/` 사용자명, 이메일 도메인 |
| baseline | 기본 `.redaction-inventory-baseline` — 검토 완료 토큰 제외 |
| 바이너리 | 앞 8KB에 NUL이면 스킵 (출력에 “skip” 고지 없음) |

| Exit | 의미 |
| --- | --- |
| `0` | uncovered 후보 0 |
| `1` | 후보 발견 (시크릿 판정이 아님) |
| `2` | 규칙 파일 없음/비파싱, git 아님, tracked 파일 0 |

한국어 인명은 자동 탐지하지 않는다. org 규칙에 `person_*` 등으로 직접 넣는다.

## 스캔이 보지 않는 것

저장소 밖 표면은 범위 밖이다.

- PR 제목/본문, 리뷰 코멘트, 이슈, 릴리스 노트
- untracked / unstaged 신규 파일
- inventory 기준 바이너리 파일 내용

외부로 나가는 산문은 게시 전 별도 grep이 필요하다.

## CI

`.github/workflows/gates.yml`의 `redaction` job:

- `./scripts/redaction-scan.sh` — **커밋된 규칙만** (org 파일은 저장소에 없음)
- inventory는 워크플로에서 비차단일 수 있음; 전체 org 스캔은 로컬 pre-push 책임

gitleaks는 워크플로에서 버전 핀·체크섬 검증 후 설치한다. 테스트 job에도 gitleaks를 깔아 `redaction-scan.sh` 실측 테스트를 돌린다.

## 운영 체크리스트

<Steps>
  <Step title="도구 준비">
    `gitleaks`, `bash`, `git`, `python3`를 PATH에 둔다. macOS 예: `brew install gitleaks`.
  </Step>
  <Step title="조직 규칙 파일">
    `REDACTION_EXTRA_PATTERNS`를 VCS 밖 파일로 설정한다. 로마자 + 네이티브 표기를 같이 넣는다.
  </Step>
  <Step title="스테이징 후 스캔">
    변경을 stage한 뒤 `scripts/redaction-scan.sh --staged` 또는 full tracked + `--require-extra`.
  </Step>
  <Step title="pre-push 활성화">
    `git config core.hooksPath hooks`
  </Step>
  <Step title="역검사">
    `scripts/redaction-inventory`로 규칙 blind-spot 후보를 고르고, 필요한 것만 org 규칙 또는 baseline에 반영한다.
  </Step>
</Steps>

### 실패 대응

| 증상 | 조치 |
| --- | --- |
| `gitleaks is not on PATH` (exit 2) | gitleaks 설치 |
| `required organization rules were not loaded` | `REDACTION_EXTRA_PATTERNS` 경로·내용 확인 |
| `retired format` allowlist | `.gitleaksignore` 또는 `config/gitleaks.toml`로 이전 후 allowlist 파일 비우기 |
| `not supported by RE2` | lookaround 등 제거 후 규칙 재작성 |
| `range does not resolve` | 로컬에 없는 SHA/range — fetch 또는 base 재설정 |
| finding (exit 1) | 제거·로테이션, 또는 의도적 fixture만 fingerprint 면제 |
| 초록인데 unstaged 파일에 시크릿 | stage 후 재스캔 (스코프 한계) |

## Related pages

<CardGroup>
  <Card title="방어 인벤토리" href="/defense-inventory">
    redaction scan·inventory를 포함한 런타임 가드 표
  </Card>
  <Card title="Contributing" href="/contributing">
    tracked-only 전제, hooksPath, pytest, exit 규약
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    preflight·gate 실패 복구 프로브
  </Card>
  <Card title="CLI 레퍼런스" href="/cli-reference">
    scripts/ 공개 명령 표와 옵션 경계
  </Card>
</CardGroup>

---

## 14. CLI 레퍼런스

> scripts/ 공개 명령 표, 하위 명령 목적, 주요 옵션, --help 동기화 테스트 계약과 비공개 표면 경계.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/14-cli.md
- Generated: 2026-08-07T07:05:18.307Z

### Source Files

- `docs/public/reference.md`
- `tests/test_reference_command_table.py`
- `scripts/dispatch-gate`
- `scripts/master-succeed`
- `scripts/acceptance-loop`
- `scripts/adapter`
- `scripts/worker-reap`
- `scripts/l1-digest`

---
title: "CLI 레퍼런스"
description: "scripts/ 공개 명령 표, 하위 명령 목적, 주요 옵션, --help 동기화 테스트 계약과 비공개 표면 경계."
---

공개 CLI는 저장소 루트의 `scripts/` 아래 **실행 비트 있는 파일**이다. 각 엔트리는 `src/`를 자체 `sys.path`에 넣은 뒤 `master_runtime.core.*`를 호출하거나, bash로 측정을 수행한다. 네트워크 모듈·API 키 의존성은 없다. 명령 인벤토리는 `docs/public/reference.md` 표와 `tests/test_reference_command_table.py`가 `--help` 출력으로 동기화한다.

## 호출 모델

```console
$ scripts/<name> [--global-flags] <subcommand> [options]
$ scripts/<name> --help
```

| 규칙 | 동작 |
| --- | --- |
| 경로 | 저장소 루트에서 `scripts/...` 상대 경로로 실행. PATH 설치 패키지가 아님. |
| 런타임 | 대부분 `#!/usr/bin/env python3` (stdlib only). shell 스크립트: `onboarding-preflight.sh`, `redaction-scan.sh`, `next-version`, `codex-worker-pretrust`, `cursor-worker-pretrust`. |
| 소스 로딩 | Python 엔트리는 `REPO_ROOT/src`를 `sys.path`에 삽입. 별도 `PYTHONPATH` 불필요. |
| 하위 명령 | argparse subparsers. `--help` usage의 `{a,b,c}` 그룹이 공개 하위 명령 집합. |
| 하위 명령 없음 | 단일 명령으로 표에 한 행. |
| JSON | 여러 도구가 `--json`으로 기계 판독 출력을 지원. |
| Windows | `tests/windows_exec_surface.py`의 skip 마크; 실행 표면 동기화 테스트는 Unix 실행 비트를 전제로 함. |

:::files
scripts/                    # 공개 CLI 엔트리 (이 페이지 범위)
src/master_runtime/core/    # 라이브러리 구현 (직접 CLI 아님)
master-ops/scripts/         # 템플릿/인스턴스 운영 스크립트 (비공개 표면)
hooks/pre-push              # git hooksPath용; scripts/ 표 밖
docs/public/reference.md    # 손으로 쓴 명령 표 (동기화 대상)
tests/test_reference_command_table.py
:::

## 공개 명령 표

표 행 형식은 `| \`scripts/x\` | \`x sub\` | purpose | key options |`이며, 마커 `COMMAND TABLE` … `END COMMAND TABLE` 사이만 인벤토리 계약에 포함된다. 현재 측정 표면: **실행 파일 19개 · 명령 행 30개**(누락/스테이 0).

| Script | Command | Purpose | Key options |
| --- | --- | --- | --- |
| `scripts/acceptance-loop` | `acceptance-loop validate` | acceptance suite 구조 검증 | `--config` (required) |
| `scripts/acceptance-loop` | `acceptance-loop split` | suite를 visible / holdout으로 분할 | `--config`, `--output-dir` |
| `scripts/acceptance-loop` | `acceptance-loop run` | proposer에 대해 결정적 acceptance loop 실행 | `--config`, `--max-iterations`, `--baseline-ref`, `--restore-cmd` |
| `scripts/acceptance-loop` | `acceptance-loop inspect` | 실행 산출물 구성 보고(루프 미실행) | `--run-dir` (required) |
| `scripts/adapter` | `adapter doctor` | 어댑터 도구 가시성·로컬 의존성 존재 보고 | (플래그 없음; JSON stdout) |
| `scripts/codex-worker-pretrust` | `codex-worker-pretrust` | Orca-managed Codex account config에 worktree trust 기록 | positional 절대 worktree 경로; `--accounts-dir` |
| `scripts/cursor-worker-pretrust` | `cursor-worker-pretrust` | Cursor Agent trust storage에 worktree trust 기록 | positional 절대 worktree 경로; `--projects-dir` |
| `scripts/dispatch-gate` | `dispatch-gate check` | worker 계약 평가, allow/deny를 ledger에 기록 | 전역 `--ledger`; `--runtime`, `--model`, `--tier-policy`, `--tier-override`, `--no-record`, `--contract`, `--agents`, `--est-chars`, `--completion-channel` |
| `scripts/dispatch-gate` | `dispatch-gate register` | probe 성공 후 job 등록 | 전역 `--ledger`; `--job-id`, `--probe-cmd`, `--contract-sha`, `--runtime`, `--orchestration-task`, `--tier-policy`, `--declared-model`, `--model-probe-cmd` |
| `scripts/dispatch-gate` | `dispatch-gate watch` | worker 로그 stall 조건 검사 | 전역 `--ledger`; `--log`, `--max-idle` (default 360) |
| `scripts/dispatch-gate` | `dispatch-gate report` | ledger 집계(denial·override·tier·model) | 전역 `--ledger`; `--today` (UTC 당일) |
| `scripts/generate-manifest` | `generate-manifest` | Stage 1 skeleton walk → `master-ops/MANIFEST.json` | `--skeleton`, `--out`, `--check`, `--stdout` |
| `scripts/l1-digest` | `l1-digest tick` | 읽기 전용 L1 digest 1 tick | `--config` |
| `scripts/master-bootstrap` | `master-bootstrap` | charter·handoff·budget으로 bounded bootstrap 블록 생성 | `--charter`, `--handoff`, `--budget`, `--session-id`, `--strict-lease`, `--json` |
| `scripts/master-bootstrap-live` | `master-bootstrap-live` | live session-start bootstrap 블록 방출 | `--handoff-dir`, `--role-state-file`, `--budget`, `--bd`, `--charter-pointer` |
| `scripts/master-recover` | `master-recover` | recovery 입력 검사·리포트 | `--charter`, `--handoff`, `--ledger`, `--repo`, `--monitor-pattern`, `--session-id`, `--json` |
| `scripts/master-succeed` | `master-succeed detect` | succession trigger 텍스트·context pressure 분류 | `text`, `--context-ratio`, `--json` |
| `scripts/master-succeed` | `master-succeed handoff` | JSON spec에서 thin handoff 생성 | `--spec`, `--json` |
| `scripts/master-succeed` | `master-succeed verify-successor` | successor recovery report 검증 | `--report`, `--json` |
| `scripts/master-succeed` | `master-succeed check-duplicates` | marker 기준 중복 master 검출(self handle 제외) | `--self-handle`, `--marker`, `--json` |
| `scripts/master-succeed` | `master-succeed retire` | predecessor terminal/session 1개 resolve·optional close | `--self-handle`, `--expected`, `--target-*`, `--execute`, `--json` |
| `scripts/master-succeed` | `master-succeed spawn` | clean successor terminal spawn/dry-run | `--workspace-selector`, `--expected-placement`, kickoff, `--root`, `--model`, `--agent`, `--title`, `--dry-run`, `--json` |
| `scripts/model-identity-probe` | `model-identity-probe` | transcript assistant 이벤트에서 측정 model vs expect 비교 | `--transcript`, `--runtime`, `--config`, `--expect`, `--limit` |
| `scripts/model-drift-audit` | `model-drift-audit` | transcript 전 assistant turn 모델 전이 보고 | `--transcript`, `--session`, `--expect`, `--projects-dir`, `--workspace-dir`, `--ignore-synthetic`, `--json` |
| `scripts/next-version` | `next-version` | owner-managed MAJOR.MINOR + 파생 build count 출력 | `--help` only |
| `scripts/onboarding-preflight.sh` | `onboarding-preflight.sh` | 온보딩 전제 도구 측정; required 누락 시 block | `--fix`; env `PREFLIGHT_WAIVE` |
| `scripts/workspace-descriptor-check` | `workspace-descriptor-check` | path+action에 대한 descriptor 금지 여부 | `--path`, `--action`, `--config`, `--allow-unknown-repo`, `--json` |
| `scripts/redaction-inventory` | `redaction-inventory` | redaction rule이 덮지 않는 토큰(역검사) | `--baseline`, `--min-count`, `--json` |
| `scripts/redaction-scan.sh` | `redaction-scan.sh` | tracked 범위 gitleaks + 커밋 메시지 스캔 | default all tracked; `--staged`, `--range A..B`, `--commit-messages A..B` |
| `scripts/worker-reap` | `worker-reap` | settled worker terminal close + included worktree 정리 | `--task-id` \| `--dispatch-id`, `--ledger`, `--dry-run`, `--json` |

<Note>
`scripts/redaction-allowlist.txt`는 디렉터리에 남아 있으나 **실행 파일이 아니며** 명령 표에 포함되지 않는다. 허용 목록 형식은 폐기됨: 항목이 남아 있으면 `redaction-scan.sh`가 exit 2.
</Note>

## 도메인별 엔트리

### Dispatch · 등록 · 감시

`scripts/dispatch-gate` — 전역 `--ledger` (JSONL 경로).

| Subcommand | 필수 | 선택 | 성공/거부 exit (요약) |
| --- | --- | --- | --- |
| `check` | `--runtime`, `--contract`, `--agents` | `--model`, `--tier-policy`, `--tier-override`, `--no-record`, `--est-chars`, `--completion-channel {orchestration,sentinel-log}` | allow → 0; deny → 2 |
| `register` | `--job-id`, `--probe-cmd` | `--contract-sha`, `--runtime`, `--orchestration-task`, `--tier-policy`, `--declared-model`, `--model-probe-cmd` | decision JSON; orchestration 미검증 시 deny 경로 |
| `watch` | `--log` | `--max-idle` (default **360**) | stall 상태 보고 |
| `report` | — | `--today` | ledger 카운트 집계 |

<ParamField body="--no-record" type="flag">
`check`만. ledger 행 append·ticket 발급 없이 평가.
</ParamField>

<ParamField body="--completion-channel" type="orchestration | sentinel-log">
`check`에서 완료 채널 선언. `est-chars` 생략 시 channel이 있으면 contract 파일 길이로 추정.
</ParamField>

<ParamField body="--model-probe-cmd" type="string">
`register`에서 실제 worker model 측정. 프로브 timeout 상수: orchestration·model 각각 30초.
</ParamField>

<RequestExample>
```console
# Dispatch gate check (safe dry path with --no-record)
$ scripts/dispatch-gate --ledger ./gate-ledger.jsonl check \
  --runtime codex --model grok-4.5 --contract ./job-contract.md \
  --agents 1 --est-chars 1000 --completion-channel orchestration
```
</RequestExample>

세부 플래그·reason code·ledger 스키마는 [dispatch-gate 레퍼런스](/dispatch-gate-reference). 흐름 문맥은 [Supervised dispatch](/supervised-dispatch).

### Succession · recover · bootstrap

`scripts/master-succeed` 하위 명령: `detect`, `handoff`, `verify-successor`, `check-duplicates`, `retire`, `spawn`. 공통 `--json`.

| Subcommand | 핵심 입력 | 비고 |
| --- | --- | --- |
| `detect` | positional `text`; `--context-ratio` | pure 분류; 시도 안전 |
| `handoff` | `--spec` JSON 파일 | thin handoff 객체 |
| `verify-successor` | `--report` | recovery report 검증 |
| `check-duplicates` | `--self-handle`, `--marker` | 현재 핸들 제외 중복 |
| `retire` | `--self-handle`; optional targets; `--execute` | 기본은 resolve/preview; 실행은 `--execute` |
| `spawn` | `--workspace-selector`, `--root`, `--title`, kickoff one-of | `--expected-placement` 불일치 시 **exit 26** (`SPAWN_PLACEMENT_MISMATCH`) |

`spawn` 기본 model 맵(에이전트 소문자 키): `claude`/`claude-code` → `claude-fable-5`, `grok`/`grok-build` → `grok-4.5`, `codex` → `gpt-5.6-sol`. 맵에 없는 agent는 `--model` 필수(없으면 exit 2).

인접 단일 명령:

| Script | 역할 |
| --- | --- |
| `master-bootstrap` | offline bounded bootstrap (`--charter` 필수) |
| `master-bootstrap-live` | session-start 훅용 live 블록 (`--handoff-dir` 필수); 내부 오류 시 fallback 라인·exit 0 설계 |
| `master-recover` | 비정상 종료 후 charter+handoff recovery report |

세부 옵션·exit는 [master-succeed 레퍼런스](/succession-cli-reference). 루프 문맥은 [Clean succession](/succession), [마스터 라이프사이클](/master-lifecycle).

### Acceptance loop

```console
$ scripts/acceptance-loop validate --config suite.json
$ scripts/acceptance-loop split   --config suite.json --output-dir ./splits
$ scripts/acceptance-loop run     --config suite.json --max-iterations 3 \
    --baseline-ref HEAD --restore-cmd 'git checkout -- .'
$ scripts/acceptance-loop inspect --run-dir /path/to/run
```

| Subcommand | 필수 | 선택 |
| --- | --- | --- |
| `validate` | `--config` | — |
| `split` | `--config` | `--output-dir` |
| `run` | `--config` | `--max-iterations`, `--baseline-ref`, `--restore-cmd` |
| `inspect` | `--run-dir` | — |

config 로드 실패 → exit **2**. restore 타임아웃 상수 5분. 스위트·holdout 상세는 [acceptance-loop 레퍼런스](/acceptance-loop-reference).

### Model identity · drift

| Script | Exit 0 | Exit 1 | Exit 2 |
| --- | --- | --- | --- |
| `model-identity-probe` | expect 일치, 또는 expect 없이 정보만 | — | drift, undecidable, transcript 미설정 |
| `model-drift-audit` | 전이 없음 | 전이 또는 expectation 불일치 | undecidable |

`model-identity-probe` transcript 해석 순서 (`--transcript` 생략 시):

1. env `MOGUI_TRANSCRIPT_GLOB`
2. `config/instance-runtime.json` (`INSTANCE_RUNTIME_CONFIG`가 경로 override)
3. honest unconfigured (baked default path 없음) → exit 2

`model-drift-audit`의 `--projects-dir`은 호스트별 layout이라 기본값이 없다.

### Worker reap · pretrust · descriptor

**`worker-reap`**

| Flag | 의미 |
| --- | --- |
| `--task-id` \| `--dispatch-id` | 상호 배타, 하나 필수 |
| `--ledger` | reap 감사 레코드 append 경로 |
| `--dry-run` | close/remove 없이 계획만 |
| `--json` | compact JSON |

| Exit | 의미 |
| --- | --- |
| 0 | 성공 |
| 2 | task/dispatch 인자 누락 |
| 3 | dispatch not settled |
| 4 | dispatch JSON parse 실패 |
| 1 | 기타 실패 |

**Pretrust** (dispatch 전 trust prompt 회피): absolute worktree path 필수. `tomllib`/JSON 인터프리터 없으면 **설정 파일을 건드리지 않고** loud skip.

**`workspace-descriptor-check`**: exit 0 allowed, 1 prohibited, 2 unconfigured/invalid. config 해석: `--config` → `WORKSPACE_DESCRIPTOR` / `MOGUI_WORKSPACE_DESCRIPTOR` → `config/workspace-descriptor.json` → unconfigured.

### Redaction · preflight · release 유틸

| Script | Exit 0 | Exit 1 | Exit 2 |
| --- | --- | --- | --- |
| `redaction-scan.sh` | clean | findings (및 일부 도구/usage 오류 — 헤더 선언 예외) | cannot decide; `REDACTION_REQUIRE_EXTRA=1`인데 extra 비어 있음; retired allowlist 잔존 |
| `redaction-inventory` | uncovered 없음 | candidates 발견 | cannot decide |
| `onboarding-preflight.sh` | ready | blocked | — |
| `generate-manifest --check` | 일치 | drift | — |
| `next-version` | 버전 문자열 stdout | — | — |

`redaction-scan.sh` 조직 규칙: env `REDACTION_EXTRA_PATTERNS` 파일, 줄 형식 `id|description|regex`. 스테이징 전제: unstaged 신규 파일은 스캔 범위 밖.

### Adapter · L1 digest

```console
$ scripts/adapter doctor
# → {"results":[...],"present":[...],"missing":[...], ...}

$ scripts/l1-digest tick --config digest.json
```

`adapter doctor`는 결과 JSON만 출력하고 정상 시 0. `l1-digest tick`은 읽기 전용 관찰 1회.

## `--help` 동기화 계약

기계가 고정하는 것은 **인벤토리**뿐이다. purpose·key options 산문은 사람이 쓴다(`--help`에 purpose 문자열이 없음).

```text
measured_surface()  = scripts/ 실행 파일 × (--help 의 {sub,...} 또는 단일 이름)
documented_surface() = docs/public/reference.md 표 행 (Script, Command 열)
assert measured - documented == ∅   # missing rows
assert documented - measured == ∅   # stale rows
```

| 테스트 | 역할 |
| --- | --- |
| `test_reference_table_matches_the_script_surface` | 누락·스테이 행 실패 |
| `test_the_check_can_fail` | 비교가 항상 통과하지 않음을 보장(가상 gap 탐지) |

새 공개 명령을 추가할 때:

1. `scripts/`에 실행 비트 있는 엔트리 추가
2. `--help`가 subparsers면 usage에 `{...}` 노출
3. `docs/public/reference.md` 표에 행 추가
4. `PYTHONPATH=src python3 -m pytest tests/test_reference_command_table.py -q`

## 비공개 표면 경계

이 페이지·`docs/public/reference.md`가 다루는 것은 **`scripts/` 공개 명령만**이다.

| 경계 밖 | 이유 |
| --- | --- |
| `master-ops/scripts/*` (`dispatch`, `orca-wait`, `spawn-test`, hooks, …) | 온보딩이 복사하는 **템플릿** 운영 스크립트. 설치본마다 분기; 공개 인벤토리 계약 밖. |
| `src/master_runtime/**` | 라이브러리. CLI wrapper 없이 import 전제. |
| `hooks/pre-push` | `git config core.hooksPath hooks`로 활성화; `scripts/` 표 행 아님. |
| Host routing / sensitive-lane / private paths | 워크스페이스 로컬 정책. public docs가 명시적으로 제외. |
| Vendor-internal transcript layout | probe가 실패·undecidable로 **기록**할 뿐 계약 API가 아님. |

<Warning>
`master-ops/` 변경은 신규 설치에만 도달한다. 이미 복사된 operations repository는 자동 갱신되지 않는다.
</Warning>

## Exit 코드 규약 (공유 어휘 아님)

| 패턴 | 사용처 예 | 의미 |
| --- | --- | --- |
| 0 / 1 / 2 | redaction-inventory, model-drift-audit, workspace-descriptor-check | clean / finding / undecidable |
| 0 / 2 | model-identity-probe, dispatch-gate check deny | match·allow / drift·deny·undecidable 혼합 가능 |
| 0 / 1 | onboarding-preflight, generate-manifest --check | ready·일치 / blocked·drift |
| 26 | master-succeed spawn | `SPAWN_PLACEMENT_MISMATCH` |
| 0 / 2 / 3 / 4 / 1 | worker-reap | success / missing args / not settled / parse / other |

CONTRIBUTING 규칙: 새 failure path는 가능하면 **2 = could not decide**로 보내고, 처리되지 않은 예외(exit 1)가 finding처럼 보이지 않게 한다. `redaction-scan.sh`는 usage/tool 오류를 1로 접는 자체 예외를 헤더에 선언한다. **스크립트마다 행·`--help`·소스를 읽을 것.**

## 검증 신호

```console
# 인벤토리 동기화
$ PYTHONPATH=src python3 -m pytest tests/test_reference_command_table.py -q

# 단일 명령 help (동기화 테스트가 읽는 것과 동일)
$ scripts/dispatch-gate --help
$ scripts/master-succeed spawn --help

# 어댑터 존재 여부
$ scripts/adapter doctor
```

성공: reference 테스트 통과, `--help`가 표 하위 명령과 일치, doctor JSON에 기대 도구 `present`.

## Related pages

<CardGroup>
  <Card title="dispatch-gate 레퍼런스" href="/dispatch-gate-reference">
    check·register·watch·report 플래그, ledger, reason code, 티어 정책.
  </Card>
  <Card title="master-succeed 레퍼런스" href="/succession-cli-reference">
    succession 하위 명령, exit 26, JSON 출력.
  </Card>
  <Card title="acceptance-loop 레퍼런스" href="/acceptance-loop-reference">
    validate·split·run·inspect, holdout, baseline/restore.
  </Card>
  <Card title="Worker reap" href="/worker-reap">
    issued→reaped, settled 검증, dry-run, 거부 exit.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    INSTANCE_RUNTIME_CONFIG, DISPATCH_TIER_POLICY, WORKSPACE_DESCRIPTOR, MOGUI_* env.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    preflight BLOCKED, placement mismatch, MODEL_PROBE_FAILED, undecidable.
  </Card>
  <Card title="Contributing" href="/contributing">
    pytest, exit 규약, redaction 게이트, master-ops 템플릿 경계.
  </Card>
</CardGroup>

---

## 15. Configuration reference

> JSON 키·기본값·필수/선택, INSTANCE_RUNTIME_CONFIG·DISPATCH_TIER_POLICY·WORKSPACE_DESCRIPTOR·MOGUI_* 환경 변수 해석 순서.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/15-configuration-reference.md
- Generated: 2026-08-07T07:04:42.209Z

### Source Files

- `config/instance-runtime.example.json`
- `config/model-tier-policy.example.json`
- `config/workspace-descriptor.example.json`
- `src/master_runtime/core/instance_runtime_config.py`
- `src/master_runtime/core/workspace_descriptor.py`
- `src/master_runtime/core/dispatch_gate.py`
- `master-ops/MANIFEST.json`

---
title: "Configuration reference"
description: "JSON 키·기본값·필수/선택, INSTANCE_RUNTIME_CONFIG·DISPATCH_TIER_POLICY·WORKSPACE_DESCRIPTOR·MOGUI_* 환경 변수 해석 순서."
---

인스턴스 설정은 세 개의 JSON 표면과 경로·값 오버라이드 환경 변수로 구성된다. 로더는 값을 추측하지 않고 **환경 → 인스턴스 파일 → 정직한 unconfigured**(티어 정책만 템플릿 폴백) 순으로 해석한다. 저장소에는 `config/*.example.json`만 커밋되며, 온보딩이 `config/instance-runtime.json`, `config/model-tier-policy.json`, `config/workspace-descriptor.json`을 작성한다.

## 설정 표면 요약

| 표면 | 기본 경로 | 경로 env | 값 오버라이드 env | 파일 없을 때 |
|------|-----------|----------|-------------------|--------------|
| Instance runtime | `config/instance-runtime.json` | `INSTANCE_RUNTIME_CONFIG` | `MOGUI_MASTER_HOST_RUNTIME`, `MASTER_HOST_RUNTIME`, `MOGUI_TRANSCRIPT_GLOB`, `MOGUI_PRODUCT_REPO` | unconfigured (에러 아님) |
| Model tier policy | `config/model-tier-policy.json` → `master-ops/model-tier-policy.json` | `DISPATCH_TIER_POLICY` (및 CLI `--tier-policy`) | 없음 (파일 전체 교체) | 인스턴스 없으면 템플릿 사용 |
| Workspace descriptor | `config/workspace-descriptor.json` | `WORKSPACE_DESCRIPTOR`, `MOGUI_WORKSPACE_DESCRIPTOR` | 없음 (파일 전체 교체) | 빈 inventory / unconfigured |

<Note>
`_`로 시작하는 JSON 키(`_docs`, `_notes` 등)는 문서용이며 파서가 무시한다. 예제 파일의 `_docs`는 커밋된 스키마 설명이다.
</Note>

## 공통 해석 순서

```text
명시 CLI 경로 (있는 경우)
        │
        ▼
환경 변수 경로 오버라이드
  INSTANCE_RUNTIME_CONFIG
  DISPATCH_TIER_POLICY
  WORKSPACE_DESCRIPTOR / MOGUI_WORKSPACE_DESCRIPTOR
        │
        ▼
인스턴스 파일 (config/*.json)
        │
        ├─ instance-runtime / workspace-descriptor
        │     → 값 env 오버라이드 (해당 시)
        │     → 없으면 unconfigured (require_* 가 실패)
        │
        └─ model-tier-policy
              → 인스턴스 파일 없음 → master-ops/model-tier-policy.json
```

원칙:

1. **경로 선택**과 **필드 값 선택**은 별층이다. 경로 env가 파일을 고르고, 값 env가 필드 단위로 덮는다.
2. Instance facts(`master_host_runtime`, transcript glob, `product_repo`)는 베이크된 기본값을 쓰지 않는다.
3. Tier policy만 측정된 템플릿 폴백(`master-ops/model-tier-policy.json`)을 허용한다.

## 파일 레이아웃

:::files
config/
  instance-runtime.example.json      # 템플릿 (커밋)
  model-tier-policy.example.json     # 템플릿 (커밋)
  workspace-descriptor.example.json  # 템플릿 (커밋)
  gitleaks.toml                      # 공개 redaction 엔진 규칙
  instance-runtime.json              # 인스턴스 전용 (온보딩 작성, 보통 비커밋)
  model-tier-policy.json             # 인스턴스 전용
  workspace-descriptor.json          # 인스턴스 전용
master-ops/
  model-tier-policy.json             # 게이트 템플릿 폴백 (version 2)
  MANIFEST.json                      # master-ops 템플릿 파일 목록
:::

## Instance runtime (`config/instance-runtime.json`)

로더: `load_instance_runtime_config` → `InstanceRuntimeConfig`.  
기본 상대 경로: `config/instance-runtime.json`. 모듈 위치 기준으로 repo root를 찾으므로 cwd와 무관하다.

### 경로 해석

1. 호출자가 넘긴 명시 `path`
2. `INSTANCE_RUNTIME_CONFIG` (비어 있지 않은 문자열; `~` 확장)
3. `<repo>/config/instance-runtime.json`

파일이 없어도 로드는 성공한다. `source_path`는 실제 파일이 있을 때만 설정된다.

### JSON 필드

<ParamField body="master_host_runtime" type="string | null" required={false}>
마스터 세션이 돌아가는 에이전트 CLI 이름(예: `claude`, `codex`, `grok`). 공백 문자열은 unset. 잘못된 타입(비-string)은 `InstanceRuntimeConfigError`.
</ParamField>

<ParamField body="transcript_globs" type="object" required={false}>
런타임 이름 → 세션 JSONL glob 맵. 모델 identity probe가 transcript 위치를 찾을 때 사용. 키·값 모두 비어 있지 않은 문자열. `_` 접두 키는 무시.
</ParamField>

<ParamField body="product_repo" type="string | null" required={false}>
이 마스터가 서비스하는 primary product 저장소 절대 경로. 단일 primary가 없으면 omit/`null`.
</ParamField>

### 값 해석 순서 (필드별)

| 필드 | 1순위 | 2순위 | 3순위 |
|------|--------|--------|--------|
| `master_host_runtime` | `MOGUI_MASTER_HOST_RUNTIME` | `MASTER_HOST_RUNTIME` | 파일 |
| `product_repo` | `MOGUI_PRODUCT_REPO` | 파일 | unconfigured |
| transcript glob | `MOGUI_TRANSCRIPT_GLOB` (런타임 무관 단일 오버라이드) | `transcript_globs.<runtime>` | unconfigured |

`require_master_host_runtime()`, `require_transcript_glob(runtime?)`, `require_product_repo()`는 값이 없으면 `InstanceRuntimeConfigError`를 발생시킨다. CLI 소비자는 보통 exit 2.

### 예제 골격

```json
{
  "master_host_runtime": "claude",
  "transcript_globs": {
    "claude": "~/.claude/projects/-Users-example-workspace/*.jsonl",
    "codex": "~/.codex/sessions/**/*.jsonl",
    "grok": "~/.grok/sessions/**/*.jsonl"
  },
  "product_repo": "/absolute/path/to/primary-product-repo"
}
```

### 관련 CLI

| 명령 | 설정 사용 |
|------|-----------|
| `scripts/model-identity-probe` | `--transcript` 생략 시: 명시 → `MOGUI_TRANSCRIPT_GLOB` → 파일 → unconfigured. `--config`로 경로 지정 가능 |

## Model tier policy (`config/model-tier-policy.json`)

디스패치 게이트(`DispatchGate`)가 worker 모델 티어와 rolling fan-out 캡을 적용한다. 인스턴스 파일은 온보딩에서 agent-inventory 동의 후 작성한다. 예제: `config/model-tier-policy.example.json`. 템플릿 폴백: `master-ops/model-tier-policy.json`.

### 경로 해석

1. CLI `--tier-policy` (호출자가 `DispatchGateConfig.tier_policy_path` 설정)
2. `DISPATCH_TIER_POLICY`
3. 존재하면 `<repo>/config/model-tier-policy.json`
4. `<repo>/master-ops/model-tier-policy.json`

### Version 2 (권장, 게이트 소비 필드)

| 키 | 필수 | 기본 | 규칙 |
|----|------|------|------|
| `version` | 예 | — | 반드시 `2` |
| `tiers` | 예 | — | 비어 있지 않은 object. 티어 이름 → model id 배열. model id는 casefold 후 멤버십. 한 model은 한 티어만. 티어 이름 `unknown`은 예약 |
| `fanout_caps` | 예 (object) | — | 티어 → 비음수 정수. **키가 없으면 uncapped** (`top`·`unknown` 포함). 존재하지 않는 티어 이름 금지 (`unknown`만 예외로 허용) |
| `window_seconds` | 아니오 | `86400` | 양의 정수. rolling window(초) |
| `agents` | 아니오 | — | 게이트 무시. 측정 인벤토리 문서용 |
| `consent` | 아니오 | 예제 `"no"` | 게이트 무시. `yes` / `no` / `manual-only` 기록용 |

미등재 model id → 예약 티어 `unknown`. `cap_for(tier)`가 `None`이면 해당 티어 캡 없음. Owner 지시(2026-08-05): top 티어 fan-out 캡은 제거하고, top 디스패치는 `master-ops/scripts/dispatch --top-approved`로 소유자 승인.

### Version 1 (레거시)

| 키 | 필수 | 규칙 |
|----|------|------|
| `version` | 예 | `1` |
| `worker_allowed` | 예 | model id 배열 |
| `worker_denied_tiers` | 예 | model id 배열; allowed와 겹치면 로드 실패 |
| `unknown_model` | 예 | `"deny"` 또는 `"warn"` |

버전 1 동작은 유지되어, 런타임만 올려도 기존 설치의 판정이 바뀌지 않는다.

### 예제 (인스턴스 시작점)

```json
{
  "version": 2,
  "consent": "no",
  "agents": [
    { "runtime": "claude", "version": "unknown", "model_ids": ["unknown"] }
  ],
  "tiers": {
    "top": [],
    "efficient": []
  },
  "fanout_caps": {
    "unknown": 8
  },
  "window_seconds": 86400
}
```

model id를 추측하지 않는다. 측정 불가 필드는 문자열 `unknown`을 쓴다.

### 게이트 기본값 (정책 파일 밖)

| 설정 | 기본 | env / 비고 |
|------|------|------------|
| Ledger path | `.dispatch-gate-ledger.jsonl` | `DISPATCH_GATE_LEDGER` |
| Ticket dir | `~/.mogui/dispatch-tickets` | config 필드 |
| Known roots | `~/.mogui/known-roots.json` | config 필드 |
| Single dispatch char limit | `500000` | |
| Batch dispatch char limit | `1000000` | |
| Duplicate window | `1800` 초 | |
| Ticket TTL | `600` 초 | |
| Expired ticket GC grace | `86400` 초 | |
| High-cost runtimes | `{"fable"}` | |

정책 로드 실패 시 reason `TIER_POLICY_UNAVAILABLE`. 캡 초과 시 `TIER_FANOUT_CAP`. 결정은 ledger에 `tier_policy_path`와 `tier_policy_sha256`을 남긴다.

## Workspace descriptor (`config/workspace-descriptor.json`)

로더: `load_workspace_descriptor` → `WorkspaceDescriptor`. 워크스페이스 루트는 **sibling 저장소가 있는 plain folder**이며 submodule parent가 아니다.

### 경로 해석

1. 명시 `path`
2. `WORKSPACE_DESCRIPTOR`
3. `MOGUI_WORKSPACE_DESCRIPTOR`
4. `<repo>/config/workspace-descriptor.json`

파일 없음 → `repositories=()`, `source_path=None`, `master_seat=""`, `workspace_root_is_plain_folder=True`.  
`action_is_prohibited` 헬퍼는 파일이 없으면 `WorkspaceDescriptorError`(unconfigured).

### 루트 필드

<ParamField body="workspace_root_is_plain_folder" type="boolean" required={false}>
기본 `true`. `true`가 아니면 로드 실패. submodule parent 거부.
</ParamField>

<ParamField body="workspace_root" type="string | null" required={false}>
워크스페이스 루트 절대 경로. 절대 경로 매칭에 필요. omit/`null`/빈 문자열이면 절대 후보 매칭 불가.
</ParamField>

<ParamField body="master_seat" type="string" required={false}>
마스터 세션 좌석 설명/selector 메모. 타입은 string; 기본 `""`.
</ParamField>

<ParamField body="repositories" type="array" required={false}>
멤버 저장소 인벤토리. omit 시 빈 목록. `require_repositories()`는 비어 있으면 오류.
</ParamField>

### `repositories[]` 항목

| 키 | 필수 | 규칙 |
|----|------|------|
| `name` | 예 | 비어 있지 않은 string. 인벤토리 내 유일 |
| `path` | 예 | 워크스페이스 루트 상대 경로. 단일 저장소면 `"."` 허용. 경로 유일 |
| `remote` | 아니오 | string; 기본 `""` |
| `role` | 예 | `product` 또는 `ops`만 |
| `capabilities` | 아니오 | string 배열; omit → `()`. open set. 문서화된 바닥: `pr`, `dispatch-target` |
| `prohibited` | **예** | 반드시 배열 존재. `null`/누락 무효. 소유자가 금지 없음으로 확인한 경우만 `[]`. open set. 문서화된 바닥: `direct-main-commit`, `force-push` |

### 경로 매칭

`repository_for_path`:

- 선언 `path` 정확 일치 (`.` 포함)
- 경로 구분자가 없는 후보는 `name` 정확 일치
- 절대 경로: `workspace_root` 하위일 때만 상대 나머지로 매칭
- 복수 매칭 → `WorkspaceDescriptorError`
- 미매칭 + `is_prohibited(..., default_when_unknown_repo=True)` → fail-closed (금지 취급)

### 예제 골격

```json
{
  "workspace_root_is_plain_folder": true,
  "workspace_root": "/absolute/path/to/workspace-root",
  "master_seat": "folder-workspace-of-workspace-root",
  "repositories": [
    {
      "name": "example-product",
      "path": "example-product",
      "remote": "https://github.com/example/example-product.git",
      "role": "product",
      "capabilities": ["pr", "dispatch-target"],
      "prohibited": ["direct-main-commit", "force-push"]
    }
  ]
}
```

### 관련 CLI

| 명령 | exit |
|------|------|
| `scripts/workspace-descriptor-check --path … --action …` | 0 허용, 1 금지, 2 unconfigured/invalid |

## 환경 변수 카탈로그

### 경로 선택

| 변수 | 대상 |
|------|------|
| `INSTANCE_RUNTIME_CONFIG` | instance-runtime JSON 경로 |
| `DISPATCH_TIER_POLICY` | model-tier-policy JSON 경로 |
| `WORKSPACE_DESCRIPTOR` | workspace-descriptor JSON 경로 (우선) |
| `MOGUI_WORKSPACE_DESCRIPTOR` | workspace-descriptor 대체 경로 |
| `DISPATCH_GATE_LEDGER` | dispatch gate ledger JSONL 경로 |

### Instance 값 오버라이드 (`MOGUI_*` 및 별칭)

| 변수 | 효과 |
|------|------|
| `MOGUI_MASTER_HOST_RUNTIME` | `master_host_runtime` 최우선 |
| `MASTER_HOST_RUNTIME` | 위 변수가 없을 때 대체 |
| `MOGUI_TRANSCRIPT_GLOB` | 활성 런타임 transcript glob 단일 오버라이드 |
| `MOGUI_PRODUCT_REPO` | `product_repo` |

### Redaction (인접 설정)

| 변수 | 효과 |
|------|------|
| `REDACTION_EXTRA_PATTERNS` | org 규칙 파일 (`id\|description\|regex` 라인). 기본 preflight 후보: `~/.config/redaction-extra.txt` |
| `REDACTION_REQUIRE_EXTRA` | `1`이면 extra 규칙 없거나 비어 있으면 exit 2 |
| `REDACTION_ALLOWLIST` | 레거시; 항목이 남아 있으면 scan exit 2 |
| `GITLEAKS_CONFIG` | gitleaks 설정 경로 (공개 규칙 밖 org 패턴용; 저장소 `config/gitleaks.toml`은 extend defaults) |

## 오류·검증 신호

| 증상 | 원인 | 신호 |
|------|------|------|
| `master_host_runtime is unconfigured` | env·파일 모두 없음 | `InstanceRuntimeConfigError` / probe exit 2 |
| `transcript_glob for runtime '…' is unconfigured` | `MOGUI_TRANSCRIPT_GLOB` 없고 map 키 없음 | 동일 |
| `instance runtime config is not valid JSON` | 손상된 파일 | 로드 즉시 실패 |
| `workspace descriptor is unconfigured` | 파일·경로 env 없음 | `WorkspaceDescriptorError` / check exit 2 |
| `repositories[i].prohibited must be present` | prohibited 누락 | 파싱 실패 |
| `workspace_root_is_plain_folder must be true` | false 또는 non-bool | 파싱 실패 |
| `duplicate repository path/name` | 인벤토리 중복 identity | 파싱 실패 |
| `tier policy version must be 1 or 2` | 잘못된 version | 게이트 `TIER_POLICY_UNAVAILABLE` |
| `fanout_caps names a tier that does not exist` | 캡 키 오타 | 로드 실패 |
| `a model may not appear in two tiers` | 교차 등재 | 로드 실패 |

## 온보딩·커밋 경계

- 템플릿 저장소는 `*.example.json`만 공개 스키마로 유지한다.
- 채워진 `config/instance-runtime.json` / `model-tier-policy.json` / `workspace-descriptor.json`은 인스턴스 소유이며, 예제를 git에서 덮어쓰지 않는다.
- `master-ops/MANIFEST.json`의 `template_version`과 파일 목록은 master-ops 배포 표면이며, `config/` 인스턴스 파일과 분리된다.
- model id·transcript path·product path는 측정 또는 소유자 명명으로만 채운다.

## Related pages

<CardGroup cols={2}>
  <Card title="인스턴스 설정" href="/configure-instance">
    instance-runtime·model-tier-policy 작성 절차, transcript glob, 티어×fan-out 캡 운영.
  </Card>
  <Card title="Workspace descriptor" href="/workspace-descriptor">
    sibling 인벤토리, role·capabilities·prohibited, master_seat, check 액션.
  </Card>
  <Card title="dispatch-gate 레퍼런스" href="/dispatch-gate-reference">
    check·register 플래그, ledger, reason code, 티켓 TTL.
  </Card>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    check → dispatch → register와 계약 해시·model probe.
  </Card>
  <Card title="Redaction gates" href="/redaction-gates">
    REDACTION_*·gitleaks·pre-push 게이트.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    unconfigured, MODEL_PROBE_FAILED, placement·preflight 복구.
  </Card>
</CardGroup>

---

## 16. dispatch-gate 레퍼런스

> check·register·watch·report 플래그, ledger 스키마, reason code, 티어 정책 해석, 티켓 TTL, 기본 문자 한도.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/16-dispatch-gate.md
- Generated: 2026-08-07T07:05:09.938Z

### Source Files

- `scripts/dispatch-gate`
- `src/master_runtime/core/dispatch_gate.py`
- `master-ops/docs/charter/05-dispatch-gate.md`
- `config/model-tier-policy.example.json`
- `tests/test_dispatch_gate.py`
- `tests/test_documented_reason_codes.py`
- `docs/public/reference.md`

---
title: "dispatch-gate 레퍼런스"
description: "check·register·watch·report 플래그, ledger 스키마, reason code, 티어 정책 해석, 티켓 TTL, 기본 문자 한도."
---

`scripts/dispatch-gate`는 supervised worker 디스패치를 기계적으로 게이트하는 공개 CLI다. 코어 로직은 `src/master_runtime/core/dispatch_gate.py`의 `DispatchGate`이며, 서브커맨드 `check` → (디스패치) → `register` 순서를 전제로 하고, 판정·등록·stall 감시·ledger 집계를 JSONL ledger와 on-disk ticket으로 기록한다.

## 공개 표면

| 항목 | 값 |
| --- | --- |
| CLI | `scripts/dispatch-gate` |
| 코어 | `src/master_runtime/core/dispatch_gate.py` |
| 전역 옵션 | `--ledger PATH` (JSONL ledger 경로) |
| 서브커맨드 | `check`, `register`, `watch`, `report` |
| 판정 stdout | compact JSON (sort_keys, separators `,` `:`) |
| 진단 stderr | `dispatch-gate: <message>` 접두사 |
| 기본 ledger | env `DISPATCH_GATE_LEDGER` → 없으면 `.dispatch-gate-ledger.jsonl` (cwd 상대) |
| 운영 관례 ledger | `~/.mogui/dispatch-ledger.jsonl` (`master-ops/scripts/dispatch`가 `--ledger`로 명시) |

<Warning>
stdout(JSON 판정)과 stderr(진단)를 `2>&1`로 합치면 JSON 파서가 실패한다. 기계 소비는 `2>/dev/null` 또는 스트림 분리. 거부 사유는 stderr에 있다.
</Warning>

### Exit 코드

| 서브커맨드 | 0 | 2 | 3 |
| --- | --- | --- | --- |
| `check` | `allow=true` | `allow=false` | — |
| `register` | `allow=true` | `allow=false` 또는 orchestration 미검증 거부 | — |
| `watch` | stall 아님 (`OK`) | 로그 없음 (`MISSING`) | stall (`STALLED`) |
| `report` | 성공 | ledger 읽기/파싱 실패 (`REPORT_UNAVAILABLE`) | — |
| (parser) | — | 알 수 없는 커맨드 | — |

## 워크플로

```text
check  →  supervised dispatch (Orca orchestration)  →  register
         [--no-record 로 dry-run 시 ticket/ledger 미기록]
```

<Steps>
  <Step title="check">
    계약·런타임·모델·에이전트 수·완료 채널을 평가한다. 허용이면 ledger에 `ALLOW`를 append하고 ticket을 발급한다. `--no-record`면 평가만 하고 행·ticket을 쓰지 않는다.
  </Step>
  <Step title="dispatch">
    Orca orchestration으로 Run/Task/worker를 붙인다. 게이트 자체는 스폰하지 않는다. 상위 래퍼 `master-ops/scripts/dispatch`가 top-tier에 대해 `--top-approved` 절차를 추가로 강제한다.
  </Step>
  <Step title="register">
    선행 성공 `check`의 pending 행·ticket과 매칭한다. `--probe-cmd`가 exit 0이고 stdout에 `--job-id`가 포함되어야 한다. orchestration 채널이면 `--orchestration-task`와 `orca orchestration dispatch-show` 검증이 필요하다.
  </Step>
</Steps>

## 서브커맨드와 플래그

### 전역

<ParamField body="--ledger" type="path">
JSONL ledger 경로. 생략 시 `DISPATCH_GATE_LEDGER` 또는 `.dispatch-gate-ledger.jsonl`.
</ParamField>

### `check`

필수: `--runtime`, `--contract`, `--agents`.

<ParamField body="--runtime" type="string" required>
워커 런타임 id. 패턴 `^[a-z0-9][a-z0-9_-]{0,31}$` (검증 시 대소문자 허용 후 lower 비교/저장 경로에 사용).
</ParamField>

<ParamField body="--contract" type="path" required>
계약 파일 경로. 내용은 SHA-256으로 해시되어 `contract_sha`가 된다. 읽기 실패 시 `CONTRACT_UNREADABLE`.
</ParamField>

<ParamField body="--agents" type="int" required>
디스패치 에이전트 수. `cost_proxy = n_agents * est_chars`. 1 미만이면 `INVALID_REQUEST`.
</ParamField>

<ParamField body="--model" type="string">
선언 모델 id. 비어 있거나 공백 패딩이면 `NO_MODEL` / `INVALID_REQUEST`. casefold로 티어 매칭.
</ParamField>

<ParamField body="--completion-channel" type="enum">
`orchestration` \| `sentinel-log`. 없거나 다른 값이면 `NO_COMPLETION_CHANNEL`.
</ParamField>

<ParamField body="--est-chars" type="int">
추정 입력 문자 수. 생략 시: 채널이 있으면 계약 파일 길이, 채널이 없으면 `0`. 계약을 읽지 못하면 `None` → `CONTRACT_UNREADABLE`(검증·예산보다 먼저 fail-closed).
</ParamField>

<ParamField body="--tier-policy" type="path">
티어 정책 JSON 경로. CLI 최우선. 생략 시 해석 순서 아래 참고.
</ParamField>

<ParamField body="--tier-override" type="string">
티어 정책 거부(v1 identity deny 또는 v2 fan-out cap 초과)를 한 요청에 한해 우회할 사유 문자열. 빈 문자열은 `INVALID_REQUEST`. 윈도우 카운트는 환불되지 않는다.
</ParamField>

<ParamField body="--no-record" type="flag">
ledger 행 append와 ticket 발급을 생략. dry-run·`dispatch --check-only`용. 예산 소모 없이 현재 ledger 기준으로 평가.
</ParamField>

### `register`

필수: `--job-id`, `--probe-cmd`.

<ParamField body="--job-id" type="string" required>
등록할 job/dispatch id. probe stdout에 이 문자열이 포함되어야 한다.
</ParamField>

<ParamField body="--probe-cmd" type="string" required>
shell 명령. exit 0이고 stdout에 job-id가 있어야 통과. 파일명만 찍는 출력(`grep -l` 등)은 검증 스탬프만 줄 수 있으므로 권장하지 않는다. 내용 출력(`grep <id> logfile`, `cat evidence.txt`)을 사용한다.
</ParamField>

<ParamField body="--contract-sha" type="string">
pending/ticket 디스ambiguation용 접두사·전체 sha. 생략 시 유효 ticket 1개에 의존. 복수 매칭 시 `AMBIGUOUS_TICKET`, 없음 시 `NO_MATCHING_TICKET`.
</ParamField>

<ParamField body="--runtime" type="string">
runtime 필터. 잘못된 형식이면 `INVALID_REQUEST`.
</ParamField>

<ParamField body="--orchestration-task" type="string">
`completion_channel != sentinel-log`이면 필수. `orca orchestration dispatch-show --task <id> --json`(또는 `ORCA_CLI_COMMAND` / `ORCA_DEV_REPO_ROOT`→`orca-dev`)으로 검증. 실패 시 `ORCHESTRATION_UNVERIFIED` + ledger `probe_failure`.
</ParamField>

<ParamField body="--tier-policy" type="path">
register의 모델 티어 비교에 쓰는 정책 경로. check와 동일 해석 규칙.
</ParamField>

<ParamField body="--declared-model" type="string">
check 시 선언한 모델. 측정값과 비교.
</ParamField>

<ParamField body="--model-probe-cmd" type="string">
워커가 만든 아티팩트(세션 transcript 등)에서 실제 모델 id를 stdout 마지막 non-empty 줄로 출력하는 명령. 타임아웃 30s. 참조 구현: `scripts/model-identity-probe`. TUI 상태줄 스크래핑은 금지.
</ParamField>

### `watch`

<ParamField body="--log" type="path" required>
워커 로그 경로. `check_stall`이 idle을 계산한다.
</ParamField>

<ParamField body="--max-idle" type="int">
기본 `360`초. 초과 시 `STALLED`(exit 3). 파일 없으면 `MISSING`(exit 2).
</ParamField>

stdout 예:

```json
{"idle_seconds":12,"last_progress_at":"…","reason":null,"status":"ok"}
```

### `report`

<ParamField body="--today" type="flag">
UTC 기준 당일 행만 집계.
</ParamField>

사람이 읽는 텍스트 리포트(JSON 아님): Models / Denials / Tiers / Tier policies / Tier overrides, malformed 스킵 수, time span. 정책 행이 둘 이상이면 해당 span이 단일 정책으로 판정되지 않았음을 의미한다.

## 판정 JSON (check / register)

stdout `GateDecision` 직렬화:

| 필드 | 타입 | 설명 |
| --- | --- | --- |
| `allow` | bool | 허용 여부 |
| `reason` | string | `ReasonCode` 값 |
| `warnings` | string[] | 경고 reason 코드 |
| `contract_sha` | string\|null | 계약 SHA-256 hex |
| `cost_proxy` | int | `n_agents * est_chars` (check) 또는 pending `est_chars` (register 단순화) |
| `message` | string | 선택. 거부 상세·`ticket_absent` 등 |
| `tier_override` | string | 선택. override 사유 |

<RequestExample>
```bash
G=scripts/dispatch-gate
L=~/.mogui/dispatch-ledger.jsonl

"$G" --ledger "$L" check \
  --runtime codex \
  --model gpt-5.3-codex \
  --contract /path/to/contract.md \
  --agents 1 \
  --est-chars 12000 \
  --completion-channel orchestration
```
</RequestExample>

<ResponseExample>
```json
{"allow":true,"contract_sha":"abc…","cost_proxy":12000,"reason":"OK","warnings":[]}
```
</ResponseExample>

## Ledger 스키마

append-only JSONL. 한 줄 = 한 객체, `sort_keys=True`.

### check 행 (`_append_decision`)

| 필드 | 조건 | 설명 |
| --- | --- | --- |
| `ts` | 항상 | Unix epoch float |
| `contract_sha` | 가능하면 | 거부 조기 경로에서는 null 가능 |
| `runtime` | 항상 | 요청 runtime |
| `n_agents` | 항상 | 요청 agents |
| `est_chars` | 항상 | 요청 추정 문자 (null 가능: unreadable) |
| `decision` | 항상 | `ALLOW` \| `DENY` |
| `reason` | 항상 | reason code |
| `cost_proxy` | 항상 | 정수 |
| `completion_channel` | 항상 | 요청 값 |
| `model` | 항상 | 요청 모델 |
| `warnings` | 경고 시 | string 배열 |
| `tier_override` | override 시 | 사유 문자열 |
| `tier` | v2에서 tier 확정 시 | 예: `efficient`, `unknown` |
| `tier_policy_path` | 항상 | 사용한 정책 경로 |
| `tier_policy_sha256` | 정책 로드 성공 시 | 파일 바이트 sha256 |
| `attempt` | ALLOW + contract_sha | 동일 contract_sha의 누적 ALLOW 시도 번호 |

### register 성공 행

| 필드 | 설명 |
| --- | --- |
| `ts`, `contract_sha`, `runtime`, `n_agents`, `est_chars` | pending check 행에서 복사 |
| `decision` | `ALLOW` |
| `reason` | `OK` |
| `job_id` | 등록 id |
| `completion_channel` | pending 채널 |
| `orchestration_task` | 제공 시 |
| `model_declared` | 선언 모델 |
| `model_measured` | 측정 모델 |
| `model_verified` | `bool(declared and measured and not probe_failed)` |
| `warnings` | `MODEL_*` 경고 시 |
| `attempt` | 동일 contract_sha 다음 번호 |

### register orchestration 거부 행 (CLI)

| 필드 | 설명 |
| --- | --- |
| `ts`, `decision=DENY`, `reason=ORCHESTRATION_UNVERIFIED` | |
| `job_id` | |
| `probe_failure` | `orca_missing` \| `probe_timeout` \| `probe_unparseable` \| `task_omitted` \| `task_not_found` |
| `orchestration_task` | 제공 시 |

### `MODEL_TIER_ESCALATION` 거부 행

`model_declared`, `model_measured`, `model_verified=true`(측정은 됐고 상향 티어), `decision=DENY`.

## Ticket

| 항목 | 값 |
| --- | --- |
| 디렉터리 | `~/.mogui/dispatch-tickets` (`DEFAULT_TICKET_DIR`) |
| 파일명 | `{runtime}-{contract_sha[:12]}.json` |
| TTL | `DEFAULT_TICKET_TTL_SECONDS = 600` (10분) |
| GC grace | `DEFAULT_EXPIRED_TICKET_GC_GRACE_SECONDS = 86400` (TTL+grace 후 삭제) |
| 발급 | 성공 `check` + `record=True` |
| 소비 | `register`가 `contract_sha`로 매칭 시 unlink |
| 페이로드 | `runtime`, `contract_sha`, `issued_ts`, `count` (≥1) |
| 잠금 | `fcntl` 기반 ticket dir lock |
| 경로 탈출 | `runtime` 세그먼트가 ticket_dir 밖이면 발급 거부 → `INVALID_REQUEST` |

ticket 없이 `contract_sha` 없는 경로로 register하면 pending ledger 매칭에 의존한다. charter: 성공 `check` 없는 register는 무효로 취급한다.

## Reason code

`ReasonCode` enum 값만 문서·ledger에 사용한다 (`tests/test_documented_reason_codes.py`).

| 코드 | 전형적 의미 | allow |
| --- | --- | --- |
| `OK` | 통과 | true |
| `NO_COMPLETION_CHANNEL` | 채널 누락/비허용 | false |
| `NO_MODEL` | 모델 누락 | false |
| `INVALID_REQUEST` | runtime/agents/override/채널 불일치 등 | false |
| `CONTRACT_UNREADABLE` | 계약 크기·내용 측정 실패 | false |
| `BUDGET_EXCEEDED` | 단일/배치 문자 한도 초과 | false |
| `ROUTING_VIOLATION` | high-cost runtime(`fable`) + `n_agents >= 2` | false |
| `HIGH_COST_RUNTIME` | high-cost 단일 디스패치 경고 | warn |
| `TIER_POLICY` | v1 identity deny 또는 unknown=deny | false / warn |
| `TIER_POLICY_UNAVAILABLE` | 정책 파일 없음·파싱 실패 | false |
| `TIER_FANOUT_CAP` | v2 윈도우 내 agents > cap (override 없음) | false |
| `TIER_UNKNOWN_MODEL` | v2 미등록 모델 → `unknown` 티어 | warn |
| `UNVERIFIED_JOB` | probe 실패 또는 job-id 불일치 | false |
| `ORCHESTRATION_UNVERIFIED` | orchestration task 미검증 | false |
| `AMBIGUOUS_TICKET` | ticket/pending 복수 매칭 | false |
| `NO_MATCHING_TICKET` | 매칭 없음 | false |
| `MCP_TRUST_UNHANDLED` | 계약에 mcp 언급·trust 처리 없음 | warn |
| `PATH_OUTSIDE_KNOWN_ROOTS` | 알려진 root 밖 `/Users/…` 경로 | warn |
| `WORKTREE_AS_REPO_ROOT` | worktree를 repo root로 취급하는 문구 | warn |
| `MODEL_TIER_ESCALATION` | 측정 티어가 선언보다 엄격(cap 더 작음) | false |
| `MODEL_MISMATCH` | 불일치이지만 하향/동등 감시 | warn |
| `MODEL_UNVERIFIED` | 선언 없음 또는 측정 공백 | warn |
| `MODEL_PROBE_FAILED` | probe 명령 실패/타임아웃/비0/빈 출력 | warn |

### 모델 검증 등급 (register)

| 상황 | 결과 |
| --- | --- |
| 선언 없음 | `MODEL_UNVERIFIED` 경고, 등록 허용 |
| probe 실패 | `MODEL_PROBE_FAILED` 경고, 등록 허용 |
| 측정 공백 | `MODEL_UNVERIFIED` 경고, 등록 허용 |
| 동일(casefold) | 경고 없음 |
| v2에서 측정 티어 cap이 선언보다 작음(더 엄격) | `MODEL_TIER_ESCALATION` **거부** |
| 그 외 불일치 | `MODEL_MISMATCH` 경고, 등록 허용 |
| 정책 로드 실패 중 불일치 | `MODEL_MISMATCH` 경고 |

엄격도는 cap 수치: 작을수록 엄격, uncapped = `inf`(가장 느슨).

## 티어 정책 해석

### 경로 우선순위

1. CLI `--tier-policy`
2. env `DISPATCH_TIER_POLICY`
3. 인스턴스 `config/model-tier-policy.json` (존재 시)
4. 템플릿 `master-ops/model-tier-policy.json`

예제 스키마: `config/model-tier-policy.example.json`. 채워진 인스턴스 파일은 커밋하지 않는다.

### Version 2 (권장)

| 키 | 규칙 |
| --- | --- |
| `version` | `2` |
| `tiers` | 비어 있지 않은 객체. 티어 이름 → 모델 id 배열. id는 casefold. 한 모델은 한 티어만 |
| `unknown` | 티어 이름으로 예약. 미등록 모델이 이 티어로 떨어짐 |
| `fanout_caps` | 티어 → 비음수 int. **키 없음 = uncapped** (`top`·`unknown` 동일) |
| `window_seconds` | 양의 int. 기본 `86400` |
| `agents` / `consent` | 게이트 파싱 비필수(문서·온보딩용) |

윈도우 카운트는 **누적 agents**(동시성 아님). `ALLOW` 행만 세며 override로 통과한 요청도 포함. 과거 행에 `tier`가 없으면 현재 정책으로 model을 재해석한다.

### Version 1 (레거시)

| 키 | 규칙 |
| --- | --- |
| `worker_allowed` / `worker_denied_tiers` | casefold 집합, 교집합 금지 |
| `unknown_model` | `deny` \| `warn` |
| 미허용 + deny | `TIER_POLICY` 거부(또는 override) |
| 미허용 + warn | `TIER_POLICY` 경고 후 허용 |

### Owner top-tier 절차

템플릿 v2는 `fanout_caps.top`을 두지 않아 top은 gate 상 uncapped다. `master-ops/scripts/dispatch`가 top-tier 모델에 `--top-approved "<reason>"`를 요구한다. 이는 런 로그 절차 증거이며 인증 경계가 아니다.

## 문자·예산 한도

| 상수 | 기본값 | 적용 |
| --- | --- | --- |
| `DEFAULT_SINGLE_DISPATCH_CHAR_LIMIT` | `500_000` | `est_chars > limit` → `BUDGET_EXCEEDED` |
| `DEFAULT_BATCH_DISPATCH_CHAR_LIMIT` | `1_000_000` | `cost_proxy = n_agents * est_chars > limit` → `BUDGET_EXCEEDED` |
| `DEFAULT_DUPLICATE_WINDOW_SECONDS` | `1800` | 설정 필드(게이트 config) |
| `DEFAULT_HIGH_COST_RUNTIMES` | `{"fable"}` | multi-agent 시 `ROUTING_VIOLATION` |
| `DEFAULT_TICKET_TTL_SECONDS` | `600` | ticket 유효 기간 |
| `DEFAULT_TIER_WINDOW_SECONDS` | `86400` | v2 fan-out 창 |
| model probe timeout | `30` | CLI |
| orchestration probe timeout | `30` | CLI |
| watch `--max-idle` 기본 | `360` | stall |

## 계약 lint 경고 (check)

허용을 막지 않는 경고:

- `MCP_TRUST_UNHANDLED` — 계약에 mcp/`mcp__`/`code-review-graph`가 있고 trust/신뢰/다이얼로그 처리가 없음
- `PATH_OUTSIDE_KNOWN_ROOTS` — `~/.mogui/known-roots.json` 밖 `/Users/…` 절대 경로
- `WORKTREE_AS_REPO_ROOT` — worktree를 repo root로 쓰는 패턴

## 운영 메모

- `check` 없이 `register`하지 않는다. 아티팩트 존재 후·최종 증거 보고 전에 등록한다.
- Codex/Cursor attach 전 `scripts/codex-worker-pretrust` / `scripts/cursor-worker-pretrust` (charter §5).
- 완료 채널 `orchestration`이 규범이다. raw terminal polling·vendor-direct CLI는 non-compliant.
- Orca 실행 파일: `ORCA_CLI_COMMAND` → `ORCA_DEV_REPO_ROOT`면 `orca-dev` → 기본 `orca`.

## Next

<CardGroup>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    check → dispatch → register 흐름, 계약 해시, model probe, pretrust, 완료 채널.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    DISPATCH_TIER_POLICY, model-tier-policy JSON, 환경 변수 해석 순서.
  </Card>
  <Card title="CLI 레퍼런스" href="/cli-reference">
    scripts/ 공개 명령 표와 --help 동기화 계약.
  </Card>
  <Card title="방어 인벤토리" href="/defense-inventory">
    디스패치 게이트·모델 프로브·ledger 방어 표.
  </Card>
  <Card title="Worker reap" href="/worker-reap">
    settled 후 lease 회수와 ledger 감사 행.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    MODEL_PROBE_FAILED, tier/placement 실패 복구.
  </Card>
</CardGroup>

---

## 17. master-succeed 레퍼런스

> detect·handoff·verify-successor·check-duplicates·retire·spawn 옵션, exit 코드, SPAWN_PLACEMENT_MISMATCH, JSON 출력.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/17-master-succeed.md
- Generated: 2026-08-07T07:06:20.887Z

### Source Files

- `scripts/master-succeed`
- `src/master_runtime/core/succession.py`
- `scripts/master-bootstrap`
- `scripts/master-bootstrap-live`
- `scripts/master-recover`
- `src/master_runtime/core/lineage.py`
- `tests/test_succession.py`
- `docs/public/reference.md`

---
title: "master-succeed 레퍼런스"
description: "detect·handoff·verify-successor·check-duplicates·retire·spawn 옵션, exit 코드, SPAWN_PLACEMENT_MISMATCH, JSON 출력."
---

`scripts/master-succeed`는 Orca 네이티브 마스터 승계(succession) CLI다. 구현은 `src/master_runtime/core/succession.py`의 `detect_trigger`, `build_handoff`, `verify_successor`, `detect_duplicate_instances`, `retire_predecessor`, `spawn_successor`에 위임한다. 성공 시 stdout에 JSON을 쓰고 exit `0`이다. 가드 위반은 `SuccessionError`로 stderr 메시지와 `exc.exit_code`를 반환한다(기본 `2`).

## 명령 요약

| 하위 명령 | 역할 | 핵심 입력 | 성공 시 payload 키 |
| --- | --- | --- | --- |
| `detect` | 승계 트리거 분류 | positional `text`, `--context-ratio` | `status`, `reason`, `message` |
| `handoff` | thin handoff 마크다운 생성 | `--spec` JSON 파일 | `handoff` |
| `verify-successor` | U8 recovery report 검증 | `--report` JSON 파일 | `status`, `checks`, `evidence` |
| `check-duplicates` | 동일 marker 중복 마스터 탐지 | `--self-handle`, `--marker` | `duplicates` |
| `retire` | predecessor 1개 해석·선택적 close | `--self-handle`, target 필드, `--execute` | `RetirementReport` 필드 |
| `spawn` | successor 터미널 create 또는 dry-run | workspace·kickoff·root·title·agent | `SpawnReport` 필드 |

공통 옵션:

- `--json` — 컴팩트 JSON(`separators=(",", ":")`, `sort_keys=True`). 생략 시 들여쓰기 JSON.
- `--fixture-json` — 숨김 테스트 훅. `orca terminal list` / scoped list / `close`를 fixture로 대체한다. 공개 운영 표면이 아니다.

```bash
scripts/master-succeed {detect|handoff|verify-successor|check-duplicates|retire|spawn} ...
```

## 공통 동작

- 파서는 `argparse` 서브커맨드 필수(`required=True`).
- stdout은 항상 JSON 객체(또는 배열 래핑 객체). 오류 본문은 stderr 문자열이며 JSON이 아니다.
- Orca 호출은 기본 `subprocess` 러너(`orca terminal list|create|close --json`). 단위 테스트는 `--fixture-json` 또는 인메모리 runner로 대체한다.
- 기본 에이전트 모델 맵(`scripts/master-succeed` `DEFAULT_MODELS`):

| `--agent` | 기본 `--model` |
| --- | --- |
| `claude`, `claude-code` | `claude-fable-5` |
| `grok`, `grok-build` | `grok-4.5` |
| `codex` | `gpt-5.6-sol` |
| 그 외(cursor/agy 포함, 미측정) | 기본 없음 → exit `2` (`--model is required for agent …`) |

## detect

```bash
scripts/master-succeed detect "succession now" --context-ratio 0.65 --json
```

### 옵션

<ParamField body="text" type="string" required>
승계 트리거로 분류할 원문. 소문자 normalize 후 부분 문자열 매칭.
</ParamField>

<ParamField body="--context-ratio" type="float">
컨텍스트 사용 비율. `>= 0.60`이면 명시 IMMEDIATE 마커가 없을 때 `ADVISORY`.
</ParamField>

<ParamField body="--json" type="boolean">
컴팩트 JSON 출력.
</ParamField>

### 판정 규칙

| `status` | 조건 | `reason` |
| --- | --- | --- |
| `IMMEDIATE` | 텍스트에 명시 마커 포함: `succession now`, `handoff to successor`, `승계해줘`, `다음 마스터로 넘기자`, `승계 진행해` | `explicit succession instruction` |
| `ADVISORY` | `context_ratio >= 0.60` | `context ratio threshold reached` + auto-succession 금지 메시지 |
| `ADVISORY` | context에 non-empty `milestone` (CLI는 ratio만 전달; 라이브러리 API용) | `natural milestone reached` |
| `NONE` | 그 외 | `no succession trigger` |

`IMMEDIATE`가 아니면 **자동 spawn하지 않는다**. advisory는 제안만 허용한다.

### JSON 출력

```json
{"message":"","reason":"explicit succession instruction","status":"IMMEDIATE"}
```

```json
{
  "message": "Context ratio is high. Auto-succession is not permitted; propose succession only.",
  "reason": "context ratio threshold reached",
  "status": "ADVISORY"
}
```

## handoff

```bash
scripts/master-succeed handoff --spec ./ops/handoff-spec.json --json
```

### 옵션

<ParamField body="--spec" type="path" required>
handoff 섹션을 채울 JSON 스펙 파일 경로.
</ParamField>

### 스펙 필드

| 키 | 용도 | 비고 |
| --- | --- | --- |
| `role_state` | Role State 동결 입력 | 없으면 `current_role` 또는 기본 `Reference Implementation`으로 `RoleState` 구성 |
| `current_role` | 현재 역할 | `role_state` 없을 때 사용 |
| `current_objective` | Current Objective | 텍스트 |
| `active_tracks` / `open_tracks` | Active/Open Tracks | 리스트 → `- item` 줄 |
| `accepted_artifacts` | Accepted Artifacts | 리스트 |
| `deferred_work` | Deferred Work | 리스트 |
| `open_questions` | Open Questions | 리스트 |
| `recommended_next_role` | Recommended Next Role | 기본: frozen current role |
| `observed_baseline` | Observed Baseline | 텍스트 |

출력 handoff는 Role State를 lock-enabled로 동결한다(`Frozen: all other roles`, `Unlock: explicit user instruction only`).

### JSON 출력

```json
{"handoff":"## Role State\n\n```\nCurrent Role: ...\n..."}
```

## verify-successor

```bash
scripts/master-succeed verify-successor --report ./ops/recovery-report.json --json
```

### 옵션

<ParamField body="--report" type="path" required>
U8 recovery report JSON. `steps` 배열에 `{ "step", "status" }` 항목.
</ParamField>

### 판정

| 결과 `status` | 조건 |
| --- | --- |
| `FAILED` | 어떤 step `status == "MISS"` |
| `PASS` | step `6` OK + `2-3` OK/없음 + `5` OK → checks 3개 |
| `PARTIAL` | checks 1–2개 |
| `FAILED` | checks 0개 |

checks 문자열:

- step `6` == `OK` → `open tracks recited`
- step `2-3` in (`OK`, 없음) → `baseline matched`
- step `5` == `OK` → `monitors rearmed`

### JSON 출력

```json
{
  "checks": ["open tracks recited", "baseline matched", "monitors rearmed"],
  "evidence": "checks=open tracks recited,baseline matched,monitors rearmed",
  "status": "PASS"
}
```

## check-duplicates

```bash
scripts/master-succeed check-duplicates \
  --self-handle <current-handle> \
  --marker <session-marker> \
  --json
```

### 옵션

<ParamField body="--self-handle" type="string" required>
현재 마스터 터미널 handle. 결과에서 제외한다.
</ParamField>

<ParamField body="--marker" type="string" required>
handle / pty / session / worktree / title / path 등 세션 필드 부분 문자열.
</ParamField>

동작:

1. `orca terminal list --json`으로 세션 수집
2. `handle != self_handle` 이고 marker가 매칭 필드에 부분 일치하면 중복 후보
3. 비어 있지 않은 `duplicates`는 **finding**이다. CLI는 그 자체로 non-zero exit를 강제하지 않는다(호출자가 해석).

### JSON 출력

```json
{
  "duplicates": [
    {
      "handle": "term-u8-shadow",
      "worktree_path": "...",
      "branch": "...",
      "title": "...",
      "connected": true,
      "pty_id": "...",
      "worktree_id": "...",
      "session_id": "...",
      "process_id": null
    }
  ]
}
```

`self_handle` 또는 marker가 비면 `SuccessionError` exit `2`.

## retire

```bash
scripts/master-succeed retire \
  --self-handle <successor-handle> \
  --target-handle <predecessor-handle> \
  --target-pid <pid> \
  --target-tty /dev/ttysNNN \
  --json
# 실제 close:
#   ... --execute
```

### 옵션

<ParamField body="--self-handle" type="string" required>
후임(또는 호출자) handle. 후보에 포함되면 self-close 거부로 raise.
</ParamField>

<ParamField body="--expected" type="string">
selector 모드 부분 문자열. exact target 필드가 없을 때 사용.
</ParamField>

<ParamField body="--target-handle" type="string">
predecessor handle 정확 일치.
</ParamField>

<ParamField body="--target-pty-id" type="string">
pty id 정확 일치.
</ParamField>

<ParamField body="--target-session-id" type="string">
session id 정확 일치.
</ParamField>

<ParamField body="--target-pid" type="integer">
외부 측정 pid. 터미널 레코드 `process_id`가 null인 folder-workspace 좌석에서 필요. `<= 0`이면 exit `2`.
</ParamField>

<ParamField body="--target-tty" type="string">
외부 측정 tty (`/dev/ttys147` 또는 bare name). 생략 시 tty 검사는 `skipped:…`이고 full `CLOSED` 불가.
</ParamField>

<ParamField body="--execute" type="boolean">
없으면 `DRY_RUN`. 있으면 `orca terminal close --terminal <handle> --json` 후 소멸 측정.
</ParamField>

### 후보 해석

1. `--target-handle` / `--target-pty-id` / `--target-session-id` 중 하나라도 있으면 **exact** 매칭만 사용.
2. 없으면 `--expected`(또는 selector 문자열)로 여러 필드 **부분 문자열** 매칭.
3. 후보 0개 → `REFUSED`
4. 후보 2개 이상 → `REFUSED` (`ambiguous predecessor candidates`)
5. 후보에 `self_handle` 포함 → raise (self close 거부)

### 상태와 disappearances

| `status` | 의미 |
| --- | --- |
| `DRY_RUN` | 후보 1개, `--execute` 없음 |
| `REFUSED` | 후보 없음/모호, close 실패, 또는 소멸 측정 실패(`still_present` 등) |
| `CLOSED` | pane·process·tty 모두 `measured` |
| `CLOSED_PARTIAL` | pane `measured`, still_present 없음, process/tty 중 일부가 `skipped:*` |

`disappearances` 키: `pane`, `process`, `tty`.

- `measured` / `still_present`
- process/tty만 `skipped:no target-pid and no pid reported by terminal list`, `skipped:no target-tty supplied` 등

close 명령 반환값만으로 `CLOSED`를 내지 않는다. 재 list + pid/tty probe가 판정한다.

### JSON 출력 (dry-run 예)

```json
{
  "candidates": [{"handle": "term-u8", "...": "..."}],
  "closed": false,
  "disappearances": {},
  "match_attempts": ["term-u8: handle=term-u8 -> handle"],
  "reason": "dry-run only",
  "status": "DRY_RUN",
  "target_handle": "term-u8"
}
```

## spawn

```bash
scripts/master-succeed spawn \
  --workspace-selector "id:folder:<uuid>" \
  --expected-placement "id:folder:<uuid>" \
  --kickoff-text "Founding master boot" \
  --root /abs/workspace/root \
  --agent claude \
  --model claude-fable-5 \
  --title "Founding master boot" \
  --json \
  --dry-run
```

### 옵션

<ParamField body="--workspace-selector" type="string" required>
`orca terminal create --worktree`에 전달하는 selector. 검증된 형태: `id:<repoId>::<path>`, `id:folder:<uuid>` / `folder:<uuid>`. `path:`는 repository worktree 경로 일치에만 사용.
</ParamField>

<ParamField body="--kickoff-text" type="string">
에이전트 기동 prompt 문자열. `--kickoff-file`과 상호 배타·필수 그룹.
</ParamField>

<ParamField body="--kickoff-file" type="path">
kickoff 본문을 파일에서 읽음.
</ParamField>

<ParamField body="--root" type="path" required>
startup command의 `cd` 대상 및 agent cwd.
</ParamField>

<ParamField body="--title" type="string" required>
Orca pane title. handle reissue 시 title 매칭에 사용.
</ParamField>

<ParamField body="--model" type="string">
에이전트 모델 id. 생략 시 `DEFAULT_MODELS[agent]`, 없으면 exit `2`.
</ParamField>

<ParamField body="--agent" type="string" default="claude">
기동 CLI 이름. `claude`/`claude-code`, `grok`/`grok-build`, `codex`, `cursor`/`cursor-agent`, `agy`, 또는 custom executable.
</ParamField>

<ParamField body="--expected-placement" type="string">
**독립적으로** 기대하는 worktree id. create 응답 `worktreeId`(또는 reissued 세션)와 불일치 시 fail-closed, exit **`26`** (`SPAWN_PLACEMENT_MISMATCH`), 가능하면 생성 터미널 close.
</ParamField>

<ParamField body="--dry-run" type="boolean">
터미널을 만들지 않고 create command / startup_command / verification 계획만 반환.
</ParamField>

### 에이전트 startup command 형태

| agent | 대략적 형태 |
| --- | --- |
| claude / claude-code | `cd ROOT && exec claude --model MODEL --dangerously-skip-permissions KICKOFF` |
| grok / grok-build | `cd ROOT && exec grok --model MODEL --always-approve --cwd ROOT KICKOFF` |
| codex | `cd ROOT && exec codex --model MODEL KICKOFF` (승인 플래그 없음; pretrust 경로 별도) |
| cursor / cursor-agent | `cd ROOT && exec cursor-agent --model MODEL --force --trust KICKOFF` |
| agy | `cd ROOT && exec agy --model MODEL --dangerously-skip-permissions KICKOFF` |
| unknown | `cd ROOT && exec AGENT --model MODEL KICKOFF` (승인 플래그 없음 → prompt-block 가능) |

### fail-closed 검증 순서 (non-dry-run)

```text
scoped terminal list snapshot
  → (best-effort global list snapshot)
  → orca terminal create --worktree SELECTOR --title TITLE --command STARTUP --json
  → parse handle + worktreeId
  → worktree match(selector) else close + SPAWN_WORKTREE_MISMATCH(22)
  → expected_placement match else close + SPAWN_PLACEMENT_MISMATCH(26)
  → scoped re-list liveness
  → reported handle is new+connected+in-worktree → MATCH
  → else unique new titled candidate → MATCH_REISSUED (handle_reissued=true)
  → else SPAWN_HANDLE_STALE(24) without closing unmanaged candidates
```

selector 비교는 `id:` prefix 정규화와 `path:` realpath 매칭을 허용한다. create 응답이 bare `repoId::path`이고 요청이 `id:repoId::path`여도 동일 worktree로 본다.

### JSON 출력

**DRY_RUN:**

```json
{
  "command": ["orca", "terminal", "create", "--worktree", "id:folder:abc", "--title", "master", "--command", "cd /tmp && exec claude --model claude-fable-5 --dangerously-skip-permissions boot", "--json"],
  "handle": null,
  "handle_reissued": false,
  "requested_worktree": "id:folder:abc",
  "startup_command": "cd /tmp && exec claude --model claude-fable-5 --dangerously-skip-permissions boot",
  "status": "DRY_RUN",
  "verification": {
    "agent": "claude",
    "expected_response_field": "worktreeId",
    "fail_closed_action": "terminal close on mismatch",
    "liveness_check": "reported handle must be live+new+connected in requested worktree, else adopt unique titled candidate or fail closed without closing",
    "requested_worktree": "id:folder:abc"
  },
  "verified": false,
  "worktree_id": null
}
```

`--expected-placement`가 있으면 `verification.expected_placement`가 포함된다.

**CREATED (성공):**

| 필드 | 의미 |
| --- | --- |
| `status` | `CREATED` |
| `handle` | 신뢰된 terminal handle (reissue 시 대체 handle) |
| `worktree_id` | 실제 worktree id |
| `requested_worktree` | 요청 selector |
| `verified` | `true` |
| `handle_reissued` | title-unique 재발급 채택 여부 |
| `command` | create argv 배열 |
| `startup_command` | pane 안 shell 문자열 |
| `verification.result` | `MATCH` 또는 `MATCH_REISSUED` |

## Exit 코드

| 코드 | 상수 / 상황 |
| --- | --- |
| `0` | 성공, JSON stdout |
| `2` | 기본 `SuccessionError` (인자 누락, invalid report, self-handle retire 거부, unknown agent without `--model` 등) |
| `20` | `SPAWN_CREATE_ERROR` — `orca terminal create` 실패 또는 create 응답 `ok:false` |
| `21` | `SPAWN_PARSE_ERROR` — create JSON 파싱/필수 필드 누락 |
| `22` | `SPAWN_WORKTREE_MISMATCH` — 응답 worktree ≠ `--workspace-selector` (close 시도) |
| `23` | `SPAWN_CLOSE_ERROR` — mismatch/error 후 생성 터미널 close 실패 |
| `24` | `SPAWN_HANDLE_STALE` — 보고 handle 신뢰 불가, reissue 후보 0/다수 |
| `25` | `SPAWN_LIST_ERROR` — precheck 또는 liveness `terminal list` 실패 |
| `26` | **`SPAWN_PLACEMENT_MISMATCH`** — 실제 worktree ≠ `--expected-placement` (close 시도) |

<Warning>
`--expected-placement`는 “요청이 맞는지”가 아니라 **라인리지/운영 카드가 독립적으로 기대한 좌석**과의 교차 검증이다. selector를 맞춰 녹색만 내는 것은 placement 통과가 아니다.
</Warning>

오류 메시지 예 (stderr):

```text
spawn placement mismatch; closed terminal term-x: expected id:folder:abc, got id:repo::/path
```

선택자 힌트가 붙을 수 있다:

```text
Accepted selector forms are full id:<repoId>::<path>, id:folder:<uuid> for folder workspaces; path: is matched by resolved directory.
```

## 상수 요약

| 영역 | 값 |
| --- | --- |
| Trigger | `IMMEDIATE`, `ADVISORY`, `NONE` |
| Verify | `PASS`, `PARTIAL`, `FAILED` |
| Retire status | `DRY_RUN`, `REFUSED`, `CLOSED`, `CLOSED_PARTIAL` |
| Spawn status | `DRY_RUN`, `CREATED` |
| Disappearance | `measured`, `still_present`, `skipped:…` |

## 운영 흐름 (CLI 조합)

clean succession에서 이 스크립트가 담당하는 측정 표면:

```text
detect → handoff → spawn [--expected-placement] → (boot tools) → verify-successor → retire --execute
```

- founding / successor create: `spawn` (먼저 `--dry-run`)
- empty seat / 이중 마스터: `check-duplicates`
- recovery proof: `verify-successor` (입력 report는 `scripts/master-recover` 등이 산출)
- predecessor teardown: `retire` (handshake 후; `--target-pid`/`--target-tty` 직접 측정)

Lineage append(`src/master_runtime/core/lineage.py`)는 이 CLI의 하위 명령이 아니다. 검증 후 운영 카드/절차가 별도 append한다.

## 관련 bootstrap / recovery 진입점

| 스크립트 | 역할 경계 |
| --- | --- |
| `scripts/master-bootstrap` | charter·handoff·budget 기반 boot block |
| `scripts/master-bootstrap-live` | handoff-dir·role-state 기반 live boot block |
| `scripts/master-recover` | recovery report 산출 (`verify-successor` 입력) |
| `scripts/master-succeed` | 승계 detect·handoff·spawn·duplicate·retire·verify |

## 트러블슈팅 신호

| 증상 | 확인 |
| --- | --- |
| exit `26` | `--expected-placement` vs 실제 `worktreeId`. folder seat에 repo worktree를 넣었는지 재측정 |
| exit `22` | `--workspace-selector` 형태(`path:` 단독 사용 지양, `id:` 선호) |
| exit `24` | create 직후 handle 재발급; title 유일성, unmanaged pane 정리 |
| exit `25` | bare `terminal list` 실패 호스트 → spawn은 scoped list 사용; precheck 실패 원인 확인 |
| retire `CLOSED_PARTIAL` | pid/tty 미전달. folder pane의 null process_id 보완 |
| retire `REFUSED` + still_present | close 후에도 pane/process/tty 생존 → handshake/RST 정책 |
| duplicates non-empty | 이중 마스터 사고 신호. 두 번째 founding 금지 |
| unknown agent exit `2` | 측정된 기본 모델 없음 → `--model` 명시 |

## Next

<CardGroup>
  <Card title="Clean succession" href="/succession">
    handoff → placement spawn → verify → retire → lineage 절차 가이드
  </Card>
  <Card title="마스터 라이프사이클" href="/master-lifecycle">
    founding부터 lineage까지의 generation 루프
  </Card>
  <Card title="Orca 객체 모델" href="/orca-object-model">
    selector 형태, folder seat, worktreeId 판정
  </Card>
  <Card title="방어 인벤토리" href="/defense-inventory">
    placement·duplicate·empty-seat 가드 표
  </Card>
  <Card title="CLI 레퍼런스" href="/cli-reference">
    scripts/ 공개 명령 표와 --help 동기화 계약
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    placement mismatch, seat 중복, revival 복구
  </Card>
</CardGroup>

---

## 18. acceptance-loop 레퍼런스

> validate·split·run·inspect 하위 명령, 스위트 구조, holdout, max-iterations, baseline/restore, 판정 산출물.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/18-acceptance-loop.md
- Generated: 2026-08-07T07:06:17.535Z

### Source Files

- `scripts/acceptance-loop`
- `src/master_runtime/core/acceptance/loop.py`
- `src/master_runtime/core/acceptance/casebook.py`
- `src/master_runtime/core/acceptance/config.py`
- `src/master_runtime/core/acceptance/verdict.py`
- `tests/test_acceptance_loop.py`
- `docs/public/reference.md`

---
title: "acceptance-loop 레퍼런스"
description: "validate·split·run·inspect 하위 명령, 스위트 구조, holdout, max-iterations, baseline/restore, 판정 산출물."
---

`scripts/acceptance-loop`는 마스터 측 **결정적 수락 루프** CLI다. JSON 스위트 설정을 로드한 뒤, 후보 변경을 `train`+`holdout` 결합 통과 수(pass count)로만 비교해 수락·거절한다. 모델 판단·휴리스틱·자연어 근거는 판정에 참여하지 않는다. 구현은 `src/master_runtime/core/acceptance/`에 있고, proposer는 구독형 CLI 서브프로세스(`claude` · `codex` · `cursor-agent`)로만 호출되며 SDK·API 키를 읽지 않는다.

## 하위 명령 요약

| 하위 명령 | 목적 | 필수 옵션 | 종료 코드 |
| --- | --- | --- | --- |
| `validate` | 설정·casebook 구조 검증 후 요약 JSON 출력 | `--config` | `0` 성공, `2` 설정 오류 |
| `split` | 마스터 측 스플릿 매니페스트 기록 | `--config` | `0` 성공, `2` 설정 오류 |
| `run` | baseline 평가 → 반복 propose/evaluate/decide | `--config` | `0` gated 전부 통과, `1` 미완료, `2` 오류 |
| `inspect` | 기존 런의 `report.json` 출력 | `--run-dir` | `0` 성공, `2` 리포트 없음 |

```bash
scripts/acceptance-loop validate --config path/to/suite.json
scripts/acceptance-loop split    --config path/to/suite.json [--output-dir DIR]
scripts/acceptance-loop run      --config path/to/suite.json \
  [--max-iterations N] [--baseline-ref REF] [--restore-cmd CMD]
scripts/acceptance-loop inspect  --run-dir path/to/run
```

<Note>
공개 `docs/public/reference.md` 표의 `inspect` 설명(“스위트 구성 보고”)과 달리, 구현은 **이미 작성된** `run_dir/report.json`을 그대로 stdout에 찍는다. 스위트 구조 검증은 `validate`가 담당한다.
</Note>

## 설정 JSON

경로 필드는 **설정 파일 위치 기준**으로 해석된다. 기본 `run_dir`은 `runs/{name}`이다.

```json
{
  "name": "dz-bwh-demo",
  "workspace_root": "workspace",
  "run_dir": "run",
  "max_iterations": 2,
  "proposer": {
    "runtime": "codex",
    "model": "gpt-5-codex",
    "timeout_seconds": 60
  },
  "regression_log": "state/regressions.jsonl",
  "cases": [
    {
      "case_id": "t1",
      "split": "train",
      "stratum": "unit",
      "command": ["true"]
    },
    {
      "case_id": "h1",
      "split": "holdout",
      "stratum": "unit",
      "command": ["true"]
    }
  ]
}
```

### 최상위 필드

<ParamField body="name" type="string" required>
런 이름. 비어 있으면 로드 실패.
</ParamField>

<ParamField body="workspace_root" type="string">
평가·proposer cwd. 기본 `"."` (설정 파일 기준 해석).
</ParamField>

<ParamField body="run_dir" type="string">
산출물 루트. 기본 `runs/{name}`.
</ParamField>

<ParamField body="max_iterations" type="integer">
제안 반복 상한. 기본 `3`. **1 이상** 양의 정수만 허용.
</ParamField>

<ParamField body="proposer" type="object" required>
`runtime` 필수. `model`(string\|null), `timeout_seconds`(기본 `1800` = 30분).
</ParamField>

<ParamField body="regression_log" type="string">
선택. 실패 핀용 append-only JSONL 경로.
</ParamField>

<ParamField body="cases" type="array" required>
검증 케이스 목록. 루트 배열 또는 `{ "cases": [...] }` 형태.
</ParamField>

### proposer.runtime

`core/adapter`의 sync CLI 프로필 이름만 허용한다. 현재: `claude`, `codex`, `cursor-agent`. 그 외 이름은 `AcceptanceConfigError` / exit `2`.

| runtime | argv 형태 (요약) |
| --- | --- |
| `claude` | `claude -p <prompt> [--model …]` |
| `codex` | `codex exec [--model …] <prompt>` |
| `cursor-agent` | `cursor-agent -p --trust --force [--model …] <prompt>` |

## 스위트 · casebook 구조

### VerificationCase

| 필드 | 타입 | 규칙 |
| --- | --- | --- |
| `case_id` | string | 필수, 스위트 내 유일 |
| `split` | string | `train` · `holdout` · `scorecard` (별칭 허용) |
| `stratum` | string | 필수. seed train/holdout이 **같은 stratum 집합**을 커버해야 함 |
| `command` | string[] | 선택. 없으면 command evaluator가 fail-closed (`case has no command`) |
| `origin` | string | `seed`(기본) · `regression` |

### 스플릿 의미

| Split | 가시성 | 수락 게이트 | 용도 |
| --- | --- | --- | --- |
| `train` | **visible** (proposer 노출) | gated | 후보가 고칠 수 있는 실패 목록 |
| `holdout` | **private** | gated | 오버피팅 방지. proposer workspace에 기록되지 않음 |
| `scorecard` | private | **비-gate** | baseline·최종 후보에만 별도 측정 |

별칭: `visible`→`train`, `private`→`holdout`, `acceptance`→`scorecard`.

### 구조 검증 (`CaseBook.validate`)

- 케이스 1개 이상
- `case_id` 중복 금지
- gated 스플릿(`train`, `holdout`) 각각 최소 1케이스
- seed 기준 train/holdout **stratum 집합이 동일**해야 함

`validate`와 `load_acceptance_config` 모두 이 검증을 통과해야 한다.

## validate

```bash
scripts/acceptance-loop validate --config suite.json
```

성공 시 stdout JSON 예:

```json
{
  "holdout": 1,
  "max_iterations": 2,
  "name": "dz-bwh-demo",
  "proposer_runtime": "codex",
  "regression_log": null,
  "run_dir": "/abs/path/run",
  "scorecard": 0,
  "train": 1,
  "workspace_root": "/abs/path/workspace"
}
```

## split

regression log가 설정돼 있으면 **적용 후** 매니페스트를 쓴다.

| 옵션 | 기본 |
| --- | --- |
| `--config` | 필수 |
| `--output-dir` | 생략 시 `config.run_dir` |

기록 파일 (`AcceptanceRunLayout.write_manifest`):

- `manifest.json` — config 직렬화
- `split.json` — 전체 스플릿 매니페스트
- `split.md` — 사람이 읽는 매니페스트

stdout: `split.json`의 절대/상대 경로 한 줄.

## run

### 플래그

<ParamField body="--config" type="path" required>
스위트 JSON.
</ParamField>

<ParamField body="--max-iterations" type="integer">
설정 값을 덮어씀. `< 1`이면 stderr 메시지 후 exit `2`.
</ParamField>

<ParamField body="--baseline-ref" type="string">
baseline `Candidate.ref`. 기본 `""`. 코어는 ref를 해석하지 않음(평가기가 워크스페이스 상태를 읽음).
</ParamField>

<ParamField body="--restore-cmd" type="string">
거절된 후보 후 워크스페이스 복원 셸 명령. `shlex.split` 후 `workspace_root`에서 실행. 타임아웃 **300초**.
</ParamField>

### 루프 동작

```text
baseline = Candidate(label="baseline", ref=--baseline-ref)
evaluate(gated cases) → baseline_score
promote failures → regression_log (optional)

for i in 1..max_iterations:
  if current_score complete → stop
  build visible-only proposer workspace
  candidate = CLI proposer(...)
  if no candidate → record empty iteration, stop
  if surfaces empty → reject NO_CANDIDATE_CHANGE (재평가 없음)
  else evaluate → decide(strict combined pass increase)
  if accepted → current = candidate
  elif restore-cmd → on_reject(candidate)

if scorecard cases exist:
  evaluate baseline + final on scorecard only
  (nothing accepted → final_scorecard is baseline_scorecard, no re-run)

write report.json + report.md
stdout: report Markdown
exit 0 iff final_score.is_complete() else 1
```

### 판정 규칙 (`decide`)

수락은 **gated 스플릿 결합 통과 수의 엄격 증가**만 본다. scorecard는 합산에서 제외.

| `AcceptanceReason` | 의미 |
| --- | --- |
| `PASS_COUNT_INCREASED` | `candidate_combined > current_combined` → 수락 |
| `NO_PASS_COUNT_INCREASE` | 증가 없음(동률·감소 포함) → 거절 |
| `NO_CANDIDATE_CHANGE` | `surfaces` 비어 있음 → 거절, 평가 생략 |

train을 늘리고 holdout을 깎아 합이 같으면 거절된다(테스트: holdout 교환 방지).

누락 케이스 결과는 fail-closed: `detail = "missing result"`, 통과 수에 포함되지 않음. 평가기가 보고한 split/stratum은 무시되고 **casebook 소유 값**으로 덮어쓴다.

### baseline / restore / in-place

- baseline 라벨: 항상 `baseline`
- 후보 라벨: `iter-001`, `iter-002`, … (`CANDIDATE_LABEL_FORMAT`)
- CLI proposer와 `command_evaluator`는 모두 **in-place mutation**을 선언한다
- `max_iterations >= 2` 이고 restore 훅이 없으면 루프가 `ValueError`로 거부한다  
  → CLI에서는 `--restore-cmd` 필요. `max_iterations=1`이면 restore 불필요
- 거절 시에만 `on_reject` 호출. 수락 시에는 복원하지 않음

```bash
scripts/acceptance-loop run --config suite.json \
  --max-iterations 3 \
  --restore-cmd 'git checkout -- . && git clean -fd'
```

### proposer workspace (visible only)

경로: `run_dir/history/visible/iterations/{NNN}/proposer_workspace/`

| 파일 | 내용 |
| --- | --- |
| `task.md` | 제안 작업 지시(가시 실패만 나열) |
| `casebook_visible.json` | visible 매니페스트 (`train`만) |
| `visible_failures.json` | 가시 실패 결과 |
| `history.json` | 이전 반복 결정 요약 |
| `candidate.json` | proposer가 써야 하는 선언 (없으면 후보 없음) |
| `proposal.md` | 선택 근거 텍스트 |
| `proposer_result.json` / `stdout` / `stderr` | CLI 호출 기록 |

`candidate.json` 형태:

```json
{
  "surfaces": ["src/target.py"],
  "summary": "what changed and why",
  "ref": "optional-opaque-handle"
}
```

`surfaces`가 비어 있으면 변경 없음으로 기록되고 거절된다. 파일이 없거나 JSON 불량이면 후보 없음으로 반복이 종료된다.

holdout case_id는 proposer workspace 어디에도 쓰이지 않는다.

## inspect

```bash
scripts/acceptance-loop inspect --run-dir runs/dz-bwh-demo
```

- 대상: `{run_dir}/report.json`
- 없으면: `no report at …` → exit `2`
- 있으면: 파일 내용을 그대로 stdout에 출력 (JSON)

## 런 산출물 레이아웃

```text
run_dir/
  manifest.json
  split.json
  split.md
  report.json
  report.md
  history/
    visible/
      train/<label>/result.json
      iterations/
        001/
          decision.json
          decision.md
          proposer_workspace/...
    private/
      holdout/<label>/result.json
      scorecard/<label>/result.json   # scorecard cases 있을 때
```

- `history/visible/**` — proposer에 복사·노출 가능
- `history/private/**` — 마스터 전용. holdout·scorecard 결과 경로
- 모든 JSON/텍스트 쓰기는 임시 파일 후 `os.replace` 원자 교체

### decision.json 필드

| 필드 | 설명 |
| --- | --- |
| `iteration` | 1-based |
| `starting_label` | 수락 전 현재 라벨 |
| `candidate_label` / `candidate_ref` | 후보 |
| `decision` | `accepted` \| `rejected` |
| `reason` | `AcceptanceReason` 값 |
| `current_combined` / `candidate_combined` / `delta` | 결합 통과 수 |
| `changed_surfaces` | `surfaces` 목록 |
| `promoted_regressions` | 이번 반복에 새로 핀된 case_id |
| `summary` | 제안 요약 |

### report

`report.md` (run stdout과 동일 형식) 요약:

- Baseline / Final 라벨
- Accepted candidates `accepted/iterations`
- 스플릿 표: train · holdout · (있으면) scorecard 의 baseline vs final `passed/total`
- 반복별 수락/거절, reason, combined delta, surfaces, pinned regressions

`report.json`은 `AcceptanceReport.to_dict()` 전체 직렬화(scorecard 포함).

## regression log

선택 경로 JSONL. 한 번 실패한 케이스는 이후 라운드에서 조용히 빠지지 않는다.

- 관측 실패 → `origin=regression`으로 append (`ts`, `iteration`, `observed_split` 포함)
- config에 아직 있으면 원래 split 유지
- config에서 빠진 케이스는 **`holdout`으로 재입대** (`REGRESSION_READMIT_SPLIT`)
- `split` / `run` 시작 시 `RegressionLog.apply(casebook)` 수행

## command evaluator

`run`은 `command_evaluator(workspace_root)`를 사용한다.

- 각 케이스 `command`를 `workspace_root`에서 실행
- exit 0 → pass, 그 외 · 타임아웃 · 누락 바이너리 → fail
- 기본 프로세스 타임아웃: **900초** (15분)
- command 없음 → fail, `detail="case has no command"`
- in-place 선언: multi-iteration 시 `--restore-cmd` 필요

## 종료 코드 정리

| 코드 | 상황 |
| --- | --- |
| `0` | `validate`/`split`/`inspect` 성공; `run`에서 gated 전부 통과 (`is_complete`) |
| `1` | `run` 완료했으나 gated 미통과 |
| `2` | 설정 오류, `--max-iterations < 1`, in-place+restore 누락, `inspect`에 리포트 없음, 알 수 없는 하위 명령 |

## 최소 워크플로

<Steps>
  <Step title="스위트 검증">
    `scripts/acceptance-loop validate --config suite.json` 이 train/holdout 카운트를 출력하고 exit 0인지 확인한다.
  </Step>
  <Step title="스플릿 매니페스트 (선택)">
    `scripts/acceptance-loop split --config suite.json` 후 `split.json` / `split.md`를 검토한다. holdout이 visible 트리에 없는지 확인한다.
  </Step>
  <Step title="루프 실행">
    multi-iteration이면 restore 명령을 넣고 run 한다. stdout Markdown의 Final·Accepted candidates·스플릿 표를 본다.
  </Step>
  <Step title="감사">
    `history/visible/iterations/*/decision.json`과 `report.json`을 확인한다. `scripts/acceptance-loop inspect --run-dir …`로 최종 JSON을 다시 출력할 수 있다.
  </Step>
</Steps>

## 실패 모드

| 증상 | 원인 |
| --- | --- |
| `split holdout must include at least one case` | train만 있는 스위트 |
| `train and holdout must cover the same seed strata` | stratum 집합 불일치 |
| `unsupported proposer runtime` | `claude`/`codex`/`cursor-agent` 외 |
| `… mutates the workspace in place; pass on_reject…` | multi-iter + restore 없음 |
| `--max-iterations must be at least 1` | CLI 플래그 검증 |
| `no report at …/report.json` | inspect 대상 런 미완료 |
| 후보 없음 반복 | CLI 실패 또는 `candidate.json` 미작성 |
| holdout 교환 거절 | train↑ holdout↓ 로 합 통과 수 동일 |

## Related pages

<CardGroup>
  <Card title="증거와 수락" href="/evidence-and-acceptance">
    워커 self-report와 독립 검증, acceptance 판정 규칙의 개념 층.
  </Card>
  <Card title="CLI 레퍼런스" href="/cli-reference">
    `scripts/` 공개 명령 표와 --help 동기화 계약.
  </Card>
  <Card title="dispatch-gate 레퍼런스" href="/dispatch-gate-reference">
    디스패치 허용·ledger. 수락 루프 이전 단계.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    preflight·undecidable·복구 프로브 등 운영 장애.
  </Card>
</CardGroup>

---

## 19. 방어 인벤토리

> 디스패치 게이트, 모델 신원 프로브, placement·empty-seat·duplicate, redaction, revival, progressive onboarding 가드 표.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/19-page-19.md
- Generated: 2026-08-07T07:06:12.225Z

### Source Files

- `docs/public/defense-inventory.md`
- `src/master_runtime/core/dispatch_gate.py`
- `src/master_runtime/core/succession.py`
- `scripts/model-identity-probe`
- `scripts/model-drift-audit`
- `scripts/redaction-scan.sh`
- `master-ops/docs/MASTER-OPERATIONS.md`

---
title: "방어 인벤토리"
description: "디스패치 게이트, 모델 신원 프로브, placement·empty-seat·duplicate, redaction, revival, progressive onboarding 가드 표."
---

런타임이 이미 비용을 치른 실패 모드를 막는 가드는 `scripts/dispatch-gate`, `scripts/master-succeed`, `scripts/model-identity-probe`, `scripts/model-drift-audit`, `scripts/redaction-scan.sh`, `scripts/redaction-inventory`, 그리고 `master-ops/ONBOARDING.md` 라우터에 고정되어 있다. 표의 각 행은 트리에 존재하는 경로와 측정 가능한 판정(exit 코드·`ReasonCode`·ledger 필드)을 가지며, 열 수 없는 경로의 주장은 인벤토리에 넣지 않는다.

<Info>
이 페이지는 개념 카탈로그가 아니라 **게이트 표**다. 플래그·스키마 전량은 [dispatch-gate 레퍼런스](/dispatch-gate-reference), [master-succeed 레퍼런스](/succession-cli-reference), [Redaction gates](/redaction-gates)를 본다.
</Info>

## 가드 표

| 가드 | 막는 것 | 위치 | 증명 측정 | 깨질 때 |
| --- | --- | --- | --- | --- |
| 티어 × fan-out 디스패치 게이트 | 캡된 티어의 무음 비용 폭증; 오너 승인 없는 top-tier | `scripts/dispatch-gate`, `src/master_runtime/core/dispatch_gate.py`, `master-ops/model-tier-policy.json`; top 승인은 `master-ops/scripts/dispatch --top-approved` | `check` → `GateDecision` + 안정 reason (`TIER_FANOUT_CAP`). 정책 v2+는 rolling window 누적 agent 캡. 출하 정책에서 top은 uncapped이며 launcher가 `--top-approved "<reason>"` 없으면 거부. dry-run은 `--no-record` | 정책 불가독·malformed·경로 없음 → `TIER_POLICY_UNAVAILABLE` deny; `master-ops/scripts/dispatch` 우회 시 top 질문 없음 |
| Ledger된 게이트 판정 | 정책 근거 없는 “게이트가 허용했다” | 동일 게이트 `--ledger` JSONL; `dispatch-gate report` | 각 판정에 policy path, `tier_policy_sha256`, 소비 tier. `report`가 span에 복수 policy 행을 보이면 단일 정책으로 판단된 것이 아님 | ledger 경로 쓰기 불가 또는 저장소 밖 경로 |
| 세션 transcript 모델 신원 | 선언 모델·TUI status line 신뢰 | `scripts/model-identity-probe`, `scripts/model-drift-audit`; charter §5·§9 | probe는 assistant 모델 필드를 JSONL에서 읽음 (`--transcript`). exit 2 = drift/undecidable; expected 없이 exit 0은 무주장 INFO. drift-audit은 전 턴 transition 보고, exit 2 = undecidable. status line은 증거가 아님 | transcript 형식·위치·권한 변경 → `MODEL-PROBE DRIFT:` + exit 2 |
| `register` 시점 모델 검증 | `check` 선언만 맞고 실실행이 다른 경우 | `scripts/dispatch-gate register --model-probe-cmd` | ledger `model_declared`, `model_measured`, `model_verified`. `MODEL_TIER_ESCALATION` deny; `MODEL_UNVERIFIED` / `MODEL_PROBE_FAILED`는 warn 후 register (모델 보고 불가 runtime이 게이트를 끄지 않도록) | probe 실패·빈 출력·nonzero → `MODEL_PROBE_FAILED` 기록(무음 아님) |
| 마스터 spawn placement | 잘못된 worktree에 마스터 착석 | `scripts/master-succeed spawn --expected-placement` | 요청 vs 실제, 실제 vs 독립 expected 비교; 불일치 exit **26** `SPAWN_PLACEMENT_MISMATCH`. three-set: host selector, workspace root cwd, expected namespace session artifact | expected 인자 오류·workspace 밖 프로세스·artifact 미측정 → exit 26, 진행 없음 |
| Empty-seat (founding) | 재진입·중단 설치로 같은 seat에 두 번째 마스터 | `master-ops/onboarding/09-spawn.md` | `orca terminal list --worktree <selector> --json`이 seat 단말 **0개**. leftover seat-check/이전 마스터는 hard stop | list 실패·malformed JSON·selector 미해석 → 점유 검증 불가, spawn 거부 |
| 런타임 duplicate master | succession/resume 후 이중 마스터 | `scripts/master-succeed check-duplicates` (`detect_duplicate_instances`) | marker 스캔, `--self-handle` 제외; 비어 있지 않으면 finding(소프트 경고 아님) | handle/marker 누락 또는 Orca introspection 불가 → `SuccessionError` exit 2 |
| 범위 명시한 redaction scan | generic만 스캔하고 초록불 | `scripts/redaction-scan.sh`; org 규칙 `REDACTION_EXTRA_PATTERNS`; 강제 `REDACTION_REQUIRE_EXTRA=1` | coverage 출력(files/rules). exit 0 clean, 1 findings, 2 cannot decide. `REDACTION_REQUIRE_EXTRA=1`이면 빈/누락 extra → exit 2(generic-only 스캔 금지) | require 모드에서 extra 없음/비어 있음 → exit 2 |
| Redaction inventory (역검사) | 트리에 있는데 규칙이 안 잡는 토큰 | `scripts/redaction-inventory` | exit 0 후보 없음, 1 후보 발견(정상 finding, secret 판정 아님), 2 cannot decide | 규칙 파싱 실패·git 범위 불가 → exit 2 |
| Revival (frozen session) | 다른 기기에서 retired 마스터 재개 | `master-ops/docs/runbooks/succession-boot-card.md` | retirement 완료 = process·pane·tty 세 소멸 측정. boot 시 lineage session id로 프로세스 argv 스캔 | lineage id 미기록·다른 머신·process introspection 불가 → 탐지 불가(degrade, 가짜 pass 아님) |
| Progressive onboarding load | 전체 가이드 일괄 로드로 overload/improvisation | `master-ops/ONBOARDING.md` + `master-ops/onboarding/*.md` | 라우터: 턴당 step 파일 1개, Verify 통과 전 next 금지. 모든 host/model 강제 | 라우터 우회·경로 추측·Verify 없이 다음 파일 로드(코드가 읽기 순서를 강제하지는 않음) |

## 디스패치 게이트

### 표면

```console
$ scripts/dispatch-gate --ledger <path> check \
    --runtime <name> --model <id> --contract <path> \
    --agents N --est-chars N --completion-channel orchestration
$ scripts/dispatch-gate register \
    --job-id <id> --probe-cmd '<cmd>' \
    [--declared-model <id>] [--model-probe-cmd '<cmd>']
$ scripts/dispatch-gate report
```

`master-ops/scripts/dispatch`는 check → task-create → inject → register 파이프라인을 묶고, top-tier는 게이트 ledger 전에 `--top-approved`를 강제한다.

### 정책 (`master-ops/model-tier-policy.json`)

| 키 | 출하 값 / 의미 |
| --- | --- |
| `version` | `2` — tier + fan-out |
| `tiers.top` / `tiers.efficient` | 모델 id 집합(casefold 멤버십) |
| `fanout_caps` | 티어별 윈도우 누적 agent 상한; **키 없음 = uncapped** |
| `window_seconds` | 출하 `86400` (코드 기본 `DEFAULT_TIER_WINDOW_SECONDS`와 동일) |
| 출하 `fanout_caps` | `"unknown": 8` only — top cap 제거(2026-08-05 owner directive) |

Top-tier 사용은 숫자 캡이 아니라 `scripts/dispatch --top-approved "<reason>"` 오너 질문이다. reason은 run log에 인쇄된다.

### 문자 예산 기본값

| 한도 | 기본 |
| --- | --- |
| single dispatch | `500_000` chars |
| batch dispatch | `1_000_000` chars |
| duplicate window (ledger 관련) | `30 * 60` seconds |

### 주요 `ReasonCode`

| 코드 | 역할 |
| --- | --- |
| `OK` | 허용 |
| `TIER_FANOUT_CAP` | 윈도우 누적이 캡 초과 |
| `TIER_POLICY_UNAVAILABLE` | 정책 로드 실패 → deny |
| `TIER_UNKNOWN_MODEL` / `TIER_POLICY` | 미등록 모델 또는 정책 위반 |
| `BUDGET_EXCEEDED` | est chars 한도 |
| `NO_COMPLETION_CHANNEL` / `NO_MODEL` | 요청 검증 실패 |
| `CONTRACT_UNREADABLE` / `PATH_OUTSIDE_KNOWN_ROOTS` | 계약 경로 |
| `MODEL_TIER_ESCALATION` | 측정 모델이 선언보다 더 엄격한 티어로 상향 |
| `MODEL_MISMATCH` | 불일치(상향 아님) — 경고 경로 |
| `MODEL_UNVERIFIED` / `MODEL_PROBE_FAILED` | 측정 불가/probe 실패 — 경고 후 register 가능 |

<Warning>
`MODEL_UNVERIFIED`와 `MODEL_PROBE_FAILED`는 **deny가 아니다**. 모델 보고 수단이 없는 runtime이 게이트를 꺼 버리지 않도록 warn+register다. 상향 측정만 `MODEL_TIER_ESCALATION`으로 막는다.
</Warning>

### Ledger 필드 (모델 검증)

`register` 경로는 최소한 다음을 남긴다.

- `model_declared`
- `model_measured`
- `model_verified`
- `tier_policy_sha256` (정책 path digest)

`--no-record` check는 ledger 행과 ticket을 남기지 않아 검사 자체가 예산/캡을 소비하지 않는다. override로 통과한 ALLOW도 윈도우 카운트에 포함된다(refund 없음).

## 모델 신원 프로브

선언·TUI status line은 측정이 아니다. 측정은 session transcript JSONL의 assistant 모델 필드다.

### `scripts/model-identity-probe`

| 항목 | 내용 |
| --- | --- |
| 입력 | `--transcript` 또는 runtime config `transcript_glob` / env |
| expected | `--expect` 또는 `MODEL_IDENTITY_EXPECT` |
| exit 0 | 전부 expected 일치, 또는 expected 없음(INFO, 무주장) |
| exit 2 | drift, unreadable, unconfigured, invalid limit |

출력 접두사: `MODEL-PROBE OK`, `MODEL-PROBE INFO`, `MODEL-PROBE DRIFT:`.

### `scripts/model-drift-audit`

| 항목 | 내용 |
| --- | --- |
| 역할 | transcript 전 assistant 턴 transition 보고 |
| exit 0 | transition 없음(및 expected 일치 시) |
| exit 1 | transition 또는 expected 불일치 |
| exit 2 | undecidable (transcript 없음/0 assistant turns) |

Succession audit는 최근 N턴 probe만으로 과거 drift를 닫지 않는다. boot card는 전 구간 walk에 drift-audit을 요구한다.

## Placement · empty-seat · duplicate

### Placement (`spawn`)

`spawn_successor(..., expected_placement=...)` 흐름:

1. `orca terminal create --worktree <selector> ... --json`
2. 응답 `worktreeId` vs 요청 selector — 불일치 → `SPAWN_WORKTREE_MISMATCH` (22), 생성 단말 close
3. `expected_placement` 제공 시 `worktreeId` vs expected — 불일치 → **`SPAWN_PLACEMENT_MISMATCH` (26)**
4. handle liveness: create 이전 snapshot에 없던 live handle이어야 함

절차 three-set(런북): host selector, process cwd under workspace root, session artifact in expected namespace.

### Empty-seat (founding only)

`master-ops/onboarding/09-spawn.md`: spawn 전 `orca terminal list --worktree <selector> --json`이 **0 terminals**. seat-check leftover 또는 중단된 이전 마스터가 있으면 보고 후 중단.

Reverify/Upgrade 모드는 라우터에서 spawn 자체를 차단한다(두 번째 마스터는 convenience가 아니라 incident).

### Duplicate (runtime)

```console
$ scripts/master-succeed check-duplicates --session-marker <marker> --self-handle <handle>
```

`detect_duplicate_instances`는 동일 marker 세션에서 `self_handle`을 제외한 나머지를 반환한다. 비어 있지 않으면 finding.

## Redaction

### `scripts/redaction-scan.sh`

| 환경 변수 | 효과 |
| --- | --- |
| `REDACTION_EXTRA_PATTERNS` | org 규칙 파일 경로 |
| `REDACTION_REQUIRE_EXTRA=1` | extra 없음/비어 있음 → exit **2** (generic-only 초록불 금지) |

엔진은 `gitleaks` + `config/gitleaks.toml`. 스캔 끝 줄에 mode, files, org-rules 수를 인쇄한다.

| exit | 의미 |
| --- | --- |
| 0 | clean |
| 1 | findings |
| 2 | cannot decide (gitleaks 없음, require-extra 위반, engine error 등) |

### `scripts/redaction-inventory`

scan의 역: 규칙이 커버하지 않는 후보 토큰. 후보 = secret 판정이 아니다. exit 1은 정상 finding.

## Revival

`retire_predecessor` / boot card 규칙:

- `CLOSED` = pane·process·tty **세 소멸 측정**. close 명령 반환값은 증거가 아님
- `CLOSED_PARTIAL` = pane 없음 + 일부 probe skip — full pass로 읽지 않음
- 동결 세션은 폰/원격에서도 resume 가능. 실측: 모바일 resume으로 retired 마스터 4개 동시 부활
- boot 및 오너 신고 시: `docs/lineage/MASTER-LINEAGE.md`의 session id를 프로세스 argv에서 스캔 → 활동 회수 → terminate → tty hangup → 재확인

## Progressive onboarding

`master-ops/ONBOARDING.md`는 라우터다. 전 설치 동안 로드를 유지하는 파일은 라우터뿐이며 step 파일은 턴당 하나다.

| 규칙 | 내용 |
| --- | --- |
| 로드 | `onboarding/<step>.md` 1개/턴; 일괄 읽기 금지 |
| 진행 | 현재 step **Verify** 통과 전 next 파일 금지 |
| 모드 | Founding `00`→`10`; Reverify/Upgrade는 단일 파일, **spawn 차단** |
| 강제 범위 | 모든 host·모든 model (강한 모델도 monolith 면제 없음) |

코드가 agent 읽기 순서를 기계적으로 강제하지는 않는다. 위반은 절차·컨트롤 플로우 실패이며, control-flow 테스트는 파일 inventory만 핀한다.

## 기판(substrate)과 안정성

| 종류 | 예 | 변경 시 |
| --- | --- | --- |
| 계약 표면 | 이 repo 스크립트·exit 코드, `git`, 문서화된 `orca terminal list` / `orca status` | 이 저장소 변경 필요 |
| vendor-internal | host session 파일 layout/위치/이름 | 가드 degrade |

Degrade 원칙: **가짜 pass 금지**.

- model probe → `MODEL-PROBE DRIFT:` + exit 2
- dispatch register → `MODEL_PROBE_FAILED` ledger 경고
- revival scan 불가 → 탐지 불가 상태로 기록; “깨끗함”으로 치환하지 않음

실측 예(2026-08-03): 신규 grok worker 첫 `register`에서 session 파일 미존재 → `MODEL_PROBE_FAILED`, ledger에 unverified 기록.

```text
[contracted]  scripts/*  git  orca CLI (documented)
      | fail-closed / recorded reasons
      v
[vendor]     session JSONL paths, host tty/process layout
      | unreachable → exit 2 / MODEL_PROBE_FAILED / skip scan
      v
  never "green without scope"
```

## 인벤토리에 넣지 않는 것

선호 설정, optional skills, 위험한 동작을 게이트하지 않는 도구. “실패할 수 없는 게이트”와 “실행되지 않는 측정”은 방어가 아니다.

## 운영 신호 요약

| 증상 | 가드 신호 |
| --- | --- |
| fan-out 거부 | `TIER_FANOUT_CAP` |
| 정책 파일 깨짐 | `TIER_POLICY_UNAVAILABLE` |
| top 승인 없음 | `scripts/dispatch` 거부 (`--top-approved` 요구) |
| 모델 drift | probe exit 2 / `MODEL-PROBE DRIFT:` |
| register 미검증 | ledger `MODEL_PROBE_FAILED` / `MODEL_UNVERIFIED` |
| 티어 상향 측정 | `MODEL_TIER_ESCALATION` deny |
| 잘못된 master seat | spawn exit **26** |
| seat 점유 | founding list non-empty hard stop |
| redaction 범위 불명 | require-extra → exit 2 |
| revival 의심 | lineage session id in process argv |

## Next

<CardGroup>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    check → dispatch → register, 계약 해시·ledger, model probe
  </Card>
  <Card title="dispatch-gate 레퍼런스" href="/dispatch-gate-reference">
    플래그, ledger 스키마, reason code, 티켓 TTL
  </Card>
  <Card title="Clean succession" href="/succession">
    placement spawn, retire, revival, lineage 필드
  </Card>
  <Card title="master-succeed 레퍼런스" href="/succession-cli-reference">
    spawn/retire/check-duplicates 옵션과 exit 코드
  </Card>
  <Card title="Redaction gates" href="/redaction-gates">
    redaction-scan 범위, REDACTION_REQUIRE_EXTRA, inventory
  </Card>
  <Card title="프로그레시브 온보딩" href="/onboarding">
    ONBOARDING 라우터, 1 step/turn, Verify, founding spawn
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    placement mismatch, MODEL_PROBE_FAILED, seat 중복, revival
  </Card>
</CardGroup>

---

## 20. Worker reap

> lease 상태 issued→reaped, settled 검증, terminal close, worktree 정리, --dry-run·--ledger, 거부 exit 코드.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/20-worker-reap.md
- Generated: 2026-08-07T07:06:21.639Z

### Source Files

- `docs/runbooks/worker-reap.md`
- `scripts/worker-reap`
- `src/master_runtime/core/worker_reap.py`
- `tests/test_worker_reap.py`
- `docs/public/reference.md`
- `docs/public/delegation-and-review.md`

---
title: "Worker reap"
description: "lease 상태 issued→reaped, settled 검증, terminal close, worktree 정리, --dry-run·--ledger, 거부 exit 코드."
---

`scripts/worker-reap`는 settled 디스패치의 워커 자원을 회수하는 공개 CLI입니다. `orca orchestration dispatch-show`로 상태를 읽고, settled가 아니면 거부(exit 3)하며, settled이면 `orca terminal close`로 터미널을 닫고 안전할 때만 worktree를 제거합니다. 구현은 `WorkerReaper`(`src/master_runtime/core/worker_reap.py`)이고, 감사 기록은 `--ledger`로 넘긴 JSONL에 `event: "reap"` 한 줄로 남습니다. 자동 스윕·타이머 재프는 없습니다.

## 역할과 경계

완료 보고 처리의 끝은 검증·머지 결정만이 아니라 **reap + ledger 기록**입니다. settled 워커를 방치하면 harness debris가 됩니다. 잘못된 reap(열린 작업·미머지 브랜치 삭제) 비용이 높고 스킵 비용은 낮으므로, 애매한 경우는 항상 “남기고 사유를 보고”합니다.

| 구분 | 동작 |
|------|------|
| 공개 표면 | `scripts/worker-reap` |
| 코어 | `WorkerReaper`, `DispatchState`, `ReapRecord`, `ReapError` |
| 상태 조회 | `orca orchestration dispatch-show --json` (`--task` 또는 `--dispatch`) |
| 터미널 | `orca terminal close <terminal_id>` |
| worktree | `git worktree remove` (안전 조건 통과 시에만) |
| 감사 | `--ledger` JSONL append (`event: "reap"`) |
| 잔여 탐지 | `ReapObservability.unreaped_settled_leases()` (`work_ledger`) |

개념상 lease 수명은 `issued` → `running` → `submitted` → `accepted` → `reaped`입니다. 재프 게이트가 실제로 검사하는 Orca 디스패치 상태는 아래 settled 집합입니다.

## Lease·settled 판정

`DispatchState.is_settled()`는 status를 대문자로 정규화한 뒤 다음만 settled로 봅니다.

| status | settled | success (`is_success`) |
|--------|---------|------------------------|
| `COMPLETED` | 예 | 예 |
| `ACCEPTED` | 예 | 예 |
| `FAILED` | 예 | 아니오 |
| `ABANDONED` | 예 | 아니오 |
| `RUNNING` | 아니오 | 아니오 |
| `REGISTERED` | 아니오 | 아니오 |
| 그 외(예: `ISSUED`) | 아니오 | 아니오 |

`task_id`/`dispatch_id`가 둘 다 없으면 `ReapError` exit **2**. settled가 아니면 exit **3** — 메시지 형태:

```text
Dispatch <id> is not settled (status: RUNNING); refusing to reap open dispatch
```

## 실행 흐름

```mermaid
sequenceDiagram
  participant CLI as scripts/worker-reap
  participant WR as WorkerReaper
  participant O as orca
  participant G as git
  participant L as ledger JSONL

  CLI->>WR: reap(task_id|dispatch_id, execute)
  WR->>O: orchestration dispatch-show --json
  alt not settled
    WR-->>CLI: ReapError exit 3
  else settled
    opt terminal_id present and execute
      WR->>O: terminal close
    end
    opt worktree_path present
      WR->>G: status / branch / merge-tree
      alt clean and included in origin/main
        opt execute
          WR->>G: worktree remove
        end
      else unsafe or ambiguous
        Note over WR: worktree_left + reason
      end
    end
    opt execute and --ledger
      WR->>L: append event=reap
    end
    WR-->>CLI: ReapRecord JSON
  end
```

### 1. Settled 검증

`dispatch-show` JSON에서 `dispatch_id`, `task_id`, `terminal_id`, `status`, `worktree_path`를 읽습니다. stdout 파싱 실패 시 exit **4**. orca 자체 실패 시 runner가 반환한 code를 그대로 전파합니다.

### 2. Terminal close

`terminal_id`가 있으면:

- `execute=True`: `orca terminal close <terminal_id>` 호출. 실패 시 `ReapError` (runner code).
- 성공·dry-run 모두 `actions_taken`에 `terminal_closed:<terminal_id>` 기록.

터미널이 비어 있으면 이 단계는 생략됩니다.

### 3. Worktree 안전 검사 후 제거

`worktree_path`가 있을 때만 검사합니다. 제거 조건은 **둘 다** 참이어야 합니다: working tree clean **그리고** 브랜치 변경이 `origin/main`에 포함.

검사 순서:

1. 경로 존재 — 없으면 leave
2. `git status --porcelain` — 비어 있지 않으면 leave (`Worktree has uncommitted changes`)
3. `git branch --show-current` — 실패·detached HEAD면 leave
4. `git branch -a --merged origin/main` — 현재 브랜치가 목록에 있으면 포함으로 간주
5. 토폴로지 미머지 시 squash 대응: `origin/main^{tree}`와 `git merge-tree --write-tree origin/main HEAD` 결과가 같으면 “이미 포함”으로 제거 허용

통과 시 `git worktree remove <path>` → `worktree_removed:<path>`.  
실패·애매 시 제거하지 않고 `worktree_left:<path>:<reason>`. 검사 예외도 leave로 흡수합니다 (`Check failed: ...`).

제거 명령 자체가 실패하면 `ReapError`로 중단합니다 (exit = runner code, 기본 실패 경로 1).

### 4. 레코드·ledger

`ReapRecord` 필드:

| 필드 | 설명 |
|------|------|
| `task_id` | 디스패치의 task |
| `dispatch_id` | 디스패치 ID |
| `terminal_id` | 닫은(또는 닫을) 터미널 |
| `worktree_path` | 검사 대상 경로 또는 null |
| `actions_taken` | `;`로 이은 액션 문자열 |
| `timestamp` | Unix epoch float (`time.time()`) |

`--ledger`가 있고 `execute=True`일 때만 append. dry-run은 ledger에 쓰지 않습니다. 부모 디렉터리는 자동 생성됩니다.

Ledger 한 줄 스키마 (`sort_keys=True`, compact separators):

```json
{"actions_taken":"terminal_closed:term_123;worktree_left:/path/to/wt","dispatch_id":"dispatch_xyz","event":"reap","task_id":"task_abc123","terminal_id":"term_123","timestamp":1722787200.0,"ts":1722787200.0,"worktree_path":"/path/to/wt"}
```

`ts`와 `timestamp`가 둘 다 들어갑니다 (`ts`는 append 시 복제).

## CLI

```bash
scripts/worker-reap (-h) (--task-id TASK_ID | --dispatch-id DISPATCH_ID)
                   [--ledger LEDGER] [--dry-run] [--json]
```

<ParamField body="--task-id" type="string" required>
Task ID로 디스패치를 조회합니다. `--dispatch-id`와 상호 배타, 둘 중 하나 필수.
</ParamField>

<ParamField body="--dispatch-id" type="string" required>
Dispatch ID로 직접 조회합니다. `--task-id`와 상호 배타.
</ParamField>

<ParamField body="--ledger" type="path">
reap 감사 JSONL 경로. 실행 모드에서만 append. 절차상 감사 기록이 필요하면 지정합니다.
</ParamField>

<ParamField body="--dry-run" type="boolean">
close/remove/ledger 없이 계획된 `actions_taken`만 산출. 응답에 `"dry_run": true`.
</ParamField>

<ParamField body="--json" type="boolean">
stdout를 compact JSON으로 출력. 기본은 indent=2 pretty JSON.
</ParamField>

### 표준 절차

<Steps>
  <Step title="Dry-run으로 settled·액션 확인">
    ```bash
    scripts/worker-reap --task-id <task-id> --dry-run
    # 또는
    scripts/worker-reap --dispatch-id <dispatch-id> --dry-run
    ```
    exit 3이면 아직 열린 디스패치입니다. `orca orchestration dispatch-show`로 상태를 확인한 뒤 완료를 기다립니다.
  </Step>
  <Step title="실행과 ledger 기록">
    ```bash
    scripts/worker-reap \
      --task-id <task-id> \
      --ledger master-ops/ledger/dispatch-ledger.jsonl
    ```
    성공 시 exit 0, stdout에 `record` + `dry_run: false`.
  </Step>
  <Step title="부분 reap 해석">
    `actions_taken`에 `terminal_closed`만 있고 `worktree_left`가 있으면 터미널은 닫혔고 worktree는 수동 정리 대상입니다. dirty·unmerged·I/O 사유를 읽고 커밋·머지·수동 삭제 후 필요 시 다시 재프합니다.
  </Step>
</Steps>

<RequestExample>
```bash
scripts/worker-reap --task-id task_abc123 --dry-run
```
</RequestExample>

<ResponseExample>
```json
{
  "record": {
    "task_id": "task_abc123",
    "dispatch_id": "dispatch_xyz",
    "terminal_id": "term_123",
    "worktree_path": "/path/to/worktree",
    "actions_taken": "terminal_closed:term_123;worktree_removed:/path/to/worktree",
    "timestamp": 1722787200.0
  },
  "dry_run": true
}
```
</ResponseExample>

## Exit 코드

| Code | 의미 |
|------|------|
| 0 | 성공 (dry-run 포함, settled 통과 후 레코드 출력) |
| 1 | 기타 실패 (터미널 close 실패, worktree remove 실패, 타임아웃 등; runner code가 1인 경우) |
| 2 | `--task-id`/`--dispatch-id` 없음 (argparse 상호배타 그룹 또는 API 둘 다 누락) |
| 3 | 디스패치 not settled — open 재프 거부 |
| 4 | dispatch-show stdout JSON 파싱 실패 |

stderr에 `ReapError` 메시지가 찍히고 위 코드로 종료합니다. orca/git runner가 0이 아닌 code를 주면 그 값이 그대로 전달될 수 있습니다.

## 안전 가드

| 가드 | 동작 |
|------|------|
| Never auto-kill | 타이머·백그라운드 스윕 없음. 오퍼레이터 또는 마스터 시퀀스의 명시 호출만 |
| Never reap open | `RUNNING`/`REGISTERED` 등 non-settled → exit 3 |
| Ambiguous worktree stays | dirty, unmerged, unique changes, missing path, git 메타 오류, 검사 예외 → leave + reason |
| Squash-aware include | ancestry 없이도 `merge-tree` 트리 동등으로 포함 증명 가능 |
| Ledger as evidence | 실행 시 `--ledger`로 `event: "reap"` 감사 흔적 |

핵심 원칙: **잘못된 reap는 비싸고, 스킵은 싸다.**

## 잔여(debris) 관측

`ReapObservability`는 동일 ledger를 읽어 settled인데 reap 이벤트가 없는 디스패치를 반환합니다. 추적 이벤트:

- `dispatch_submitted` — 상태 시드
- `dispatch_completed` — status 갱신
- `reap` — `is_reaped=True`, `reaped_at` from `ts`

```python
from master_runtime.core.work_ledger import ReapObservability

obs = ReapObservability("master-ops/ledger/dispatch-ledger.jsonl")
for dispatch_id, state in obs.unreaped_settled_leases().items():
    print(f"{dispatch_id}: {state.status} (settled, not yet reaped)")
```

settled 집합은 reaper와 동일합니다: `COMPLETED`, `ACCEPTED`, `FAILED`, `ABANDONED`.

## actions_taken 토큰

| 토큰 | 의미 |
|------|------|
| `terminal_closed:<id>` | 터미널 close 실행 또는 dry-run 계획 |
| `worktree_removed:<path>` | 안전 통과 후 제거(또는 dry-run 계획) |
| `worktree_left:<path>:<reason>` | 제거하지 않음 + 사유 |

여러 액션은 `;`로 연결됩니다. 예: `terminal_closed:term1;worktree_left:/wt:Worktree has uncommitted changes`.

## 문제 해결

| 증상 | 확인 | 조치 |
|------|------|------|
| `not settled; refusing to reap` | `orca orchestration dispatch-show --task <id> --json` | 완료·실패·포기까지 대기 후 재시도 |
| `Worktree has uncommitted changes` | worktree `git status` | 커밋/푸시 또는 수동 정리 후 재프, 또는 터미널만 닫힌 부분 reap 수용 |
| `not merged` / `branch changes are not included` | 브랜치 vs `origin/main`, squash 여부 | 머지 후 재프, 또는 수동 worktree 정리 |
| `Could not parse dispatch JSON` (exit 4) | dispatch-show stdout | Orca/selector·JSON 출력 확인 |
| `Failed to fetch dispatch` | orca exit ≠ 0 | task/dispatch ID, Orca 런타임 가용성 |
| `Failed to close terminal` | terminal id | 이미 닫힌 세션·권한·Orca 상태 |
| `Failed to remove worktree` | `git worktree list` | 잠금·경로·수동 `git worktree remove` |

subprocess 타임아웃은 30초입니다. 타임아웃 시 runner는 code 1과 타임아웃 메시지를 반환합니다.

## 마스터 시퀀스에서의 위치

차터 **Worker Reap Duty**: 완료 검증·수락 후 `scripts/worker-reap --task-id <id> --ledger <path>`로 회수합니다. 워커 PR 머지 후에도 동일 호출로 자원을 회수하고, 애매한 worktree는 제거하지 않습니다. 디스패치 게이트(`dispatch-gate` check → launch → register)와 acceptance 판정 이후 단계이며, 재프 자체가 acceptance를 대체하지 않습니다.

## Related pages

<CardGroup cols={2}>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    check → dispatch → register, 계약 해시·ledger, 완료 채널.
  </Card>
  <Card title="증거와 수락" href="/evidence-and-acceptance">
    self-report와 독립 검증, acceptance 판정 — reap 직전 단계.
  </Card>
  <Card title="CLI 레퍼런스" href="/cli-reference">
    `scripts/` 공개 명령 표와 worker-reap 요약.
  </Card>
  <Card title="dispatch-gate 레퍼런스" href="/dispatch-gate-reference">
    ledger 스키마·reason code·티켓 TTL.
  </Card>
  <Card title="방어 인벤토리" href="/defense-inventory">
    디스패치·placement·redaction 등 가드 표.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    placement mismatch, probe 실패, seat·revival 복구.
  </Card>
</CardGroup>

---

## 21. Troubleshooting

> preflight BLOCKED, placement mismatch, MODEL_PROBE_FAILED, undecidable exit 2, seat 중복, revival, 복구 프로브.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/21-troubleshooting.md
- Generated: 2026-08-07T07:09:27.219Z

### Source Files

- `docs/public/defense-inventory.md`
- `src/master_runtime/core/recovery.py`
- `scripts/master-recover`
- `scripts/model-identity-probe`
- `scripts/model-drift-audit`
- `master-ops/docs/runbooks/succession-boot-card.md`
- `master-ops/docs/runbooks/error-and-logging.md`
- `tests/test_recovery.py`

---
title: "Troubleshooting"
description: "preflight BLOCKED, placement mismatch, MODEL_PROBE_FAILED, undecidable exit 2, seat 중복, revival, 복구 프로브."
---

런타임 장애는 `scripts/onboarding-preflight.sh`, `scripts/master-succeed`, `scripts/dispatch-gate`, `scripts/model-identity-probe`, `scripts/model-drift-audit`, `scripts/master-recover`, 그리고 `master-ops/docs/runbooks/succession-boot-card.md`의 측정 가능한 exit·reason code로 표면화된다. 가드는 확인 불가 상태를 통과로 접지 않으며, exit `2`(undecidable)·`MODEL_PROBE_FAILED`·`SPAWN_PLACEMENT_MISMATCH`(26)는 “성공 아님”으로 기록된다.

## 증상 → 명령 빠른 표

| 증상 | 우선 명령 | 핵심 신호 |
| --- | --- | --- |
| 온보딩 전 FAIL 누적 | `bash scripts/onboarding-preflight.sh` | 요약 `BLOCKED`, process exit `1` |
| 마스터가 잘못된 worktree에 앉음 | `scripts/master-succeed spawn --expected-placement …` | exit `26`, `SPAWN_PLACEMENT_MISMATCH` |
| register 시 모델 검증 실패 | `dispatch-gate register --model-probe-cmd …` | ledger reason `MODEL_PROBE_FAILED` (경고, register는 계속) |
| 최근 턴 모델 드리프트 / 판독 불가 | `scripts/model-identity-probe` | `MODEL-PROBE DRIFT:…`, exit `2` |
| 세션 전체 모델 전환 감사 | `scripts/model-drift-audit` | exit `0` 무전환 / `1` 전환·불일치 / `2` undecidable |
| succession 후 좌석 중복 | `scripts/master-succeed check-duplicates --self-handle … --marker …` | `duplicates` 비어 있지 않음; 인자 누락·introspect 실패 시 exit `2` |
| 은퇴 세션 부활 | lineage session id + process/tty 측정 | process·pane·tty 세 소멸이 측정돼야 retire 완료 |
| 비정상 종료 후 상태 복원 프로브 | `scripts/master-recover --charter … --handoff …` | Recovery Flow 0–6, step status `OK`/`MISS`/`WARN`/`SKIP` (항상 exit `0`) |
| redaction 범위 불명 | `scripts/redaction-scan.sh` | exit `2` cannot decide (`REDACTION_REQUIRE_EXTRA=1` 포함) |

## Preflight BLOCKED

`scripts/onboarding-preflight.sh`는 읽기 전용 필수 체크를 누적한다. FAIL이 하나라도 있으면 요약에 `BLOCKED: fix every FAIL before onboarding`을 찍고 **exit 1**로 끝난다. FAIL이 없으면 `READY`, waiver만 있으면 `READY WITH WAIVERS`.

### 자주 보는 FAIL 라벨

| 라벨 | 의미 | 복구 방향 |
| --- | --- | --- |
| `orca` | CLI 부재·비지원 basename·`status --json`이 `ok:true`가 아님 | `orca` / `orca-dev` / `orca-ide` 노출, Settings에서 Shell command 활성화 후 재측정 |
| `orchestration` | legacy read-only, Run unbound(`run: null`), RPC 불가 | `orca orchestration run-create`로 non-legacy Run 바인딩. 앱 재시작만으로는 legacy coordinator가 안 풀림 |
| `skills` | `orca-cli`·`orchestration` skill 아티팩트 부재 | 글로벌 skill 설치/갱신 후 `--fix` 또는 수동 설치 |
| `redaction-extra` | 조직 규칙 파일 없음·비어 있음·malformed | 규칙 파일을 채워 로드 가능한 규칙 ≥1 확보 |
| `bd` / agent CLI / `gh` 등 | PATH·ops repo·자격 범위 | 해당 바이너리·인증 상태 수정 (`gh` 미인증은 보통 WARN) |

```console
$ cd "{{RUNTIME_ROOT}}"
$ ORCA_AGENT_CLI="<agent-cli>" bash scripts/onboarding-preflight.sh
```

<Warning>
필수 체크를 통째로 건너뛰지 않는다. 한 체크만 합법적으로 완화할 때는 `PREFLIGHT_WAIVE=<check-label>`(쉼표 구분)로 FAIL→인쇄된 waiver로 내린다. 오타 waiver는 매칭되지 않으며 해당 체크는 계속 강제된다.
</Warning>

### Orchestration 함정

| 측정 상태 | 동작 | 조치 |
| --- | --- | --- |
| `legacy_read_only` | 읽기는 응답, 쓰기는 `effectsApplied:false`로 드롭 | 새 Run 생성 |
| unbound / `run_required` | task family 실패 | `run-create` 후 preflight 재실행 |
| binding drop 후 `count:0` | 빈 mailbox와 동일하게 보임 | Run 재바인딩 후 재측정 |

`gitleaks`·`ctx` 부재는 preflight에서 **WARN**(마스터 기동을 막지 않음). 다만 publish 경로의 redaction 엔진 부재는 게이트 exit `2`로 이어진다.

## Placement mismatch (`SPAWN_PLACEMENT_MISMATCH` = 26)

`scripts/master-succeed spawn --expected-placement <worktree-id>`는 생성 후 실제 worktree를 요청 selector·독립 expected placement와 비교한다. 불일치 시 터미널 정리 시도 후 **exit 26**.

관련 spawn exit:

| 코드 | 상수 | 의미 |
| --- | --- | --- |
| 20 | `SPAWN_CREATE_ERROR` | create 실패 |
| 21 | `SPAWN_PARSE_ERROR` | spawn JSON 파싱 실패 |
| 22 | `SPAWN_WORKTREE_MISMATCH` | 요청 vs 실제 worktree |
| 23 | `SPAWN_CLOSE_ERROR` | close 실패 |
| 24 | `SPAWN_HANDLE_STALE` | 보고 handle을 live로 신뢰 불가 |
| 25 | `SPAWN_LIST_ERROR` | terminal list 실패 |
| 26 | `SPAWN_PLACEMENT_MISMATCH` | expected placement vs 실제 |

### Placement evidence three-set

`master-ops/docs/runbooks/succession-boot-card.md` 기준, UI 제목·status line은 증거가 아니다.

1. host pane/worktree selector = 의도한 workspace  
2. process cwd ⊆ workspace root  
3. session artifact/log path ∈ 예상 namespace  

```console
$ scripts/master-succeed spawn \
    --expected-placement "<workspace-folder-selector>" \
    # … selector, kickoff, model 등 호스트 인자
```

<Check>
복구: 저장소 worktree가 아닌 **workspace folder seat** selector인지 확인 → 잘못된 터미널 close/reconcile (`orca terminal list`) → expected placement를 독립 측정값으로 다시 공급 → spawn 재시도.
</Check>

## `MODEL_PROBE_FAILED`와 모델 프로브

### `register` 경로

`scripts/dispatch-gate register --model-probe-cmd …`가 프로브 실패·빈 결과·비정상 exit를 만나면 ledger에 `MODEL_PROBE_FAILED`를 **경고**로 남기고 등록은 계속한다(모델 보고 불가 런타임을 강제 off 하지 않기 위함). ledger 필드: `model_declared`, `model_measured`, `model_verified`.

| reason | 효과 |
| --- | --- |
| `MODEL_PROBE_FAILED` | 경고, register 유지 |
| `MODEL_UNVERIFIED` | 선언/측정 부재 경고 |
| `MODEL_MISMATCH` | 선언≠측정 기록 |
| `MODEL_TIER_ESCALATION` | **deny** (상향 티어 측정) |

신규 워커 세션 파일이 아직 없을 때 첫 `register`가 `MODEL_PROBE_FAILED`를 내는 것은 의도된 열화이며, 검증 성공 위장이 아니다.

### `model-identity-probe` (최근 턴)

```console
$ scripts/model-identity-probe --transcript <jsonl> --expect <model>
$ scripts/model-identity-probe   # MOGUI_TRANSCRIPT_GLOB 또는 instance-runtime transcript_glob
```

| 출력 / exit | 의미 |
| --- | --- |
| `MODEL-PROBE OK …` / 0 | 최근 assistant 모델이 `--expect`와 일치 |
| `MODEL-PROBE INFO … nothing asserted` / 0 | expect 없음 — 측정만, 단언 없음 |
| `MODEL-PROBE DRIFT:…` / **2** | 드리프트 또는 undecidable (transcript 부재·JSONL 오류·설정 누락·limit&lt;1) |

해석 순서: `--transcript` → `MOGUI_TRANSCRIPT_GLOB` → `INSTANCE_RUNTIME_CONFIG` / `config/instance-runtime.json`의 runtime `transcript_glob` (최신 mtime 매치). 기본 expect 모델은 하드코딩되지 않는다 (`--expect` 또는 `MODEL_IDENTITY_EXPECT`).

### `model-drift-audit` (전체 세션)

최근 10턴만 보면 중간 전환이 가려진다. succession 감사는 전체 transcript walk를 사용한다.

| exit | 의미 |
| --- | --- |
| 0 | 단일 모델, 전환 없음 |
| 1 | 전환 발견 또는 `--expect` 불일치 |
| **2** | undecidable: transcript 없음·비가독·assistant turn 0·unresolved model only |

```console
$ scripts/model-drift-audit --transcript <path>
$ scripts/model-drift-audit --transcript <path> --expect <model> --json
```

환경 대체: `MODEL_DRIFT_EXPECT`, `MODEL_DRIFT_PROJECTS_DIR`, `MODEL_DRIFT_WORKSPACE_DIR`.

<Warning>
exit `2`를 exit `0`처럼 읽지 않는다. “검사 불가”와 “문제 없음”은 분리된 계약이다. 동일 규칙은 redaction-scan/inventory에도 적용된다.
</Warning>

## Undecidable exit 2 패턴

| 표면 | exit 2 의미 | 통과 위장 여부 |
| --- | --- | --- |
| `model-identity-probe` | drift 또는 판독 불가 | 없음 (`MODEL-PROBE DRIFT`) |
| `model-drift-audit` | undecidable | 없음 |
| `redaction-scan` / `redaction-inventory` | cannot decide (규칙 부재 포함) | 없음; coverage 출력 |
| `master-succeed check-duplicates` | `SuccessionError` (인자·introspect) | 스캔 생략, 조용한 빈 결과 아님 |
| ledger append 권한 등 | 기록 실패 경로 | 환경에 따라 2 또는 실패 |

벤더 세션 경로/포맷 변경 시 가드는 false pass 대신 위 열화로 떨어진다.

## Seat 중복 (empty seat + runtime)

### Founding empty-seat

`master-ops/onboarding/09-spawn.md`: spawn 전 seat의 terminal 수가 **0**이어야 한다.

```console
$ orca terminal list --worktree <selector> --json
```

잔여 seat-check·이전 마스터 터미널이 있으면 hard stop. list 실패·malformed JSON이면 점유를 증명할 수 없어 spawn 거부.

### Runtime `check-duplicates`

```console
$ scripts/master-succeed check-duplicates \
    --self-handle <current-handle> \
    --marker <session-marker>
```

`detect_duplicate_instances`가 `--self-handle`을 제외한 same-marker 세션을 반환한다. 비어 있지 않으면 **finding**(soft warning 아님). 필수 인자 누락·Orca introspect 불가는 exit `2`.

<Check>
중복 시: 어느 handle이 현 마스터인지 확정 → 선임을 retire(아래 revival 측정) → 동일 세션 double-start 금지 → 재스캔.
</Check>

## Revival (은퇴 세션 부활)

동결 세션은 전화·원격 포함 어떤 터미널에서도 재개 가능하다. 측정 사고: 은퇴 마스터 4개가 모바일 resume으로 동시 부활.

### Retire 완료 조건 (세 소멸)

close 명령 반환값이 아니라 측정이 결정한다.

1. **process** — pid 소멸  
2. **pane** — host terminal list에 live handle 없음  
3. **tty** — device·login chain 소멸  

`retire`의 `CLOSED`는 세 소멸 전부. process/tty 스킵이 있으면 `CLOSED_PARTIAL`.

### Boot / 신고 시 절차

1. `docs/lineage/MASTER-LINEAGE.md`의 session id로 실행 중 agent process argv 스캔 (CLI 플래그가 아니라 **session id**가 이식 키).  
2. 히트 시: 동결 이후 활동(도구 호출·repo 쓰기) 확인 → 미응답 owner 지시 회수 → agent process 종료 → hosting tty chain hang-up → tty 소멸 확인.  
3. lineage id 미기록·다른 머신·process introspect 불가면 탐지가 약해질 수 있으나, 그 경우에도 false pass로 “부활 없음”을 단언하지 않는다.

프로세스 사망·호스트 재시작·stale UI handle은 succession이 아니다. live 중복 process가 없음을 증명한 뒤 same-session resume을 먼저 시도한다.

## 복구 프로브 (`master-recover`)

비정상 종료 후 **쓰기·프로세스 변이 없이** Recovery Flow 0–6을 실행한다. 구현: `src/master_runtime/core/recovery.py`, CLI: `scripts/master-recover`.

```console
$ scripts/master-recover \
    --charter <path> \
    --handoff <path> \
    [--ledger <jsonl>] \
    [--repo <path>]… \
    [--monitor-pattern <pgrep>]… \
    [--session-id <id>] \
    [--json]
```

CLI는 리포트 출력 후 **항상 exit 0**. 판단은 step `status` 필드로 한다.

| step | 내용 | 대표 상태 |
| --- | --- | --- |
| `0` | charter 존재 + bootstrap → Role State | `MISS`(charter/bootstrap 실패) 시 이후 fail-closed `SKIP` |
| `1` | handoff 본문 + `Current Role:` / `Role Lock:` | 부재 `MISS`, Role block 누락 `WARN` |
| `1-ledger` | active tracks (ledger 없으면 `SKIP`) | warnings 시 `WARN` |
| `2-3` | repo `git` HEAD/branch/dirty 관찰 | non-git → `WARN` |
| `4` | miss 수집; Trace Archive 검색은 **수동** | 항상 manual_actions 포함 |
| `5` | monitor pgrep; 실행 중 매치 시 takeover 후 재장전 안내 | 매치/`pgrep` 실패 → `WARN` |
| `6` | successor 검증 체크리스트 (active tracks 낭독, PID 비교, lineage append) | status `OK` + manual_actions |

step 0 `MISS`면 step 1/2–3/5는 `SKIP` (`fail-closed after step 0 MISS`). `DUAL_INSTANCE:` bootstrap warning은 manual_actions로 승격된다.

텍스트 모드 예:

```text
0 OK: charter exists; Role State restored: …
1 OK: handoff exists; Role State block present; body present
1-ledger OK: active tracks=1
2-3 OK: …
4 OK: no misses collected; Trace Archive search is manual
5 OK: …
6 OK: successor verification checklist generated
```

## 로깅 경계 (진단 시)

| 로그 | 경로 | 용도 |
| --- | --- | --- |
| hook-fire | `~/.mogui/hook-fire-log.jsonl` | hook 수명주기; `hook-coverage-report` 호환 |
| event | `~/.mogui/event-log.jsonl` | 결정 이벤트 (`ts`, `level`, `event`, `outcome`, `reason`, …) |

`mg_emit`은 fail-open: 로그 append 실패가 guard 결정을 바꾸지 않는다. 이벤트에 raw command·절대경로·자격증명을 넣지 않는다.

## 운영 체크리스트

<Steps>
  <Step title="표면 신호 고정">
    preflight 요약 줄, spawn exit 코드, ledger `reason`, `MODEL-PROBE`/`model-drift-audit` 한 줄, `check-duplicates` JSON을 먼저 확보한다.
  </Step>
  <Step title="계약 표면 vs 벤더 내부">
    스크립트 exit·`orca` 서브커맨드는 계약 표면이다. 세션 파일 위치/스키마는 벤더 내부라 깨지면 exit 2 / `MODEL_PROBE_FAILED`로 열화한다 — 통과로 해석하지 않는다.
  </Step>
  <Step title="좌석·배치 재측정">
    terminal list, cwd, session artifact, lineage session id process 스캔을 독립적으로 다시 잰다. UI 라벨만으로 판단하지 않는다.
  </Step>
  <Step title="복구 프로브 후 수동 항목">
    `master-recover` JSON의 `manual_actions`·`active_tracks`를 수행하고, 필요 시 clean succession(프로모션 감사 → spawn → 세 소멸 retire → lineage)으로 넘긴다.
  </Step>
</Steps>

## Related pages

<CardGroup>
  <Card title="방어 인벤토리" href="/defense-inventory">
    가드별 경로·측정·깨짐 조건 표.
  </Card>
  <Card title="Installation" href="/installation">
    preflight 전제조건과 FAIL/WARN 맥락.
  </Card>
  <Card title="Clean succession" href="/succession">
    handoff·placement spawn·retire·revival·lineage.
  </Card>
  <Card title="master-succeed 레퍼런스" href="/succession-cli-reference">
    spawn/check-duplicates exit 코드와 플래그.
  </Card>
  <Card title="Supervised dispatch" href="/supervised-dispatch">
    check → register, model probe, ledger.
  </Card>
  <Card title="dispatch-gate 레퍼런스" href="/dispatch-gate-reference">
    reason code·ledger 스키마.
  </Card>
</CardGroup>

---

## 22. Contributing

> 5문항 스택 기준, pytest 실행, exit 코드 규약, redaction 게이트, master-ops 템플릿 경계, 릴리스 cut·CHANGELOG.

- Page Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/pages/22-contributing.md
- Generated: 2026-08-07T07:06:48.938Z

### Source Files

- `CONTRIBUTING.md`
- `SECURITY.md`
- `CHANGELOG.md`
- `docs/internal/release-runbook.md`
- `scripts/next-version`
- `.github/workflows/gates.yml`
- `hooks/pre-push`
- `tests/conftest.py`

---
title: "Contributing"
description: "5문항 스택 기준, pytest 실행, exit 코드 규약, redaction 게이트, master-ops 템플릿 경계, 릴리스 cut·CHANGELOG."
---

소규모 단일 유지보수 저장소다. 이슈와 PR 모두 받는다. 런타임 코드는 표준 라이브러리만 쓰고, 검증은 `PYTHONPATH=src python3 -m pytest tests -q`와 redaction 게이트, CI(`.github/workflows/gates.yml`)로 고정한다. 이 페이지는 스택 추가 기준, 테스트·exit 규약, redaction·pre-push, `master-ops/` 템플릿 경계, 릴리스 cut 절차를 묶는다.

## 5문항 스택 기준

하네스에 도구·컴포넌트를 넣기 전에 아래 다섯 답을 PR에 적는다. 답이 없으면 나중에 빼기 논쟁조차 하기 어렵다.

| # | 질문 | 통과 실패 시 |
| --- | --- | --- |
| 1 | API 키가 필요한가? | 보통 단독으로 탈락 |
| 2 | 텔레메트리를 강제하거나, 작업에 필요한 것보다 더 수집하는가? | 보통 단독으로 탈락 |
| 3 | 관리 지점을 하나 더 만드는가? | 보통 단독으로 탈락 |
| 4 | 한 사람 운영을 넘어가도 동작하는가? | 확장성 판단 |
| 5 | 에이전트 컨텍스트 외, 이 도구가 **실제로** 해소하는 문제는 무엇인가? | 답 없으면 선호(preference)로만 허용 |

1–3을 깨는 항목은 의존성처럼 보이지만 구독에 가깝다. 1–4를 통과하고 5에 답이 없으면 선호로 라벨링하고, 그 선호에 게이트를 걸지 않는다.

이 질문은 유지보수자용이다. 설치 쪽은 스택을 고르지 않고 받은 스택에서 무엇을 켤지 고른다. 같은 다섯 문항은 온보딩 단계 `master-ops/onboarding/08-settings-and-skills.md`(라우터 `master-ops/ONBOARDING.md`)에도 실린다.

## 범위

이 저장소가 소유하는 것:

- 워크스페이스 수준 오케스트레이션: 역할, succession, worker dispatch, lineage
- `src/master_runtime/`, `scripts/`

이 저장소가 소유하지 **않는** 것:

- 저장소 로컬 rules·hooks·runbook → [mogui-agent-harness](https://github.com/baksohyeon/mogui-agent-harness)
- 디스패치 대상 에이전트 런타임 자체
- 모델 출력의 옳고 그름(수락 전 검증이 답)

개발·실행 측정 환경은 macOS / Claude Code 기준이다. Orca는 Linux·Windows 빌드를 제공하고 Linux 설치 보고가 있으나, 이 저장소 테스트는 그 플랫폼을 전면 보증하지 않는다. Windows CI 레그는 measurement-only다.

## 테스트 실행

런타임은 stdlib only. 테스트만 pytest가 필요하다.

```console
$ python3 -m pip install pytest
$ PYTHONPATH=src python3 -m pytest tests -q
```

릴리스 cut 쪽 runbook은 `uv run` 경로도 쓴다.

```console
$ PYTHONPATH=src uv run pytest tests -q
```

`tests/conftest.py`는 `tests/`를 `sys.path`에 넣어 헬퍼 공유를 허용한다. `tests/`를 패키지로 만들지 않는다(형제 모듈의 top-level import 유지).

### 머지 기준

| 변경 종류 | 기대 |
| --- | --- |
| 코드 | 그 변경 없이는 실패하는 테스트. PR에 해당 테스트 이름을 적거나, 테스트가 왜 불필요한지 설명 |
| 문서만 | 테스트 불필요. 이전 문구가 무엇이 틀렸는지 적기 |
| 통과 건수만 보고 | 불충분. 테스트 없이 통과한 브랜치와 같은 숫자일 수 있음 |

## Exit 코드 규약

여러 스크립트가 다음 삼원 모델을 쓴다.

| 코드 | 의미 |
| --- | --- |
| `0` | 깨끗함 / 후보 없음 |
| `1` | finding (발견·후보·거절 등 “판정 결과”) |
| `2` | cannot decide / undecidable (도구 없음, 입력 불명, usage, 측정 불가) |

**주의:** 처리되지 않은 예외가 `1`로 나가면 호출자는 crash와 finding을 구분하지 못한다. 실패 경로를 추가할 때 “판정 불가”는 `2`에 올려라. 이 실수가 리뷰에서 가장 큰 클래스다.

### 스크립트별 고정값

| 표면 | 0 | 1 | 2 |
| --- | --- | --- | --- |
| 일반 규약 | clean | finding | cannot decide |
| `scripts/redaction-scan.sh` | clean | findings | missing tool/required rules/usage 등 |
| `scripts/redaction-inventory` | uncovered 후보 없음 | 후보 발견(정상 triage) | 패턴 파일 없음·git repo 아님 등 |
| `scripts/next-version` | (버전 stdout) | — | bad args, `origin/main` 없음, shallow clone |

`redaction-scan.sh` 헤더는 usage·도구 오류를 문서화된 코드로 접는다. 각 스크립트 헤더의 Exit 절을 읽고 가정하지 마라.

셸에서 exit를 잡을 때 파이프 뒤에 `$?`를 두지 않는다.

```bash
out=$(cmd 2>&1); rc=$?
# 파이프 끝의 $?는 파이프라인 마지막 명령의 것이다
```

## Redaction 게이트

### `redaction-scan.sh`

gitleaks를 엔진으로 쓰고, 이 스크립트가 스코프·커밋 메시지·조직 규칙 주입·커버리지 선언을 맡는다.

```console
$ ./scripts/redaction-scan.sh                 # tracked 전체
$ ./scripts/redaction-scan.sh --staged        # index만
$ ./scripts/redaction-scan.sh --range A..B    # 범위 파일 + 해당 커밋 메시지
```

환경:

| 변수 | 역할 |
| --- | --- |
| `REDACTION_EXTRA_PATTERNS` | 조직 전용 규칙 파일 경로. 형식 `id\|description\|regex` (줄당 1). 공개 저장소에 커밋하지 않음 |
| `REDACTION_REQUIRE_EXTRA=1` | extra 파일 없거나 비면 exit `2` (generic만으로 조용히 통과 금지) |

예외는 gitleaks 메커니즘: `.gitleaksignore` fingerprint 또는 `config/gitleaks.toml` 클래스 단위.

### `redaction-inventory`

스캔의 역: 규칙이 가리키지 않는 토큰 후보를 보고한다. 후보 ≠ secret, 빈 결과 ≠ 안전 증명.

```console
$ REDACTION_EXTRA_PATTERNS=~/.config/redaction-extra.txt ./scripts/redaction-inventory
$ ./scripts/redaction-inventory --baseline .redaction-inventory-baseline
$ ./scripts/redaction-inventory --json
```

바이너리는 처음 8KB 안의 NUL 바이트로 휴리스틱 판별 후 스킵한다. 출력은 여전히 “tracked 전체”처럼 보일 수 있어 silent다.

### 스테이징 전제

스캐너는 **tracked** 내용만 읽는다. unstaged 새 파일은 보이지 않고 스캔이 green으로 돌아올 수 있다.

```console
$ git add -A
$ ./scripts/redaction-scan.sh
```

### Pre-push 훅

클론당 한 번:

```console
$ git config core.hooksPath hooks
```

`hooks/pre-push`는 push 범위에 대해 `redaction-scan.sh --range base..local_sha`를 돌린다. tip-of-tree만 보면 중간 커밋에 있었다가 사라진 비밀을 놓친다(이 저장소의 실제 유출 형태). 범위를 못 잡으면 tracked tree 전체로 fallback. **테스트 스위트는 훅에 넣지 않는다** — 느린 훅은 `--no-verify`로 우회된다. 훅은 조직 규칙을 강제하지 않는다(기여자는 private 규칙을 가질 수 없음).

### CI

`.github/workflows/gates.yml` — `pull_request`와 `main` push.

| job | 내용 | 비고 |
| --- | --- | --- |
| `tests` | Python 3.12, pytest, gitleaks 설치(pinned+checksum) | `ubuntu`/`macos` blocking; `windows-latest`는 `continue-on-error` measurement-only |
| `redaction` | `./scripts/redaction-scan.sh` (committed ruleset) | `redaction-inventory`는 informational (`|| true`) |

CI는 커밋된 규칙만 돌린다. `REDACTION_EXTRA_PATTERNS` 전체 스캔은 여전히 로컬이다. gitleaks는 redaction job뿐 아니라 tests job에도 설치한다(커밋 메시지 스캔 테스트가 엔진을 실측함).

## master-ops 템플릿 경계

`master-ops/`는 **템플릿**이다. 온보딩 시 사용자 ops 저장소로 복사된다. 이 저장소에 대한 문서가 아니다.

| 사실 | 결과 |
| --- | --- |
| 템플릿 변경 | **신규** 설치에만 도달 |
| 기존 설치 | 복사본; 자동 갱신 없음 |
| 템플릿 버전 | `master-ops/TEMPLATE-VERSION`, `master-ops/MANIFEST.json` |
| 템플릿 changelog | `master-ops/CHANGELOG.md` — 오케스트레이터 `CHANGELOG.md`와 **독립** 버전 |
| 업그레이드 | `scripts/template-check` / `scripts/template-apply` (dry-run 먼저; instance-owned 경로 거절) |

`master-ops/`를 건드리면 같은 변경에 `master-ops/CHANGELOG.md` `## Unreleased` 항목을 넣는다. `TEMPLATE-VERSION`은 릴리스 cut 때만 이동한다.

보안 범위에서도 템플릿은 out of scope: 복사 후 사용자가 실행 전에 검토한다.

## 커밋·PR

- Conventional commits, 영어: `feat(scope):`, `fix(scope):`, `docs(scope):`
- 무엇을 바꿨고 왜 필요했는지
- PR은 squash merge
- AI가 유지보수자 지도 아래 쓴 커밋은 `Co-Authored-By` trailer에 모델명. 트레일러 부재 = 관례 이전 작성이지, 반드시 수기라는 뜻은 아님

## 릴리스 cut

버전 형식: `MAJOR.MINOR.BUILD`.

| 자리 | 주체 |
| --- | --- |
| `MAJOR`, `MINOR` | 오너만 수동. 자동화 금지. `scripts/next-version`의 `OWNER_MANAGED_MAJOR_MINOR` (현재 `0.5`) |
| `BUILD` | cut 시점 `git rev-list --count refs/remotes/origin/main` |

major가 0인 동안 공개 표면은 불안정할 수 있다. CLI 플래그·파일 형식·모듈 인터페이스가 minor에서 바뀔 수 있다.

### 절차

<Steps>
  <Step title="Sync and derive">
    shallow면 unshallow. `origin/main`과 tags를 fetch한 뒤:

```console
$ version="$(./scripts/next-version)"
$ printf '%s\n' "$version"
```

`origin/main` 없거나 shallow면 `next-version` exit `2`.
  </Step>
  <Step title="Stage before redaction">
```console
$ git add -A
```
    unstaged 새 파일은 스캐너에 안 보인다.
  </Step>
  <Step title="Release gates">
```console
$ set -e
$ PYTHONPATH=src uv run pytest tests -q
$ ./scripts/redaction-scan.sh
$ rc=0
$ ./scripts/redaction-inventory || rc=$?
$ if [ "$rc" -ne 0 ]; then [ "$rc" -eq 1 ] || exit "$rc"; fi
```
    inventory exit `1`은 정상 triage. exit `2`는 cut 차단.
  </Step>
  <Step title="CHANGELOG">
    오케스트레이터 `CHANGELOG.md`에 `v${version}` 노트. Keep a Changelog 형식. 링크·날짜 확인. `master-ops/` 변경이 있으면 템플릿 changelog도 정리.
  </Step>
  <Step title="Tag (owner only)">
    **명시적 오너 승인 후에만**:

```console
$ [ -n "${version:-}" ] || exit 1
$ git tag "v${version}"
```

    태그 생성·push 자동화 금지. push도 오너가 요청할 때만.
  </Step>
</Steps>

## 보안 보고

공개 이슈에 취약점을 올리지 않는다. GitHub private vulnerability reporting을 쓴다. SLA 없음; 대략 1주 내 수신 확인, 1개월 내 수정 또는 결정을 현실적으로 기대한다. major 0 동안 최신 릴리스만 지원, backport 없음.

In scope 요약: `src/master_runtime/`, `scripts/`, 요청 밖 명령 실행·경로 읽기·디스패치, 스캐너가 읽지 않은 범위를 clean으로 보고하는 경우.

문서화된 한계(취약점으로 취급하지 않음): tracked-only 스캔, inventory 바이너리 silent skip. 설명보다 심각한 형태면 보고 대상.

## 로컬 PR 전 체크리스트

```console
$ PYTHONPATH=src python3 -m pytest tests -q
$ git add -A   # 새 파일 포함
$ ./scripts/redaction-scan.sh
$ ./scripts/redaction-inventory || true   # 1 = triage, 2 = 차단
```

선택: `git config core.hooksPath hooks`로 push 전 range 스캔.

PR 본문에 넣을 것:

1. (스택 추가 시) 5문항 답
2. 실패 없이 통과하지 않는 테스트 이름, 또는 문서-only 이유
3. exit `2` 경로를 건드렸다면 호출자가 finding과 혼동하지 않는지

## Related pages

<CardGroup>
  <Card title="Redaction gates" href="/redaction-gates">
    redaction-scan 범위·exit, REDACTION_REQUIRE_EXTRA, inventory, gitleaks, pre-push
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    preflight BLOCKED, undecidable exit 2, placement·probe 복구
  </Card>
  <Card title="방어 인벤토리" href="/defense-inventory">
    디스패치 게이트, probe, redaction, revival, onboarding 가드 표
  </Card>
  <Card title="온보딩" href="/onboarding">
    ONBOARDING 라우터, 템플릿 치환 경계, Stage 1/2
  </Card>
  <Card title="Installation" href="/installation">
    전제조건, preflight, 클론 후 측정 신호
  </Card>
  <Card title="Overview" href="/overview">
    공개 표면, Orca 전제, 마스터/워커 역할
  </Card>
</CardGroup>

---
