# task-2730-r2 완료 보고서 — Option A 대체 PR (#198 대체)

## 작업 개요
- **Task ID**: task-2730-r2
- **담당**: dev2 (오딘) — alternative resolver (dev1 원작자 아님)
- **모드**: Option A replacement PR (same-PR post-Gemini push 금지 → 대체 PR)
- **CODE_ROOT**: /home/jay/p0b-pickup-main
- **base**: task/task-2730-dev1-os-pickup-closeout (8da55523)에서 분기
- **branch**: task/task-2730-r2-dev2
- **PR**: #199 (https://github.com/Jeon-Jonghyuk/dev_workspace/pull/199)
- **replaces**: PR #198 (task-2730, head 8da55523) — 원본 보존(push 0)

## 배경
PR #198(task-2730)=OS-level pickup deterministic closeout. 11 CI checks GREEN·phase3 SUCCESS.
단, Gemini medium 2건 unresolved로 `mergeStateStatus=BLOCKED`. same-PR post-Gemini push가
금지되어, 8da55523에서 새 브랜치를 분기해 **딱 2건만** 수정하는 Option A 대체 PR로 처리.

## 수정 내용 (정확히 2줄, 그 외 변경 0)
파일: `dispatch/anu_result_pickup_runner.py`

1. **ledger makedirs (~L395)** — Gemini medium 지적
   - before: `os.makedirs(os.path.dirname(_ledger), exist_ok=True)`
   - after:  `os.makedirs(os.path.dirname(_ledger) or ".", exist_ok=True)`
   - 효과: `_ledger`가 디렉토리 성분 없는 상대경로일 때 `os.path.dirname()`이 빈 문자열을
     반환 → `makedirs("")`의 FileNotFoundError 방지. dir-guard fallback `"."`.

2. **result_dir fsync (~L456)** — Gemini medium 지적
   - before: `_fsync_dir(result_dir)`
   - after:  `_fsync_dir(result_dir or ".")`
   - 효과: `result_dir`가 빈 문자열(현재 디렉토리에 marker 생성)일 때 현재 디렉토리(`.`)의
     부모를 fsync. marker(rename) crash-durable 보장 유지.

Gemini suggestion 그대로 적용. 다른 라인 변경 0건.

## 검증 (완료조건)
- **① regression GREEN**: 52 passed (이전 52 기준 유지)
  - 구성: `tests/test_anu_pickup_closeout.py`(29) + `tests/regression/test_anu_result_pickup_runner_2720.py` + `tests/regression/test_anu_pickup_activation_hardening_2729p7.py`
  - 참고: `test_sealed_key_and_launcher_wiring_2729p13.py::test_07_dedupe_process_one_pickup_skip` 1건은 **base 8da55523에서도 동일하게 실패하는 선재(pre-existing) 이슈**(closeout 커밋 자체의 동작 변화 — PICKUP_SKIP→CLOSEOUT_DONE)로, 본 2줄 dir-guard 변경과 무관. scope 외.
- **② Codex 재lint**: CRITICAL=0, HIGH=0, MEDIUM=0, LOW=0 → PASS (independent codex review agent).
- **③ PR 생성**: #199, base=main, head=task/task-2730-r2-dev2. body에 "Replaces #198 — adds only 2 Gemini medium fixes (makedirs/fsync dir-guard). Original #198 preserved." 명시.
- **④ CI/Gemini gate**: PR open 직후 11 checks pending. 직접 장기 대기 금지 → CI_WATCH_HANDOFF(cron A068B226 + 기록파일) 핸드오프.

## diff 증빙
```
git diff 8da55523..HEAD --numstat
2  2  dispatch/anu_result_pickup_runner.py
```
- file sha256 (pre 8da55523):  42a210ae97e736df9c00f6ea016abf075d11c8826b454f5d08bf1a89a3b317c9
- file sha256 (post 52f44673): 740f5ab656d6ab278c04f4dfd15a55e6eeea2f0c97b2cfa995ab227a99b19843
- HEAD oid: 52f44673a6add7cd004be2828722aa4e1d3e50be

## 금지/scope 준수
- PR #198 same-branch push 0 · 2줄 외 수정 0 · merge 0 · activation 0 · systemd enable/start 0 · ACTIVE=true 0 · activation_epoch 0 · production/legacy result 처리 0 · p0b flag enable 0 · canonical dirty cleanup 0 · scope 확장 0.
- merge·activation은 회장 별도 승인 전 금지.

## 핸드오프 / 후속
- CI_WATCH_HANDOFF: `memory/events/task-2730-r2.ci-watch-handoff.json` + cokacdir cron `A068B226`(2026-06-10 01:37:32 발사) — PR #199 CI/Gemini gate 재평가 및 5-enum 분류 보고.
- ANU normal completion callback: ANU key c119085addb0f8b7로 cron 발사(executor self-key 자가발사 금지).
