# task-2732-r2 완료 보고서 — reporting path 신뢰성 fix (HIGH 1 + MEDIUM 2, 마지막 fix 라운드)

## 작업 개요
- **Task ID**: task-2732-r2 (Option A replacement, **1회만 · 이번이 마지막 fix 라운드**)
- **담당**: dev2 오딘 (alternative resolver · dev1 원작자 회피) — serial_only
- **상태**: `P0B_MINIMUM_PICKUP_AUTOMATION_ONLY` (reporting path 신뢰성 한정 — wake/callback launch 아님)
- **base**: `9c4d1ea9` (`task/task-2732-dev1`, PR #202 head)
- **worktree**: `.worktrees/task-2732-r2-dev2` (명시 분기, CODE_ROOT 직접 체크아웃 금지)
- **branch**: `task/task-2732-r2-dev2` (★ PR #202 원본 브랜치 push 0 — 별도 replacement PR)

## 목표 (SPEC-ANCHOR 불변)
- **ANCHOR-1**: `launcher_fn=None` 유지 · wake/callback launch 0. reporter 는 보고만, 깨우기 0.
- **ANCHOR-2**: ANU/봇 key literal 노출 0 · canonical(memory/events) write 0.
- **ANCHOR-3**: **ledger 성공 기록은 실제 send 성공(returncode==0 확인) 후에만** — 테스트로 증명.

## 수정 내용 (신뢰성 3건 · expected_files 내부만)

### (a) `dispatch/anu_pickup_reporter.py` — `_default_sender` (HIGH, reporter:298)
- **결함**: `rc = getattr(res, "returncode", 0)` → `send_report` 결과가 `None` 이거나 `returncode`
  속성이 없을 때 **기본값 0(성공)으로 오인** → 미전송을 성공으로 간주하여 processed/ledger 에
  성공 기록하던 HIGH 결함.
- **수정**: send 성공은 **실제 전송 성공이 확인된 경우에만** True.
  - 예외 발생 → False (다음 cycle 재시도, ledger 미기록).
  - 결과 `None` → False (성공 간주 0).
  - `returncode` 속성 부재 또는 비정수(미확정) → False (성공 간주 0).
  - `returncode == 0` 명시 확인 시에만 True.
- **재시도 폭주 회피**: 실패 시 ledger 미기록(다음 cycle 자연 재시도) — 별도 즉시 재발사/wake 0.

### (b) `dispatch/anu_pickup_driver.py` — `main()` reporting 예외 처리 (MEDIUM, driver:885)
- **결함**: `try: report_records(...) except Exception: pass` — 보고 예외를 silent 하게 삼킴(관찰 불가).
- **수정**: 보고 예외를 **stderr 로 관찰 가능하게 기록**(메시지 + traceback). 수거/closeout 은 이미
  완료 → driver crash 0(`return 0`) 유지(fail-safe).
- **불변 준수**: 자동 재시도 폭주 0 · wake/callback launch 0 · 후속 dispatch 0 — **관찰만**(다음 cycle 자연 재시도).

### (c) `dispatch/anu_pickup_reporter.py` — `_default_result_loader` bounded scan (MEDIUM, reporter:285)
- **결함**: processed 디렉토리 전체를 **무조건** `os.listdir` 로 scan(매 result 적재마다) → 디렉토리
  누적 시 cycle 당 비용 폭주.
- **수정**:
  1) 직접 후보(원위치 `record_path` · `processed/<basename>`)를 listdir 없이 먼저 시도 — 정상 경로
     디렉토리 scan 0.
  2) 직접 후보 부재 시에만 충돌-회피 suffix(`.<ms>[-n]`) 변형을 `os.scandir` + 상한
     `PROCESSED_SCAN_LIMIT=256` **bounded scan** 으로 탐색.
  - 부재 시 `{}` 반환(보수적: callback 판정 불가 → PASS 보존) 동작은 불변.

## 변경 파일 (expected_files 내부)
- 수정: `dispatch/anu_pickup_reporter.py` (a·c)
- 수정: `dispatch/anu_pickup_driver.py` (b)
- 수정: `tests/test_anu_pickup_reporter.py` (신규 테스트 19건 추가 — 총 44 PASS)
- 보고서: `memory/reports/task-2732-r2.md`
- diff 범위: 위 3 코드/테스트 파일만. expected_files 밖 변경 0.

## 검증 결과
1. **reporter tests PASS**: `tests/test_anu_pickup_reporter.py` 44/44 PASS.
   관련 회귀(closeout + finish-callback) 합산 93/93 PASS. driver 회귀 스위트 104 PASS.
