# task-2774+9 — PR#253 remediation: cron registration fail-open → fail-closed (same-PR micro-fix)

## 목표 (회장 옵션1 승인 — D-2 fire safety MED, 좁은 micro-fix)
PR #253(head 42fd3dcf)의 fresh Gemini **MED 1건**(D-2 fire safety)을 같은 PR에서 닫는다.
★ `build_oneshot_cokacdir_fire_fn`의 cron 등록 성공 판정이 **fail-open**: registration response의 `id`가 없거나 빈 값인데도 `registered=True`가 될 수 있음. real fire 직전 코드라 실제 미등록을 성공으로 기록하면 dedupe/duplicate fire/teardown/callback 판단이 흐려짐 → **fail-closed로 교체**.
상태명: `PR253_REMEDIATION_CRON_ID_FAILCLOSED_GUARD_NATIVE_ACTIVE_FALSE`.

## base (강제 pull 금지)
```text
PR #253 위에 remediation commit 추가. branch task/task-2774+8-dev2 (head 42fd3dcf).
worktree 재사용: /home/jay/workspace/.worktrees/task-2774+8-dev2. 강제 pull 금지.
실제 remediation delta = 42fd3dcf..HEAD 기준 2파일 이내.
```

## 수정 범위 (정확히 2파일 = 그 외 STOP_REPORT)
```text
dispatch/anu_result_pickup_runner.py                      # registered 판정 fail-closed 교체 (line 1845 부근, build_oneshot_cokacdir_fire_fn)
tests/regression/test_d2_fire_wiring_2774p8.py            # id 없/빈/None fail-closed 테스트 보강(기존 21 유지)
(필요 시) memory/plans/spawn_safety_governor/d2_fire_wiring_design_2774p8.md  # correction note 1줄만
```
★ **owner-proof/governor/G2/G3/D-1 PASS 경로·build_callback_launch_fn·attempt_owner_fire 무수정.** 범위 확장 금지·다른 safety layer 수정 금지.
★ 2파일(+design 1줄) 밖 수정 필요 → **STOP_REPORT**.

## 구현 요구 (회장 verbatim)
```text
1. registered=True 는 cron registration response 의 id 가 **실제 존재하고 비어있지 않을 때만** 허용.
2. id 가 없거나 빈 문자열이면 fail-closed — fire 성공으로 기록하지 마라.
   상태명은 FIRE_BLOCKED_NO_CRON_ID 또는 FIRE_REGISTRATION_FAILED 에 준하는 명확한 fail-closed 상태.
3. 기존 성공 경로(id 있는 경우)는 그대로 유지.
4. --once 유지 · raw key 노출 0 유지 · recurring cron 금지 유지.
권장 형태(예):
    if isinstance(data, dict) and data.get("status") == "ok":
        raw_id = data.get("id")
        if raw_id is not None:
            schedule_id = str(raw_id).strip()
            if schedule_id:
                registered = True
    # id 부재/빈값 → registered=False (fail-closed)
```

## 테스트 추가/보강 (회장 — mock fire_fn만, 실 cokacdir 호출 절대 0)
```text
1. registration response에 id 키 없음 → registered=False (또는 fail-closed status)
2. id="" (빈 문자열) → registered=False / fail-closed
3. id=None → registered=False / fail-closed
4. 정상 id 있을 때만 → registered=True
5. id 없는 응답에서 dedupe/fire success로 오판하지 않음(duplicate fire 0)
6. raw key 노출 0
7. 실 cokacdir 호출 0(mock fire_fn/subprocess assert_not_called)
+ 기존 21 테스트 유지 · 핵심 회귀(test_spawn_safety_governor_2774·test_driver_governor_wiring_2774p3·2742·owner_proof) 유지 · py_compile PASS.
```

## 진행 방식 (guard-native, PR #253 위 commit 추가)
```text
1. worktree 재사용(task/task-2774+8-dev2, 42fd3dcf). 강제 pull 금지.
2. 위 2파일(+design 1줄)만 수정. git diff(42fd3dcf..HEAD) == 정확히 그 범위. 초과 → STOP_REPORT.
3. 테스트 + py_compile PASS 후 push → PR #253 자동 업데이트. hook 우회/--no-verify 금지.
4. Gemini MED thread 는 코드 fix push 로 해소(임의 resolve 금지).
5. push 후 새 head sha 보고. CI/Gemini polling 루프 금지(ANU 독립검증).
6. ★ merge 금지(green 될 때까지·회장 명시 승인 전). real fire/ACTIVE 전환 0.
```

## 금지
```text
green까지 merge · 자동 resolve/dismiss · 범위 확장 · 다른 safety layer 수정 · owner-proof/governor/G2/G3/attempt_owner_fire 수정 ·
real fire · 실 cokacdir 호출(테스트 포함) · ACTIVE 전환 · raw key 노출 · 서명위조 · 우회/permissive stub · 자동 merge.
```

## STOP 조건 (하나라도 → STOP_REPORT)
```text
2파일(+design 1줄) 밖 수정 필요 · 다른 safety layer 수정 필요 · 기존 21/핵심 회귀 깨짐 ·
base가 42fd3dcf 아님 · 실 cokacdir 호출 발생 가능성 · merge 필요.
```

## 보고 (ANU callback envelope)
```text
remediation commit sha(새 head) · registered fail-closed 교체 내역 · 신규 fail-closed 상태명 ·
테스트 결과(보강+기존21+회귀) · 수정 2파일(+design 1줄) 확인 · raw key 0 · 실 cokacdir 호출 0 · merge 미수행(보고 후 대기).
★ ANU callback: owner=ANU(런타임 환경 .env.keys 로드·평문 금지·self-key 금지). callback prompt UTF-8 ≤3900 bytes.
```

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "dispatch/anu_result_pickup_runner.py"
    - "tests/regression/test_d2_fire_wiring_2774p8.py"
    - "memory/plans/spawn_safety_governor/d2_fire_wiring_design_2774p8.md"
  forbidden_paths:
    - "dispatch/anu_pickup_driver.py"
    - "dispatch/anu_activation_preflight.py"
    - "dispatch/anu_callback_launch_audit.py"
    - "utils/spawn_safety_governor.py"
    - "scripts/finish-task.sh"
    - "memory/state/p0b_driver_enabled"
    - "memory/state/p0b_callback_launch_enabled"
    - "memory/state/p0b_real_wake_enabled"
    - "memory/state/automation_capability_matrix.json"
    - ".claude/**"
    - ".env*"
  commands: ["git", "pytest", "python3"]
  merge_policy: "manual"
  ttl_hours: 12
```
