# task-2774+5 — activation 전 필수 안전 PR (G2 fail-closed + G3 backlog 보호, guard-native)

## 목표 (회장 옵션 C 승인 — polish 아님, activation 전 안전 과제)
B real bounded activation을 켜기 **전**, 켜는 순간 실제 위험이 되는 2개를 코드로 닫는다.
★ **G2**: activation-intended 모드에서 activation-check/builder/governor 주입 실패를 조용히 None으로 넘기지 말고 **fail-closed/STOP**.
★ **G3**: activation-intended/canary 실행이 canonical p0b_inbox backlog를 **절대 consume 안 하도록 explicit canary-only scope 강제**.
상태명: `TASK2774_ACTIVATION_PRECURSOR_SAFETY_G2_G3_GUARD_NATIVE_ACTIVE_FALSE`.
★ **이 PR은 flag/systemd/canary/real activation을 켜지 않는다(ACTIVE=false 유지).** activation-intended 모드는 코드로 정의만 하고 기본 OFF.

## base (강제 pull 금지 — 회장 명시)
```text
base = origin/main 최신 head = 23f1b6db6dca9b422a749fbfa2e4286b8cbbb320 (PR#251 merge본).
로컬 main(177792e5) stale — 강제 pull 금지. git fetch origin main 후 origin/main(23f1b6db) 기준
guard-native worktree/branch 작업. merge-base==23f1b6db 확인 후 시작.
```

## 수정 허용 파일 (정확히 3개 이내 = expected_files)
```text
dispatch/anu_pickup_driver.py                                  # activation-intended preflight gate 결선 (scan_once 전 STOP/fail-closed)
dispatch/anu_activation_preflight.py                           # 신규 preflight helper (G2+G3 판정, self-contained)
tests/regression/test_activation_intended_safety_2774p5.py     # 신규 activation-intended safety test
```
★ pickup_once(`dispatch/anu_result_pickup_runner.py`) **재작성 금지**. replacement_pr_runner / hook import **무접촉**.
★ expected_files 3개 초과 필요 · pickup_once/replacement_pr_runner/hook import 수정 필요 → **STOP_REPORT**.

## 구현 지시 (최소·정확 — 기존 동작 100% 보존)
### 1) 신규 `dispatch/anu_activation_preflight.py` (self-contained helper)
`activation_intended_preflight(root, *, paths, build_governor_fn, is_activated_fn=None, inbox_dir_rel=None) -> Decision`
- Decision = dict(ok: bool, reason: str, stop: bool). **activation-intended 전용 게이트** — 호출되는 경우에만 동작(기본 경로 미호출).
- **G2 fail-closed (순서대로 STOP, 첫 STOP에서 반환)**:
  - a. `is_activated` 호출을 try/except. 예외 발생 → **STOP** reason `ACTIVATION_CHECK_ERROR`(조용한 None 금지). 정상 False(미활성) → STOP `NOT_ACTIVATED`(activation-intended인데 flag OFF면 진행 불가).
  - b. `build_governor_fn(root)` 호출 try/except. 예외 → STOP `GOVERNOR_BUILD_ERROR`. 반환 None → STOP `GOVERNOR_NOT_INJECTED`(activation-intended에서 governor 미주입 금지).
  - c. governor_fn callable 아니면 STOP `GOVERNOR_NOT_CALLABLE`.
- **G3 backlog 보호 / canary-only scope**:
  - d. `paths` None/빈 목록 → STOP `CANARY_PATHS_REQUIRED`(explicit canary scope 미지정 금지).
  - e. canonical p0b_inbox backlog 감지: `root/<inbox_dir_rel>/task-*.result.json` glob. backlog 파일 중 **paths에 없는 것이 1개라도 있으면** STOP `CANONICAL_BACKLOG_DETECTED`(backlog consume 금지). = paths가 canonical backlog의 부분집합이 아니면 STOP. (canary path가 backlog 밖 격리 inbox면 backlog 감지 시 STOP — backlog 미소비 보장)
  - f. 위 전부 통과 → ok=True reason `ACTIVATION_INTENDED_READY`.
- ★ backlog 파일 **삭제/수정/이동/소비 0**(읽기 glob만). 12건 그대로 보존.
- self-contained(stdlib only). 외부 부작용 0.

### 2) `dispatch/anu_pickup_driver.py` (preflight gate 결선, 기존 동작 보존)
- `main(argv=None, *, activation_intended: bool=False)` 또는 동등 — **명시 opt-in 파라미터 추가**(기본 False).
- `activation_intended=True`일 때만: scan_once **호출 전** `activation_intended_preflight(...)` 호출. `stop=True`면 scan_once/process_one/pickup **미진행** + STOP record 반환(governor 미주입 live 진행 0).
- `activation_intended=False`(기본) → 기존 main 경로 **100% 보존**(preflight 미호출, 현행 동작 무변경, 회귀 0).
- ★ flag OFF/부재/오류 → 기존 _build_governor_fn None 반환은 **그대로 유지**(default 보존). G2 fail-closed는 activation-intended 모드에서만.