2. **★ 검증 #8 (ANCHOR-3) 증명**: `TestLedgerOnlyOnRealSendSuccess` —
   - `test_real_send_success_marks_ledger`: 실제 send(returncode==0) 후에만 ledger 기록.
   - `test_none_result_no_ledger_retriable`: `send_report`=None → 미기록(재시도 가능).
   - `test_nonzero_returncode_no_ledger` / `test_send_exception_no_ledger`: 실패 시 미기록.
   - `test_no_send_no_dedupe_then_retry_succeeds`: 미전송 후 재시도 시 정확히 1회 전송·기록(누락 회피·중복 0).
   - `TestDefaultSenderSuccessSemantics` 6건: None/returncode 부재/비정수/예외 → 성공 간주 0.
3. **HIGH/CRITICAL 0**: 변경은 방어적 신뢰성 수정(성공 오판 차단·관찰성·bounded scan)으로 신규 HIGH/CRITICAL 0.
   AST parse OK. (로컬 ruff/pyflakes 미설치 — fresh CI lint 는 PR head 에서 수행: **CI_WATCH_HANDOFF**.)
4. **expected_files 내부 diff**: 확인 (3 파일).
5. **key literal scan 0**: reporter 소스 16-hex literal 0 (`test_module_source_no_key_literal` PASS).
   driver 매치는 변수명(`sealed_key_loader`)으로 literal 아님. 테스트 내 16-hex 는 가짜 sha256
   픽스처(`deadbeefcafe0000` 등, pre-existing)로 key 아님 — 신규 16-hex literal 도입 0.
6. **launcher_fn=None / wake 0 회귀 PASS**: `TestWakeZero` (build_launcher_fn 기본 None · main →
   scan_once launcher_fn None · reporter 소스 no-wake/no-cron/no-relay). 본 수정은 launch posture 미변형.
7. **(b) 관찰성 증명**: `TestDriverReportingFailureObservable` — report_records 예외 시 stderr 에
   `reporting failure` 기록 + `return 0`(crash 0), wake/dispatch 0.
8. **(c) bounded scan 증명**: `TestResultLoaderBoundedScan` — 직접후보 적재 · suffix 변형 bounded 탐색
   (노이즈 60개에도 동작) · 무조건 `os.listdir(` 제거 소스 가드 · `os.scandir(` 사용 확인.

## errata / pre-existing 분리 (검증 #9)
- **errata (task-2732 result.json 스모크 문구)**: task-2732 base 보고서/result.json 의 L1 스모크 문구는
  reporting path 수거·보고에 한정된 것으로, 본 r2 의 신뢰성 수정(send 성공 오판 차단)으로 send 결과
  판정 의미론이 강화되었음을 명시한다(미전송→성공 오판 0).
- **pre-existing test_07 dedupe pollution (분리 유지·미수정)**:
  `tests/regression/test_sealed_key_and_launcher_wiring_2729p13.py::test_07_dedupe_process_one_pickup_skip`
  은 **base `9c4d1ea9`(미변경)에서도 동일하게 FAIL** 함을 확인(isolation/suite 모두). 이 테스트는
  dedupe 상태를 isolated tmp 가 아닌 실제 canonical root 에서 읽어 운영 잔재에 오염되는 **사전 존재
  pollution** 으로, 본 task expected_files(reporter/driver/reporter-test) 밖이다. **본 r2 는 이를
  수정하지 않고 분리 유지**(replacement 확장 0). 별도 정리 task 권장.

## 불변 조건 준수 (금지 목록)
launcher_fn=None·wake/callback launch 0 ✓ · ANU key literal 노출 0 ✓ · canonical write 0 ✓ ·
자동 merge/PR수렴/remediation/watcher/replacement 확장 0 ✓ · systemd 설치 0 ✓ · CANARY flag/canary 0 ✓ ·
ACTIVE=true 선언 0 ✓ · 재시도 폭주 0 ✓ · PR #202 same-branch push 0 ✓ · expected_files 밖 0 ✓ · spec 축약 0 ✓.

## 판정
task-2732-r2 마지막 fix 라운드 완료. HIGH(reporter:298 send 실패 오판) + MEDIUM 2(driver:885 silent
pass · reporter:285 무조건 scan) 신뢰성 수정. PR #202 보존(별도 replacement PR). merge/systemd/flag/
canary = 회장 승인 전 금지(미수행). r2 후 새 HIGH/구조문제 발견 시 즉시 중단→OPERATIONAL_DEFERRED 동결.
