# CI_WATCH_HANDOFF_RUNTIME_ENFORCEMENT_GATE spec (task-2643) — 260523

회장 결정 (2026-05-23 21:40 KST · v2.4 Harness Recovery 기준): **박제/spec 수준이 아니라 ANU 본체의 위험 tool call 을 실행 전 차단하는 Harness-first 구조를 만든다.** 단 live `~/.claude/settings.json` 직접 수정은 최종 회장 승인 전까지 금지.

기반: `system_ci_watch_handoff_policy_spec_260523.md` (정책 single source) + `system_ci_watch_handoff_runner_spec_260523.md` (runner spec) + `system_ci_watch_handoff_real_pr_activation_gate_spec_260523.md` (선행 activation gate spec · 회장 직접 수정 PR #145 박제 승격) + `ANU_v3_master_development_direction_final_260517_v2_4_harness_recovery.md` (v2.4 Harness Recovery 개발방향) + `anu_batch_hold_adjudication_auto_remediation_v2_4_harness_recovery.md`.

---

## 1. 본 task 범위

### 1.1 핵심 원칙
- **Harness-first**: 박제/spec/문서만으로는 ANU 직접 위반 재발 가능 → harness 수준 deny가 진짜 enforcement
- **live settings.json 수정 0** (staged template + dry-run + activation packet 까지만)
- **Track 4 병렬 진행** (A/B/C/D) + ANU-Codex loop non-critical 자동수렴
- **PR #145 와 혼합 0** (별도 worktree / 별도 PR)
- **회장 보고 트리거**: Critical7 / credential / permission expansion / live settings 적용 시도 / GitHub destructive write / forbidden target 수정

### 1.2 4 트랙 분해 (회장 verbatim)

| Track | 책임 | 핵심 산출물 |
|---|---|---|
| **A. PreToolUse Hook Enforcement** | ANU 본체 위험 tool call 실행 전 deny | `hooks/pre_tool_use_anu_guard.py` draft + staged settings template + deny/allow dry-run report |
| **B. Watcher Contract / Dead-letter** | 9 필드 schema + dead-letter state 정의 | `schemas/watcher_contract_v1.json` + `schemas/watcher_dead_letter_v1.json` |
| **C. PR Open Watcher Auto-Wrapper** | gh pr create 후 watcher 자동 발사 dry-run | `utils/pr_open_watcher_wrapper.py` (dry-run only · live 호출 0) + bzaona6au fixture |
| **D. Repo Regression / CI Guard** | 정적 검사 + closeout grade auto-classifier | `tests/regression/test_forbidden_bash_pattern_static_guard.py` + `utils/closeout_grade_auto_classifier.py` + fixtures |

---

## 2. Track A — PreToolUse Hook Enforcement

### 2.1 신규 산출물
- `hooks/pre_tool_use_anu_guard.py` (hook script draft · staged · live 미적용)
- `memory/specs/staged_settings_template_pre_tool_use_anu_guard_260523.json` (staged settings.json template · live 적용 금지)
- `memory/events/pre_tool_use_anu_guard_dry_run_report_260523.json` (deny/allow dry-run report)

### 2.2 Forbidden Bash pattern (deny 대상 5 그룹)

1. **run_in_background + gh pr view/checks/statusCheckRollup** — CI polling 위장
2. **while/until + sleep + gh pr view/checks/statusCheckRollup** — loop polling
3. **gh run watch / gh run list 반복 대기** — workflow run polling
4. **CI/Gemini terminal wait 목적의 ANU 본체 polling** — semantic intent 분석
5. **admin override / BOT App token 직접 사용 / chair_authorization 임의 발급 / real auto-merge activation / PR #141 pilot 실행** — 명시적 forbidden action

### 2.3 fail-closed 원칙
- hook timeout / parse error / config missing → high-risk tool call **DENY** (allow fallback 금지)
- deny 응답에 `allowed_alternative` 포함:
  ```json
  {
    "decision": "deny",
    "reason": "ANU_DIRECT_CI_POLLING_FORBIDDEN",
    "allowed_alternative": "delegated_watcher_contract",
    "next_steps": [
      "1. watcher contract 9 필드 생성 (Track B schema 참조)",
      "2. watcher dispatch (dev bot 또는 cron)",
      "3. ANU normal callback 대기"
    ]
  }
  ```

### 2.4 hook 작동 방식
- claude-code `PreToolUse` hook (settings.json 등록 시 활성)
- Bash tool call 시점에 command string + run_in_background flag 검사
- 정적 검사 + heuristic intent (CI/Gemini wait 목적 판정)
- staged template 만 작성 · live settings.json 적용 = **회장 최종 승인 후 별도 task**

---

## 3. Track B — Watcher Contract / Dead-letter

### 3.1 watcher_contract_v1.json schema (9 필수 필드)

```json
{
  "schema": "anu.watcher_contract.v1",
  "task_id": "string (non-empty)",
  "pr_number": "int>0",
  "head_sha": "string (40 hex)",
  "terminal_states": ["MERGE_READY", "CHAIR_REQUIRED", "GEMINI_EXTERNAL_TRIGGER_STALE", "CI_FAILED_NON_REMEDIABLE", "LOOP_BOUNDARY"],
  "ttl_seconds": "int>0 (≤7200)",
  "callback_target": {
    "type": "ANU_NORMAL_CALLBACK",
    "owner_key": "c119085addb0f8b7",
    "envelope_axes": ["delivery_outcome", "miss_cause", "root_cause_tags", "canonical_root", "registration_status"]
  },
  "duplicate_policy": "DEDUPE_ON_PR_HEAD_SHA",
  "owner": "string (dev bot name OR cron-watcher-* · NEVER 'anu')",
  "collector_role": "string ('ANU' for callback collector · NEVER 'anu_watcher')"
}
```

### 3.2 validation 규칙 (위장 폴링 차단)
- `owner` == "anu" / "ANU" / "anu_main" 등 ANU 본체 식별자면 **fail**
- `collector_role` == "anu_watcher" / "anu_polling" 등 위장 폴링 식별자면 **fail**
- `callback_only_reporting` 필드 누락 또는 false면 **fail**
- 9 필드 누락 시 session-bound polling 으로 분류 → **fail**

### 3.3 dead-letter state 4 종 (TTL 만료 후)
- `WATCHER_TIMEOUT_HOLD` — TTL 만료 + terminal state 미도달 (회장 보고 트리거)
- `WATCHER_CALLBACK_MISSING` — terminal 도달 but ANU callback 미수신 (callback registrar fail)
- `WATCHER_STALE_HEAD` — current head_sha != contract head_sha (force-push 등)
- `DUPLICATE_WATCHER` — 동일 pr/head 에 watcher 2+ 등록 (dedupe violation)

### 3.4 TTL 만료 후 fail-closed
- TTL 만료 시 ANU 직접 polling 복귀 **금지**
- dead-letter state → 회장 보고만 가능 (re-dispatch 별도 결정)

---

## 4. Track C — PR Open Watcher Auto-Wrapper

### 4.1 신규 산출물
- `utils/pr_open_watcher_wrapper.py` (dry-run only · live `gh pr create` 호출 0)
- `tests/fixtures/pr_open_watcher_wrapper/<6 시나리오>/{evidence,expected,PROVENANCE}` (18 file)
- (선택) INDEX.md

### 4.2 wrapper 흐름 (dry-run)
1. `gh pr create` 호출 (또는 mock)
2. PR number / head_sha 회수
3. watcher_contract_v1.json 자동 생성 (9 필드)
4. dev bot dispatch 또는 cron-watcher 등록
5. wrapper return: { pr_number, watcher_schedule_id, contract_path }
6. ANU 직접 polling 욕구 발생 시점 자체 제거

### 4.3 fixture 6 시나리오
- `pr_open_dev_bot_watcher_dispatch_success` → dev bot watcher 자동 발사
- `pr_open_cron_watcher_dispatch_success` → cron-watcher 등록
- `pr_open_anu_owner_in_contract_fail` → owner=ANU 위장 차단
- `pr_open_collector_role_anu_watcher_fail` → collector_role 위장 차단
- `pr_open_dry_run_only_no_live_call` → live `gh pr create` 호출 0 단언
- ★ `pr_145_bzaona6au_violation_replay` — 사고 박제 재현 fixture (회장 verbatim · Track D fixture 와 cross-link)

---

## 5. Track D — Repo Regression / CI Guard

### 5.1 신규 산출물
- `tests/regression/test_forbidden_bash_pattern_static_guard.py` (정적 검사 · 회장 verbatim 7 acceptance)
- `tests/regression/test_anu_direct_polling_violation_guard.py` (PR #145 박제 승격 · 회장 spec §1.6 명시 7 acceptance)
- `tests/regression/test_watcher_contract_validation.py` (Track B schema)
- `tests/regression/test_pr_open_watcher_wrapper_dry_run.py` (Track C wrapper)
- `tests/regression/test_closeout_grade_auto_classifier.py`
- `utils/closeout_grade_auto_classifier.py`

### 5.2 closeout grade auto-classifier (4 enum)
- `DOCUMENTED_ONLY` — feedback md / event json / spec md 만 (실 enforcement 0)
- `REGRESSION_GUARDED` — regression test 추가 (정적 검사 수준)
- `RUNTIME_GUARDED` — staged hook draft + dry-run report (실 enforcement 준비)
- `HARNESS_ENFORCED` — live settings.json 적용 + harness deny 실 작동

### 5.3 grade 산정 입력
- changed_files (PR scope)
- evidence (박제 파일 / fixture 존재)
- dry-run report (deny/allow PASS)
- rollback plan 존재 여부

### 5.4 grade 산정 doctrine
- 사람이 수동 선언한 closeout grade는 **authoritative 아님**
- batch coordinator 가 changed_files/evidence/dry-run/rollback 으로 산정
- 회장 final acceptance 시 grade audit

### 5.5 fixture (Track D)
- `bzaona6au_violation_replay/` — PR #145 사건 박제 (회장 verbatim)
- `delegated_watcher_allow/` — watcher contract 9 필드 완전 → allow
- `forbidden_bash_pattern_until_sleep_gh_pr_view/` → deny
- `forbidden_bash_pattern_run_in_background_gh_pr_view/` → deny
- `closeout_grade_documented_only/` → DOCUMENTED_ONLY 산정
- `closeout_grade_harness_enforced/` → HARNESS_ENFORCED 산정

---

## 6. 12 완료 산출물 (회장 verbatim 1:1)

1. **task-2643 spec** → `system_ci_watch_handoff_runtime_enforcement_gate_spec_260523.md` (본 파일)
2. **PreToolUse hook draft** → `hooks/pre_tool_use_anu_guard.py`
3. **staged settings template** → `memory/specs/staged_settings_template_pre_tool_use_anu_guard_260523.json`
4. **deny/allow dry-run report** → `memory/events/pre_tool_use_anu_guard_dry_run_report_260523.json`
5. **watcher contract schema** → `schemas/watcher_contract_v1.json`
6. **watcher dead-letter schema** → `schemas/watcher_dead_letter_v1.json`
7. **PR open watcher wrapper dry-run** → `utils/pr_open_watcher_wrapper.py` + fixture
8. **bzaona6au violation fixture** → `tests/fixtures/forbidden_bash_pattern/bzaona6au_violation_replay/`
9. **delegated watcher allow fixture** → `tests/fixtures/watcher_contract/delegated_watcher_allow/`
10. **closeout grade auto-classifier** → `utils/closeout_grade_auto_classifier.py` + regression
11. **rollback plan** → `memory/specs/task_2643_rollback_plan_260523.md`
12. **final activation packet** → `memory/specs/task_2643_final_activation_packet_template_260523.json`

---

## 7. 공통 acceptance (회장 verbatim 10 + 1)

- ✅ live settings.json 수정 **0**
- ✅ PR #141 pilot **0**
- ✅ BOT App token 사용 **0**
- ✅ chair_authorization 발급 **0**
- ✅ real auto-merge activation **0**
- ✅ production PR lifecycle activation **0**
- ✅ finish-task.sh / cokacdir / replacement_pr_runner 수정 **0**
- ✅ foreign dirty cleanup **0**
- ✅ destructive git/rm/reset/force push **0**
- ✅ PR #145와 task-2643 혼합 **0** (별도 worktree / 별도 PR)
- ✅ forbidden 25종 (15 + owner_trigger 4 + owner_gemini_trigger_router 3 + ci_watch_handoff_runner 3) 무수정

---

## 8. 허용 (회장 verbatim)

- spec/schema/fixture/test/hook draft/staged settings template/dry-run runner/rollback plan 작성
- ANU-Codex loop 반복 (non-critical lint/test/schema finding 자동수렴)
- 병렬 subagent fan-out (4 트랙 동시 진행)
- PASS_WITH_RECOMMENDATIONS → recommendations acceptance backlog 기록 후 계속 진행

---

## 9. 회장 보고 트리거 (verbatim 6)

- Critical7 hit
- credential expansion
- permission expansion
- live settings 적용 시도
- GitHub destructive write
- forbidden target 수정

---

## 10. frozen anchor

- ANCHOR-1: "본 task = Harness-first 구조 신설 · 박제/spec 수준 넘어 ANU 본체 위험 tool call 실행 전 deny"
- ANCHOR-2: "4 트랙 병렬 (A PreToolUse Hook / B Watcher Contract+Dead-letter / C PR Open Watcher Auto-Wrapper / D Repo Regression+Auto-Classifier)"
- ANCHOR-3: "12 완료 산출물 1:1 박제 (spec/hook/template/dry-run/schema/dead-letter/wrapper/fixture/auto-classifier/rollback/activation packet)"
- ANCHOR-4: "live settings.json 수정 0 (staged template + dry-run 까지만) · 회장 최종 승인 후 별도 task 로 live 적용"
- ANCHOR-5: "fail-closed 원칙 (hook timeout/parse error/config missing → DENY · allow fallback 금지)"
- ANCHOR-6: "watcher contract 9 필드 + dead-letter 4 state · owner/collector_role 위장 폴링 차단"
- ANCHOR-7: "closeout grade 4 enum (DOCUMENTED_ONLY / REGRESSION_GUARDED / RUNTIME_GUARDED / HARNESS_ENFORCED) · 자동 산정 · 수동 선언 authoritative 아님"
- ANCHOR-8: "ANU-Codex loop 자동수렴 + 병렬 subagent fan-out 허용 · PR #145 와 혼합 0 · PR #141 pilot 0 · real auto-merge 0"
- ANCHOR-9: "PR #145 / bzaona6au 사건을 fixture 로 박제 (Track C + Track D cross-link)"
- ANCHOR-10: "v2.4 Harness Recovery 개발방향 정합 · 회장 verbatim 2026-05-23 21:40 KST 직접 결정"
