# Limited Activation §10 GAP 확인 보고 (read-only audit, 260626)

> ★ read-only 코드 확인만 수행. flag 생성 0 · systemd 변경 0 · real fire 0 · canary 0 · recurring 0 · ACTIVE=false 유지.
> 회장 승인 범위: 2026-06-26 read-only gap 확인 (N=3 / T=120 확정, 첫 창=소수 실작업 한정).

## 최종 판정: `GAP_PRESENT_MICRO_PR_REQUIRED_ACTIVE_FALSE`
5개 확인 항목 중 **PRESENT 2 / GAP 3**. limited 가동 전 **micro-PR 1건 선행 필요**.

---

## 항목별 결과

### 1. 누적 N(=3)건 도달 시 `p0b_driver_enabled` 자동 disabled 처리 — **GAP**
- driver에 flag를 `disabled`로 **쓰는** enforcer **없음**. driver의 write는 전부 ledger/evidence jsonl
  append(`anu_pickup_driver.py:674,765,1877`)이지 activation flag 자동 OFF 아님.
- governor에 `LOOP_PER_TASK=3` / `RATE_PER_HOUR=5` 상한이 있어 **발사 자체는 BLOCK**되지만, 이는
  (a) **task_id/family/시간당** 단위라 활성창 전체 누계 N과 다르고 (b) fire를 막을 뿐 **flag/systemd를 끄지 않음**
  → 시스템은 계속 켜진 채 BLOCK만 반복. **"창이 스스로 닫힌다"는 보장 아님.**

### 2. T(=120)분 시간창 만료 시 자동 disabled 처리 — **GAP**
- `read_activation_epoch()`(`anu_pickup_driver.py:403`)로 활성 시각 마커(`p0b_activation_epoch`)를 **읽을 수는
  있으나**, "epoch + T분 경과 시 flag를 disabled로 쓰는" **만료 enforcer 없음**. epoch는 현재 provenance
  scope 식별(조건2)용으로만 쓰임.
- governor `CIRCUIT_WINDOW_MIN=10`은 **폭주 감지용 슬라이딩 윈도우**(spawn≥10 또는 실패율≥0.5 시 TRIP)이지
  **활성 시간창 만료**가 아님.

### 3. `p0b_kill` 존재 시 entrypoint 최상단 전면 no-op — **GAP**
- `anu_pickup_entrypoint.sh` 전체 확인: 게이트는 `p0b_driver_enabled=="enabled"`(line 15-23) + flock
  single-flight + CODE_ROOT fail-closed 뿐. **별도 `p0b_kill` 파일 게이트 없음.**
- runner에도 `p0b_kill` / `kill_switch` 심볼 0. 현재 정지 수단은 `p0b_driver_enabled`를 `disabled`로 쓰는 것뿐
  (즉시 kill 전용 1-touch 스위치 부재).

### 4. 관측 경로(spawn_decisions / processed ledger / schedule_history / self-callback counter) 판정 가능 — **PRESENT**
- ✅ `spawn_decisions.jsonl`: 모든 governor 결정 append, silent drop 0 (`spawn_safety_governor.py:49,149,260`).
  reason enum 완비(ALLOW/NON_TERMINAL/DEDUP/SINGLEFLIGHT/RATE/LOOP/CIRCUIT + fail-closed 3종).
- ✅ `p0b_processed_ledger.jsonl` 존재 — 처리 건수 정합 확인 가능.
- ✅ schedule_history(cokacdir) — 실 발사 count/status/duration/chat_id 판정 가능(B 1-shot E9F85186에서 실증).
- ✅ self-callback: driver가 executor self-key를 **verdict FAIL로 차단**(`anu_pickup_driver.py:324,1034`),
  `callback_launch_verdict` 필드(`anu_result_pickup_runner.py:196`)로 관측 가능. (별도 누계 counter는 없으나
  결정 기록으로 self-callback=0 판정 가능.)

