# dispatch-gate 레퍼런스

> check·register·watch·report 플래그, ledger 스키마, reason code, 티어 정책 해석, 티켓 TTL, 기본 문자 한도.

- Repository: local/mogui-ADE-orchestrator

- Human docs: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac
- Complete Markdown: https://grok-wiki.com/public/docs/local-mogui-ade-orchestrator-97afe791d5ac/llms-full.txt

## 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>
