# task-2774+2 — governor PR#250 remediation round 1 (fail-open→fail-closed, guard-native)

## 목표 (회장 A안 승인 — 1 remediation round)
PR #250(branch `task/task-2774-dev1`, head `cb910567`)의 **Gemini unresolved 10건(HIGH 4 + medium 6)** 을
**1 remediation round** 로 닫아 merge-ready 로 만든다. ★ 핵심 = **fail-open → fail-closed 전환**.
상태명: `TASK2774_GOVERNOR_PR250_REMEDIATION_ROUND1_APPROVED_ACTIVE_FALSE`.

## 절대 원칙 (회장 verbatim — fail-open 금지)
```text
governor 는 안전장치다. 카운터/ledger I/O·파싱 오류 시 ALLOW 가 아니라 BLOCK 또는 TRIP 이어야 한다.
- _load_counters 오류 → 빈 dict 무시 금지 → fail-closed decision 으로 이어지게.
- _save_counters 실패 → silent ignore 금지. 저장 실패로 count 미증가 → 무한 spawn 열리면 안 됨.
- ledger 비대화 HIGH → 스트리밍/조기탈출 또는 bounded read.
- medium 6 도 같은 round 안에서 처리. 새 축으로 번지지 말 것.
```

## ★ 정밀 주의 (ANU 인지검증 — 기존 8/8 깨지 않기)
```text
_load_counters 의 빈 dict 는 두 경우를 섞고 있다:
  (a) 파일 부재(첫 실행, 정상)  → 빈 카운터로 진행(ALLOW 경로 정상, 기존 ALLOW 테스트 보존).
  (b) 파일 존재하나 손상/파싱오류/권한·IO 오류(위험) → fail-closed (BLOCK/TRIP).
반드시 (a)/(b) 분리. (b)만 fail-closed. (a)까지 BLOCK 하면 기존 8/8 ALLOW 케이스가 깨진다.
같은 분리를 _load_jsonl_lines / dedup ledger 읽기에도 적용(파일 부재=정상, 손상/IO오류=보수적 처리).
fail-closed decision 은 reason 기록 필수(예: COUNTERS_UNREADABLE / COUNTERS_SAVE_FAILED).
```

## 수정 허용 파일 (정확히 2개 = expected_files)
```text
utils/spawn_safety_governor.py                       # fail-open→fail-closed + ledger bounded read
tests/regression/test_spawn_safety_governor_2774.py  # 신규 fail-closed 차단 증명 테스트 추가(기존 9종 유지)
```
※ `dispatch/anu_result_pickup_runner.py` 는 이번 round 미수정(governor 호출 1지점 그대로). 기존 owner-pickup 체인 재작성 금지.

## HIGH 4 대응 (Gemini)
```text
HIGH-1 _load_jsonl_lines 전량 메모리 적재 → 스트리밍 제너레이터 또는 bounded read(상한 N 또는 역방향 조기탈출).
HIGH-2 dedup 검사 → 스트리밍 + 일치 시 즉시 break(전량 적재/순회 제거).
HIGH-3 _load_counters 손상/IO오류 → fail-closed (COUNTERS_UNREADABLE → BLOCK/TRIP). 파일 부재(첫 실행)는 정상 빈 카운터.
HIGH-4 _save_counters 저장 실패 → silent pass 금지. 저장 실패 시 해당 spawn 평가는 fail-closed(COUNTERS_SAVE_FAILED → BLOCK), count 미증가로 무한 spawn 열리지 않게.
```
## medium 6 대응
```text
- events_1min/1hour/window epoch 파싱 반복 → _get_event_epoch 헬퍼로 1회 파싱/캐시.
- 이벤트 append 시 epoch 미리 저장.
- __import__("datetime").timedelta → 상단 import 된 timedelta 직접 사용(2곳).
전부 같은 파일 내, 동작 동치(차단 시맨틱 불변). 새 축 금지.
```

## 필수 테스트 (회장 명시 — 기존 9종 유지 + 신규)
```text
신규:
1. counter 파일 손상(깨진 JSON)/권한·IO 오류 → spawn 0 + reason(COUNTERS_UNREADABLE) 기록.
2. counter 저장 실패(mock 으로 _save 실패 유발) → spawn 0 + reason(COUNTERS_SAVE_FAILED) 기록.
3. ledger 비대화 상황에서도 dedup/rate 판단 bounded(전량 적재 안 함) 동작.
유지(회귀):
4. 기존 8/8 차단 증명 전부 PASS(NON_TERMINAL/DEDUP/LOOP/RATE/CIRCUIT/SINGLEFLIGHT/ALLOW/LEDGER).
5. counter 파일 부재(첫 실행) → ALLOW 경로 정상(빈 카운터, 기존 ALLOW 테스트 보존).
6. ALLOW 경로는 기존 체인 1회만 통과.
7. 모든 BLOCK/TRIP/QUEUE → spawn_decisions.jsonl reason·task_id·key·timestamp 기록(silent drop 0).
+ pytest tests/regression/test_spawn_safety_governor_2774.py 전체 PASS · py_compile PASS.
```

## 진행 방식 (guard-native, PR #250 위에 수정 commit 추가)
```text
1. start_task_guard 정규 경로(lock 자동, 수기 lock 복사 금지). branch task/task-2774-dev1 worktree attach.
2. cb910567 위에 remediation commit 추가(이번엔 새 commit 허용 — fail-closed 수정).
3. 수정 후: git diff 변경 == 정확히 위 2파일. 2파일 밖 → STOP_REPORT.
4. regression PASS 후 push → PR #250 자동 업데이트(hook 우회/--no-verify 금지).
5. CI 전체 PASS · Gemini unresolved 0 목표. Gemini HIGH thread 는 코드 fix push 로 해소(임의 resolve 금지).
6. push 후 diff 목록 1회 + PR 상태 1회 확인 후 report. CI/Gemini polling 루프 금지(ANU 독립검증 담당).
```

## 금지
```text
activation flag 생성 · systemd start/enable · canary · matrix true 전환 ·
expected_files 2개 밖 수정 · replacement_pr_runner.py 수정 · hook import 결함 수정 ·
기존 owner-pickup 체인 재작성 · dispatch.py/finish-task.sh/gate_shadow 수정 · 자동 merge/squash ·
Gemini thread 임의 resolve · 새 기능 축 확장 · 차단 시맨틱 약화(fail-open 잔존).
```

## STOP 조건 (하나라도 → STOP_REPORT)
```text
2파일 밖 수정 필요 · 기존 체인 재작성 필요 · 기존 8/8 회귀가 깨짐 ·
fail-closed 전환이 첫 실행 ALLOW 경로를 깨야만 가능 · activation/systemd/canary 축 번짐 · 자동 merge 시도.
```

## 보고 (ANU callback envelope + report)
```text
수정 commit sha · 해결한 HIGH 4/medium 6 목록 · fail-open→fail-closed 전환 증거(reason enum) ·
테스트 결과(신규+기존 9종 유지) · 2파일 밖 수정 0 · CI/Gemini 상태 · merge-ready 여부.
※ ANU 가 수정 후 독립검증으로 fail-closed 차단 증명 재현. unresolved 0 전까지 merge 금지.
```

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "utils/spawn_safety_governor.py"
    - "tests/regression/test_spawn_safety_governor_2774.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
```