# task-2775+7 보고서 — PR #255 Gemini fresh-head HIGH(ledger early-stop) + MEDIUM×2(fail-open 제거·flag 0644) micro-fix

- 작업 ID: task-2775+7
- 팀: dev2-team (오딘/토르/헤임달)
- 상태: **MERGE_CANDIDATE (머지 금지 — ANU 독립검증 → fresh-head Gemini 재리뷰 → CI 후 회장 승인 대기)**
- 일시: 2026-06-27
- ACTIVE=false · systemd disabled · flags OFF 유지 · real fire 0

---

## S (Situation)

PR #255(branch `task/task-2775-dev2`)는 limited activation 3경계(kill/N/T) 게이트를 구현 중이다.
fresh-head `29f68b5b` Gemini 재리뷰에서 신규 finding 3건이 보고되었고, 회장 승인(2026-06-26)으로
**efficiency 축 마지막 bounded micro-fix 1회**가 허용되었다.

## C (Complication)

- **thread3 HIGH** (driver:545, Efficiency): `_count_active_window_pickups`가 ledger 순방향 전수 스캔 →
  active 동안 매 실행 O(N) 병목.
- **thread4 MEDIUM** (driver:466, Security/Correctness): `tempfile.mkstemp`가 0o600 → `os.replace` 후
  `p0b_driver_enabled`도 0o600 → 타 프로세스 read 시 Permission Denied.
- **thread5 MEDIUM** (driver:662, Correctness/Safety): `elapsed` 계산 TypeError/ValueError 시 `elapsed=-1.0`로
  T 검사를 건너뛰는 **fail-OPEN** → limited 안전모델 위반.

## Q (Question)

correctness를 유지하면서(under-count 0) ledger scan을 bound하고, flag 권한을 안전하게 0644로 좁히며,
fail-open을 제거해 N·epoch부재·T 세 경로를 동일 안전정책으로 수렴시킬 수 있는가? — 단 expected_files
2개만 수정, merge/activation/real fire 0.

## A (Answer)

driver 1파일 surgical 수정 + 회귀 테스트 9건으로 3 finding을 모두 대응. expected_files 밖 수정 0,
2775(47)·2760(36)·2721(34) 전부 PASS, L1 실동작 검증 PASS, real fire 0.

---

## PR / 커밋 정보

- PR: **#255 OPEN** (새 PR 미생성 — 같은 브랜치 commit 추가)
- base(main) SHA: `e8925d91959fee06d003e14c93d15facce28810c`
- 이전 head SHA: `29f68b5be2f9163882dd0c487c96030efdaf209d`
- **신규 head SHA: `210ff3ce1860abf1a62e6ea2d5c6585d4b93e3e0`**
- 본 task(+7) diff 파일 수: **2개** (`29f68b5b..210ff3ce`)
  - `dispatch/anu_pickup_driver.py` (28 insertions / 3 deletions)
  - `tests/regression/test_limited_activation_bounds_2775.py` (회귀 9건 추가)
- 커밋 2개:
  - `28801ada` 토르: thread3/4/5 driver 수정
  - `210ff3ce` 헤임달: 회귀 9건

---

## thread별 대응 내용

### thread3 HIGH — ledger count 최적화 (correctness 우선)
- `_count_active_window_pickups`에 `max_count: Optional[int] = None` 키워드 추가.
- 카운트 루프에서 `count += 1` 직후 **`if max_count is not None and count >= max_count: break`** 추가.
- 게이트 호출부를 `max_count=n_max`로 호출하도록 변경.
- **epoch 이전 break를 하지 않았다는 증거**:
  - break 조건은 코드상 **오직 `count >= max_count`** 1개뿐이다. `processed_at < activation_epoch`
    분기는 기존대로 `continue`(스킵)이며 break가 없다(diff 확인 가능).
  - 테스트 `test_count_no_epoch_before_break_under_count`: epoch 이전 entry 5개를 파일 **앞쪽**에 배치 후
    epoch 이후 3개 → max_count=10에서 `count==3` 정확히 카운트(epoch 이전 entry로 조기 break 시 0/오류 발생).
  - 테스트 `test_count_early_stop_actually_stops_scanning`: after-epoch 50개 ledger, max_count=3 →
    `_parse_processed_at_to_unix` 호출 **정확히 3회** + 결과 3 (전수 스캔이면 50회) → 실제 stop 입증.
