# P0B EVENT STRATEGY REDESIGN packet (설계 전용, 260617)

상태: `P0B_EVENT_STRATEGY_REDESIGN_PACKET_ACTIVE_FALSE` · ACTIVE=false · ★**설계 문서만** — code change/PR/branch/dispatch/canary 0

## 0. 문제 정의 (실제 blocker)
현재 systemd `anu-pickup.path`는 `PathExistsGlob=%h/workspace/memory/events/p0b_inbox/task-*.result.json`. 그러나 **retained legacy result.json**(`memory/events/task-*.result.json` 및 inbox에 보존된 처리완료 result)이 남아 있으면, PathExists류 트리거는 "파일 존재" 자체로 **반복 발화(retrigger)** 한다. → 상시 canary 시 duplicate/재처리/self-collector 위험. = `SYSTEMD_EVENT_STRATEGY_CONFLICT`.
핵심: **"파일 존재" 기반 감시 → "신규 이벤트" 기반 감시**로 전환해야 1회 감지·1회 처리·legacy retrigger 0이 성립.

## 1. 후보 구조 1안 (권장): live inbox directory + processed ledger
```
dev bot → result.json 을 LIVE_INBOX(memory/events/p0b_live_inbox/)에 atomic write
        → ANU-owned pickup runner 가 LIVE_INBOX 만 scan
        → 처리 성공 시 processed ledger(append-only jsonl) 기록
        → 원본은 archive/processed(memory/events/p0b_processed/)로 move(또는 copy+삭제)
        → legacy memory/events/task-*.result.json 은 감시 대상에서 제외(scan 경로 분리)
```
- 감시 대상 = **LIVE_INBOX에 들어온 파일만**. 처리 후 move로 inbox를 비우므로 "존재"가 곧 "미처리 신규"가 됨 → PathExists류와도 양립(빈 inbox = 트리거 0).
- legacy result는 별도 디렉토리라 scan 경로에서 구조적으로 배제 → retrigger 0.

## 2. retained legacy result 처리 정책
- legacy `memory/events/task-*.result.json` 및 기존 inbox 보존분 = **감시 경로 밖**(p0b_live_inbox 아님). 이동/삭제 없이 그대로 두되 **scan 대상에서 제외**.
- 신규 파이프라인은 LIVE_INBOX에만 기록 → 과거 잔존물과 물리적 분리.

## 3. atomic write 규칙
- dev bot: `tmp write(.partial) → fsync → os.rename(→ .result.json)`. rename 전 파일은 `*.partial`로 scan 제외. runner가 부분쓰기 파일을 읽지 않도록 확장자/네이밍 게이트.

## 4. processed / archive 정책
- 처리 성공 시: (a) processed ledger(jsonl)에 `{task_id, source_sha256, processed_at, runner_id, outcome}` append-only 기록, (b) 원본 result를 `p0b_processed/`로 move(또는 copy 후 inbox에서 제거).
- 실패/transient: inbox 유지(재시도), permanent fail: `p0b_quarantine/`로 move + verdict 기록.

## 5. idempotency / done-marker 기준
- dedupe 키 = `(task_id, source_result_sha256)`. processed ledger에 이미 있으면 **skip(재처리 0)**.
- done-marker = ledger entry 자체(별도 .done 파일 불필요) 또는 processed/ 이동 완료. 둘 중 단일소스 1개로 고정(이중 진실 금지).

## 6. duplicate 방지 기준
- 같은 `(task_id, source_sha256)` 2회 감지 시 ledger 조회로 2번째 skip. move-after-success로 inbox 잔존 0 → 물리적 재감지 0.

## 7. self-collector 방지 기준
- pickup runner는 ANU-owned key로만 callback fire(executor self-key 금지). owner_proof gate 통과분만 처리. (기존 owner_proof_pickup_gate 재사용 — 신규 구현 아님.)

## 8. busy-skip 처리 기준
- same-chat busy 시 cron fire skip(검증된 동작). event strategy는 fire 자체를 막지 않되, busy로 미발사 시 inbox에 result 잔존 → 다음 idle window에 재감지(누락 0). busy-skip은 손실이 아니라 지연으로 설계.

## 9. canary 1shot completion 조건 (설계상 명시 — ★실행 안 함)
신규 result.json 1개 → 정확히 1회 감지 → 1회 처리 → 1회 보고 → legacy retrigger 0 → duplicate 0 → self-collector 0 → ACTIVE=false.

## 10. 구현 PR 예상 expected_files 후보 (참고 — 구현 아님)
- `dispatch/anu_pickup_driver.py`(scan 경로 LIVE_INBOX 한정·processed move·ledger append)
- `deploy/systemd/anu-pickup.path`(PathExistsGlob → LIVE_INBOX 경로) ※systemd 변경은 별도 승인·full enable 금지
- `tests/regression/test_event_strategy_*.py`(retrigger 0·duplicate 0·self-collector 0)
- ledger/archive 디렉토리 정책 문서
→ ★ 실제 파일/runner/hook/systemd 생성은 **이 packet 수용 후 별도 승인 PR**에서만.

## 11. 금지 범위 (이 packet 단계)
code change · PR/branch 생성 · dispatch · canary 실행 · matching/allowlist 재구현 · _coerce 구현 · type-safety hygiene 착수 · callback pickup 구현 · systemd full enable/install · production activation · ACTIVE=true · auto merge · auto remediation · Gemini watcher 확장 · replacement runner 확장 · external probe/key custody/ANU-Control daemon 구현 · 별도 승인 없는 callback/wake launch.

## 12. 다음 (B 이후 — 회장 승인 대기)
이 packet 수용 시에만 → event strategy **구현 PR**(좁은 단일 PR, design-first 합의 범위 내, systemd full enable 0, ACTIVE=false) → 이후 canary 1shot. **B packet 작성 후 canary/구현 PR로 넘어가지 않고 회장 승인 대기.**
