# task-2732 완료 보고서 — P0B_MINIMUM_PICKUP reporting path (result.json 수거→회장 PASS/HOLD/CALLBACK_MISSING 보고)

## 작업 개요
- **Task ID**: task-2732
- **담당**: dev1 (헤르메스) — serial_only
- **상태**: `P0B_MINIMUM_PICKUP_AUTOMATION_ONLY` (wake/callback launch 아님 — pickup 수거·보고만)
- **CODE_ROOT**: /home/jay/p0b-pickup-main
- **base**: `origin/main` = `7b06eda8` (spec 명시 base 와 일치 — task-2731-r2 #201 포함). 최초 stale
  local main(`95941e4e`, origin/main 보다 1 commit behind)에서 분기 후 `origin/main`(7b06eda8)으로
  rebase 하여 fresh-base 확보(start_task_guard 검증 #7 통과).
- **worktree**: `.worktrees/task-2732-dev1` (명시 분기, CODE_ROOT 직접 체크아웃 금지)
- **branch**: `task/task-2732-dev1`

## 목표 (ANCHOR)
driver(scan_once)가 `result.json` 을 수거·verdict 한 뒤, reporter 가 회장 chat(6937032012)에
`PASS` / `HOLD` / `CALLBACK_MISSING` 을 자동 보고한다.
- **ANCHOR-1**: `launcher_fn=None` 유지 — wake/callback launch 0. driver 는 수거·closeout·verdict 만.
- **ANCHOR-2**: verdict → 회장 PASS/HOLD/CALLBACK_MISSING. ANU/봇 key literal 노출 0.
- **ANCHOR-3**: 재현 가능 path(entrypoint/driver/reporter 코드) — ANU 수동 대행(fake automation) 금지.

## 변경 내용 (expected_files 내부만 · 3 코드 파일 + 보고서)
### 1. 신규: `dispatch/anu_pickup_reporter.py`
- `map_report(verdict, result_data) -> PASS|HOLD|CALLBACK_MISSING|None` — 순수 매핑(단위 test 대상).
- `report_one(...)` / `report_records(...)` — dedupe→보고 결선 진입점.
- 보고 수단: `cokacdir --sendfile` 만 사용(`dispatch.anu_terminal_relay.send_report` 재사용,
  allowlist-only, shell=False, 고정 argv). **key 불요 · agent wake 0 · cron prompt 0 · relay 0.**
- dedupe: `memory/p0b_state/reporter_sent.jsonl` ledger(task_id 1회). 보고 임시파일은
  `memory/p0b_state/reporter_outbox/` (isolated state). **canonical events(memory/events) 변형 0.**
- `redact_argv()` — `--key` 토큰 마스킹 helper(로그/evidence key literal 0).

### 2. 수정: `dispatch/anu_pickup_driver.py` (main() reporter 결선 — 최소 diff)
- `main()` 이 `scan_once(...)` 의 반환 records 를 `report_records(records)` 로 결선(보고만 추가).
- **★ ANCHOR-1 (launch-0 posture 유지)**: base(task-2731)의 `launcher_fn = build_launcher_fn(...)`
  (real-wake flag OFF 기본 → None → wake launch 0) + `callback_launch_fn = build_callback_launch_fn(...)`
  (ACTIVE flag gate 내장 — OFF 기본 → 전면 no-op → callback launch 0) 결선을 **그대로 보존**.
  본 task 는 두 launch gate(둘 다 OFF=launch 0)를 변형하지 않고 회장 보고만 추가. (gate 활성화는 회장 별도 승인.)
- 보고 실패는 비치명(fail-safe `try/except`) — 수거/closeout 은 이미 완료, driver crash 0.

### 3. 신규: `tests/test_anu_pickup_reporter.py` (25 tests)
verdict 매핑 / 보고 한 줄 포맷 / key 노출 0 / wake 0(launcher_fn None·source no-wake) /
dedupe 중복 0 / sender 실패 시 미기록(재시도) / canonical write 0 / DriverRecord 처리.

## reporting 매핑 (§4 그대로)
- `CLOSEOUT_DONE` / `VERDICT_AUTHORITATIVE` → **PASS** (단, callback contract 위반/부재 시 CALLBACK_MISSING 우선).
- `QUARANTINE` (owner-proof FAIL: NOT_ANU/QUERY_FAILED 포함) → **HOLD**.
- result.json `callback_schedule_created=true`(contract 위반) 또는 callback 부재 신호 → **CALLBACK_MISSING**.
- `NOOP_*` (disabled/not-target/not-ready/legacy/max-files-defer) → **보고 안 함**.
- spec 매핑 밖(WAKE_BUILT/PICKUP_SKIP/FIRE_FAILED) → 보고 안 함.
- 보고 1건당 1회 — task_id dedupe(ledger) + entrypoint flock single-flight 이중 방어.

## 검증 (완료조건)
1. **expected_files 내부 diff**: reporter 신규 + driver main 결선(최소) + test 신규 + 보고서. 그 외 0.
2. **verdict→PASS/HOLD/CALLBACK_MISSING 매핑 단위 test**: `TestMapReport` 9 cases GREEN.
3. **key 노출 0**: 신규 모듈 소스 16-hex key literal 0(grep + 모듈 자체 단위 test), ANU/봇 key literal 0.
4. **launcher_fn=None 유지(wake/callback launch 0) 회귀**: real-wake/ACTIVE flag OFF(기본) →
   `build_launcher_fn`=None(wake 0) · `build_callback_launch_fn`=gated no-op(callback launch 0).
   `test_main_launch_zero_and_reports`(main 이 scan_once 에 launcher_fn=None 전달 + report 결선),
   `test_build_launcher_fn_default_none`, reporter source 에 `launch_wake`/`relay_to_anu`/`--cron` 0.
5. **중복 보고 0(dedupe)**: 동일 task_id 2회 → 전송 1회(`test_dedupe_no_double_report`).
6. **canonical write 0(isolated)**: 보고 경로가 `memory/events` 변형 0, isolated `p0b_state` 만 기록
   (`test_canonical_events_not_modified`).
7. **regression GREEN + Codex 재lint**:
   - 신규 reporter 25 + closeout 29 + finish-callback(task-2731) 20 + 관련 regression = **214 passed**.
   - 선재(pre-existing) 무관 1건: `test_sealed_key_and_launcher_wiring_2729p13::test_07_dedupe_process_one_pickup_skip`
     — base `7b06eda8`(및 이전 95941e4e)에서도 **동일하게 실패**(canonical 공유 ledger 오염,
     process_one/pickup dedupe 영역, 본 task 변경과 무관). task-2730-r2 보고서에도 동일 선재 이슈로 기록됨.
   - Codex 재lint(독립 review agent): **CRITICAL=0, HIGH=0, MEDIUM=0, LOW=0 → CLEAN**
     (driver diff = report_records import + main records 결선 + __all__; task-2731 launch gate 보존,
      reporter sendfile-only). HIGH dedupe race 는 entrypoint flock single-flight 로 동시 reporter 미발생.
8. **PR 생성 후 CI/Gemini gate**: PR open → CI_WATCH_HANDOFF. merge·systemd 설치·canary = 회장 별도 승인 전 금지.

## 금지 항목 준수
wake/callback launch 0(launch gate 둘 다 OFF 보존) · 자동 merge 0(FINALIZE_ONLY .done) · 자동 PR 수렴 0 ·
자동 remediation 0 · Gemini watcher 확장 0 · replacement runner 확장 0 · v3/v4 0 · ACTIVE=true 선언 0 ·
production activation 표현 0 · canonical write 0(isolated p0b_state 만) · ANU/봇 key 노출 0 ·
timer/cron/system-wide/root unit 신규 설치 0 · expected_files 밖 변경 0 · spec 축약 0 · manual .done 0.

## 비고
- merge / systemd 설치 / canary 실행 = **회장 별도 승인 전 금지**. 성공 시 P0B_MINIMUM canary 단계로.

## L1 스모크테스트 결과
- **서버 재시작**: 해당없음 (라이브러리/CLI 모듈 변경, 데몬 없음).
- **API 응답 확인**: 해당없음.
- **모듈 import/문법**: `python3 -c "import ast; ast.parse(open('dispatch/anu_pickup_reporter.py').read())"` → AST OK (reporter/driver 모두).
- **단위/회귀 실행 확인**:
  - `PYTHONPATH=. python3 -m pytest tests/test_anu_pickup_reporter.py -q` → **25 passed**.
  - reporter 25 + closeout 29 + finish-callback 20 + 관련 regression = **214 passed** (선재 무관 test_07 1건 제외).
- **key 노출 grep**: `grep -rE "[0-9a-f]{16}" dispatch/anu_pickup_reporter.py` → key literal 0(테스트 dummy sha256 외).
- **보고 수단 확인**: 보고 argv = `cokacdir --sendfile`(key 불요·wake 0) — `--cron`/`--key` 0.
- **박제 파일 JSON validity**: `memory/events/task-2732.result.json` → `python3 -m json.tool` 통과.
- **스크린샷**: 해당없음 (백엔드 모듈).