- **under-count 0 보장**: max_count 미만이면 break가 발동하지 않아 전체 파일을 끝까지 읽어 정확한 카운트를
  반환(`test_count_under_max_returns_exact`). 게이트의 `count >= n_max` 판정에 필요한 값은 cap된 max_count로
  충분(이상 도달 시 True 유지). `max_count=None`(기존 직접 호출·테스트) 경로는 전수 스캔 무회귀.

### thread5 MEDIUM — fail-open 제거 (최우선)
- T 블록의 `except (TypeError, ValueError): elapsed = -1.0`(fail-open)를 제거하고,
  `except (TypeError, ValueError, OverflowError):` 시 **`_atomic_write_flag_disabled` 호출 +
  `DriverRecord(verdict=VERDICT_NOOP_ACTIVE_EPOCH_INVALID, activation=disabled)` 반환**(fail-closed).
- verdict 선택: 기존 `NOOP_ACTIVE_EPOCH_MISSING_AUTO_DISABLED`와 **원인 구분**(부재 vs 손상)을 위해 신규
  `NOOP_ACTIVE_EPOCH_INVALID_AUTO_DISABLED` 신설. 기존 verdict가 모두 "1 원인 = 1 verdict, `_AUTO_DISABLED`
  접미사" 네이밍을 따르므로 **코드 일관성 기준**으로 신규 verdict가 적합(보고서 명시 요구 충족).
- **세 경로 일관성 (동일 안전정책 수렴) 확인**:
  - N 경로(`count >= n_max`) → `_atomic_write_flag_disabled` + NOOP_N_EXCEEDED + activation=disabled + result_path="" (fire 0)
  - active + epoch 부재 경로 → `_atomic_write_flag_disabled` + NOOP_ACTIVE_EPOCH_MISSING + disabled + fire 0
  - T elapsed 손상 경로 → `_atomic_write_flag_disabled` + NOOP_ACTIVE_EPOCH_INVALID + disabled + fire 0
  - 세 경로 모두 **fire 0(result_path="")·governor/launcher 미호출·driver flag auto-disabled**로 수렴.
    `test_three_paths_converge_same_safety_policy`가 세 경로의 `activation==disabled`,
    `verdict.startswith("NOOP_")`, `result_path==""`를 동시 검증.
  - (의도된 비대칭: **not active + epoch 부재**는 legacy default-OFF 라우팅 보존을 위해 None 반환 —
    fire-capable 상태가 아니므로 안전모델 위반 아님. 이는 +1에서 고정된 정책으로 본 +7 무변경.)

### thread4 MEDIUM — p0b_driver_enabled 권한 0644
- `_atomic_write_flag_disabled`에서 `os.replace` **직전** `os.chmod(tmp, 0o644)` 추가(replace 후 최종 파일 0644).
- **적용 범위**: `p0b_driver_enabled`(ACTIVATION_FLAG_REL) 한 파일에만 적용. 다른 flag/민감 파일에 일반화 없음.
- **secret 아님 전제 (코드 검증)**: 이 함수가 쓰는 값은 `ACTIVATION_DISABLED`("disabled") 단일 문자열뿐이고,
  flag 값 도메인은 `enabled`/`disabled`로 한정된다(자격증명·토큰·PII 없음). 코드상 전제가 성립하므로 STOP 불필요.
- 검증: `test_atomic_disabled_write_mode_is_0644` + L1 실측 `mode=0o644, content='disabled\n'`.

---

## 테스트 결과

- `test_limited_activation_bounds_2775.py`: **47 passed** (기존 38 + 신규 9)
- `test_p0b_event_strategy_2760.py` + `test_p0b_event_strategy_wiring_2760.py` (test-2760): **36 passed**
- `test_anu_pickup_driver_2721.py`: **34 passed**
- 합계 **117 passed, 0 failed** · `python3 -m py_compile dispatch/anu_pickup_driver.py` **PASS**
- expected_files 밖 수정 **0** (diff 파일 2개 = driver + 2775 테스트)