## 필수 테스트 (회장 명시) — `test_activation_intended_safety_2774p5.py`
```text
[기본/OFF 보존]
1. flag 부재 → _build_governor_fn() == None (기존 유지)
2. is_activated 예외 → _build_governor_fn() == None (기존 fail-preserve 유지)
3. activation_intended=False → 기존 main 경로 동작 보존(preflight 미호출)
[G2 activation-intended fail-closed]
4. activation-intended + is_activated 예외 → preflight STOP(ACTIVATION_CHECK_ERROR), scan_once 미진행
5. activation-intended + build_governor_fn 실패(예외) → STOP(GOVERNOR_BUILD_ERROR)
6. activation-intended + governor None(미주입) → STOP(GOVERNOR_NOT_INJECTED), live path 미진행
[G3 backlog 보호]
7. activation-intended + paths 미지정 → STOP(CANARY_PATHS_REQUIRED)
8. activation-intended + canonical backlog 존재 + canary path가 backlog 밖 → STOP(CANONICAL_BACKLOG_DETECTED), backlog 파일 무처리(개수·내용 불변)
9. activation-intended + explicit canary path 1개(backlog 없음/격리) → preflight ok, canary path만 처리
[결선 보존]
10. spawn_governor_fn 로 governor_fn 전달 유지(기존 8종 threading 테스트 보존)
11. ALLOW governor → 기존 체인 1회만 진행
+ 기존 신규 8종(test_driver_governor_wiring_2774p3) 유지 · 핵심 회귀(anu_pickup_driver*/anu_result_pickup_runner_2720/spawn_safety_governor_2774) PASS
+ py_compile PASS
```

## 진행 방식 (guard-native)
```text
1. fetch origin main → 23f1b6db 확정(merge-base). 강제 pull 금지.
2. start_task_guard 정규 경로(lock 자동). 23f1b6db 기준 worktree/branch.
3. 위 3파일만 수정. git diff(23f1b6db..HEAD) == 정확히 3개 이내. 초과 → STOP_REPORT.
4. 테스트 PASS 후 push → PR 생성(base main). hook 우회/--no-verify 금지.
5. PR 생성 후 diff 목록 1회 + PR 번호 1회 확인 후 report. CI/Gemini polling 루프 금지(ANU 독립검증).
6. ★ PR 생성까지만. merge 금지(독립검증/CI/Gemini/final gate 전).
```

## 금지
```text
flag 생성/ON · systemd start/enable · canary 실행 · real callback 발사 · matrix true 전환 ·
서명 위조 · 안전층 우회 · 기존 pickup_once 재작성 · replacement_pr_runner.py 수정 · hook import 결함 수정 ·
강제 pull(local main) · 자동 merge · expected_files 3개 초과 · 추가 polish성 변경 · 기존 OFF 동작 변경.
```

## STOP 조건 (하나라도 → STOP_REPORT)
```text
expected_files 3개 초과 필요 · pickup_once/replacement_pr_runner/hook import 수정 필요 ·
기존 OFF 동작/회귀 깨짐 · activation-intended에서 fail-open 잔존(STOP 안 됨) ·
backlog consume 발생 · base가 23f1b6db 아님(stale 강제 pull 필요) · 자동 merge 시도.
```

## 검증 (완료 전 필수)
```text
pytest tests/regression/test_activation_intended_safety_2774p5.py (11종) PASS
+ 기존 test_driver_governor_wiring_2774p3(8종) 유지 PASS
+ 핵심 회귀(anu_pickup_driver*/anu_result_pickup_runner_2720/spawn_safety_governor_2774) PASS
+ py_compile dispatch/anu_pickup_driver.py + dispatch/anu_activation_preflight.py PASS
+ G2 fail-closed 실증(예외/None → STOP) · G3 backlog 미소비 실증(개수 불변)
```

## 보고 (ANU callback envelope + report)
```text
구현 diff(3파일 이내) · G2 fail-closed 처리 내역 · G3 backlog 보호 증거(12건 불변) · 테스트 결과(신규11+기존8+회귀) ·
3파일 밖 수정 0 · base=23f1b6db · PR 번호 · ACTIVE=false 유지 · merge 미승인 · B 재결정 가능 여부.
※ ANU 독립검증으로 G2 fail-closed·G3 backlog 보호 재현. unresolved 0 + final gate 전 merge 금지.
```

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "dispatch/anu_pickup_driver.py"
    - "dispatch/anu_activation_preflight.py"
    - "tests/regression/test_activation_intended_safety_2774p5.py"
  forbidden_paths:
    - "dispatch.py"
    - "dispatch/anu_result_pickup_runner.py"
    - "scripts/finish-task.sh"
    - "memory/state/automation_capability_matrix.json"
    - "memory/state/p0b_driver_enabled"
    - "utils/replacement_pr_runner.py"
    - ".claude/**"
    - ".env*"
  commands:
    - "git"
    - "pytest"
    - "python3"
    - "gh"
  merge_policy: "manual"
  ttl_hours: 12
```