### 5. fail-closed 시 real fire 0 보장 — **PRESENT**
- ✅ governor: COUNTERS_UNREADABLE / COUNTERS_SAVE_FAILED / LEDGER_UNREADABLE → 전부 **BLOCK**
  (`spawn_safety_governor.py:112,130,145,164,293`). "저장 실패 시 count 미증가 → fail-closed BLOCK(무한 spawn 차단)"(:367).
- ✅ driver MANDATORY fail-closed(task-2741-r2, `anu_pickup_driver.py:1427-1440`): fire-capable callback_launch_fn인데
  owner_gate_fn 부재 → **fire 전 단락**. owner_gate_fn 예외도 graceful capture → fail-closed(:285,1817).

---

## 요약
**순간/윈도우 폭주 방지(governor 6게이트)와 fail-closed·관측은 이미 견고**하다. 비어있는 건 딱 **"limited라는
이름을 참으로 만드는 3가지"** — ①활성창 누계 자동 종료 ②시간창 자동 종료 ③즉시 kill switch. 이 3개가
없으면 "무인인데 스스로 안 꺼지는" 상태라 limited가 아니다 → micro-PR로 닫아야 한다.

---

## micro-PR 필요 범위 (구현 아님 — 회장 승인 후)

### 목적
limited activation 3경계(누적 N 자동OFF / 시간창 T 자동OFF / kill switch)를 코드에 박는다. default OFF·
ACTIVE=false·기존 동작 무회귀.

### 설계 (최소·Simplicity First)
- **kill switch + N + T 게이트를 driver(python) 진입 최상단에 1개 함수로** 추가. (entrypoint.sh는 CODE_ROOT
  별도 worktree라 sync 비용 ↑ → kill도 driver 최상단에서 처리하면 systemd oneshot 호출 직후라 충분히 빠름.)
- 게이트 로직: pickup 직전에 (a) `p0b_kill` 존재 → 즉시 NOOP·fire 0 (b) 활성창 누계(processed_ledger 기준)
  ≥ N → `p0b_driver_enabled`를 atomic `disabled` write + NOOP (c) `p0b_activation_epoch + T분` 경과 →
  동일 자동 disabled + NOOP. 셋 다 fire 0.
- governor / runner / launcher / owner-proof / freshness **무수정**.

### expected_files
- `dispatch/anu_pickup_driver.py` (limited-bound 게이트 함수 + 진입 최상단 호출)
- `tests/regression/test_limited_activation_bounds_<id>.py` (신규)
- (선택) `memory/plans/spawn_safety_governor/limited_activation_design_packet_260626.md` correction note 1줄
- forbidden: `.github/**`, `scripts/finish-task.sh`, governor/runner/launcher 본문, systemd 유닛

### 테스트
- `p0b_kill` 존재 → driver 즉시 NOOP, fire 0, governor 미호출.
- 누계 N=3 도달 → 다음 진입 시 자동 `disabled` write + NOOP + fire 0.
- `epoch + 120분` 경과 → 자동 `disabled` write + NOOP + fire 0.
- N 미만 & T 이내 & kill 없음 & 정상 → 기존 경로 보존(회귀 0, B 1-shot 경로 무변경).
- 자동 disabled write **atomic**(부분쓰기 0, 동시성에서 손상 0).
- governor/owner-proof/freshness/self-callback 차단 무회귀.
- `py_compile` PASS · 관련 회귀 PASS · expected_files 밖 수정 0.

### 롤백 기준
- 게이트가 **정상 작업을 잘못 끄면(false-OFF)** → 보수성 재점검(과차단 0 목표).
- 기존 default-OFF 동작 / B 1-shot 경로 / NOOP_DISABLED 단락을 깨면 → revert.
- 전 과정 ACTIVE=false · systemd disabled · flags OFF 유지.

---

## ★ 정지선 (회장 지시 준수)
micro-PR이 필요하더라도 **지금 자동으로 구현/PR/merge/activation 진행 안 함.** 본 보고는 gap 사실 + micro-PR
범위/expected_files/테스트/롤백 **제시까지만**. 다음 행동(micro-PR 발의 여부)은 **회장 승인 대기**.
ACTIVE=false · systemd disabled · flags OFF 유지 중.