신규 회귀 9건: early-stop cap / 실제 stop(parse 3회) / epoch-이전 no-break / under-count exact /
게이트 N-path 무회귀 / flag 0644 / T-elapsed TypeError fail-closed / ValueError fail-closed / 세 경로 수렴.

---

## L1 스모크테스트 결과 (실동작 — pytest와 별개 실행)

- 서버 재시작: **해당없음** (순수 게이트 라이브러리 모듈 — 서버/외부 API 없음)
- API 응답 확인: **해당없음** (curl 대상 API 없음 → 대신 실제 임시 파일로 게이트 직접 호출)
- 실제 실행 검증 (`/tmp/l1_smoke_2775p7.py`, 실 파일 기반):
  - [L1-1 thread3] 실 ledger 50건, max=3 → `count=3, parse_calls=3` → **early-stop OK**
  - [L1-2 N-path] `verdict=NOOP_N_EXCEEDED_AUTO_DISABLED, activation=disabled, result_path='' → fire 0 OK**
  - [L1-3 thread4] 실제 flag write → `mode=0o644, content='disabled\n'` → **0644 OK**
  - [L1-4 thread5] `now_fn=None` → `verdict=NOOP_ACTIVE_EPOCH_INVALID_AUTO_DISABLED, activation=disabled` → **fail-closed OK**
  - [L1-5 ACTIVE=false] canonical `memory/state/p0b_driver_enabled` 값 `disabled`(OFF, 본 작업 미수정),
    `p0b_callback_launch_enabled` ABSENT(OFF). 본 작업은 temp dir만 사용 → **canonical flag 생성/수정 0, real fire 0**.
- 스크린샷: 해당없음 (CLI/라이브러리 — 브라우저 UI 없음)

---

## ACTIVE=false / real fire 0 증거

- 코드 수정은 게이트 NOOP 경로(flag disabled write)뿐 — governor/launcher/result move 호출 추가 없음.
- L1에서 gate 반환 `result_path=""`(fire 없음) 확인.
- canonical workspace의 활성 flag 2종 모두 OFF 유지(우리가 생성/수정하지 않음).
- systemctl/activation flag/canary/recurring 등록 **미수행**.

---

## 발견 이슈 및 해결

- L1 스모크 스크립트 초안에서 bash heredoc 이스케이프(`'disabled\\n'`)로 thread4 비교가 거짓 FAIL 출력 →
  스크립트 자체 버그로 판단, 별도 깔끔한 재실행으로 `mode=0o644 / content='disabled\n' → PASS` 확정. driver 무관.

---

## 모델 사용 기록

- 토르(백엔드, driver 수정): sonnet (general-purpose) — 일반 코딩/로직 구현
- 헤임달(테스터, 회귀 추가): sonnet (general-purpose) — 테스트 코드 작성
- haiku 미사용. 프레이야/미미르(프론트/UX) 미소환(범위 외 — 순수 백엔드 게이트 작업).

---

## 머지 판단

- **머지 필요**: No (회장 명시 승인 전까지 PR #255 open 유지)
- **브랜치**: `task/task-2775-dev2`
- **워크트리 경로**: `/home/jay/workspace/.worktrees/task-2775-dev2`
- **머지 의견**: +7 micro-fix는 surgical(2파일·driver 28줄), 117 테스트 + L1 PASS, expected_files 밖 0.
  단 **merge 금지** — ANU 독립검증 → fresh-head Gemini 재리뷰 → CI 확인 후 **MERGE_CANDIDATE**로만 보고.

## fresh-head Gemini 재리뷰 필요 여부

- **필요함**: 신규 head `210ff3ce`에 대한 fresh-head 재리뷰 권장.
- ★ 본 +7은 **efficiency 축 마지막 bounded micro-fix**로 기록됨. 동일 efficiency/ledger scan 계열 HIGH가
  재리뷰에서 또 나오면 **micro-fix 중단 → 구조 재설계 보고로 전환**(task 지시).
