# task-2731 (후보) finish-callback 강제 결선 — 발행 준비 packet (read-only · dispatch/구현 0)

작성: 2026-06-10 KST / ANU 직접 read-only / 상태: 발행 준비(dispatch 미승인)
선행: task-2730 CODE_LIFECYCLE_CLOSED(main 95941e4e, ACTIVE=false). 본 task = 그 runner 를 finish-callback 강제 경로로 결선.
설계 단일 출처: `memory/plans/p0b-pickup/finish_callback_enforcement_followup_task_packet_260610.md`

## 1. task id 후보
- **task-2731** (task-2730 후속·별개 독립. task-2730 은 closed → +N 아닌 신규 id). 파일명에 명시(자동 ID overwrite 방지 박제).

## 2. executor 후보
- **dev6 페룬** 권장 — P0-b callback enforcement(NORMAL_CALLBACK_REGISTRATION/self-key hardening) 경험. dev1 헤르메스·dev2 오딘은 본 결함 당사자 → resolver_reinjection 회피.
- 대안: 회장 지정. **serial_only=true**(§8).

## 3. expected_files 후보 (base=main 95941e4e)
- **신규**: `dispatch/anu_callback_launch_audit.py`(callback 코드강제+audit jsonl·self-key 거부) · `dispatch/anu_finish_preflight.py`(worktree path mismatch·branch -rN 탐지) · `tests/test_anu_finish_callback_enforcement.py`(dev1/dev2 fixture).
- **수정**: `dispatch/anu_result_pickup_runner.py`(collector closeout → callback launch 결선) · `dispatch/anu_pickup_driver.py`(callback audit 호출) · result-json contract(`terminal_intent` 필드) · `scripts/...guard`(branch regex -rN/+N 인식, task-lock 마찰 우회) · dev bot dispatch prompt 템플릿(worktree 경로 명시 강제).
- **재사용 수정0**: owner-proof(anu_owned_callback_enforcement) · systemd path/service(task-2730 anu-pickup.*).
- **설계/dry-run 한정(실 install 0)**: `deploy/systemd/anu-pickup.timer`/`.path`(파일·fixture·dry-run 검증만).
- ★ 정확 경로·base 는 구현 task md 확정 시(start_task_guard/finish-task.sh 가 main repo vs p0b-pickup-main 위치 확인 선행).

## 4. acceptance criteria (필수 설계 조건 10 반영)
1. executor callback 미발사 후 종료 → OS runner result.json 수거 → collector closeout → **ANU-owned callback launch**(코드 강제, audit). ✓
2. dev bot prompt callback 지시 0 동작(prompt 의존 금지). ✓
3. finish-task 끝단 마찰/실패 시에도 result.json 수거·callback. ✓
4. **branch regex `task-2731-r2`/`-rN`/`+N` suffix 안전 인식**(task-id 오추출·state 충돌 0). ✓
5. **worktree path mismatch preflight 탐지**(.worktrees/<task> 기대 vs 실제 불일치 → 경고/차단). ✓
6. task-lock 마찰로 callback 누락 0(result.json 수거 lock 독립). ✓
7. **self-key callback 거부**(ANU key 강제). ✓
8. **ANU-owned callback launch audit jsonl**(task_id·owner_key_class·launch_ts·schedule_id·success·executor_session_independent). ✓
9. result.json/collector_result/terminal marker/pickup.done **idempotency 검증**(1건 callback 1회·3중 dedupe). ✓
10. **dev1/dev2 closeout 미완 fixture 재현** → runner 수거·callback 강제. ✓
11. owner-proof read-only(cron-history) 유지·cron-fire 의존 0. ✓
12. ACTIVE=false flag OFF → 전면 no-op. ✓

## 5. regression fixture 목록
- **fixture-dev1**: result.json+report 존재·`.done`/callback 미생성(65분 git hygiene 후 종료) → runner 수거·callback.
- **fixture-dev2**: result.json(CODE_ROOT)·worktree path mismatch(.worktrees 기대 vs CODE_ROOT 직접)·branch `-r2`(guard 미인식)·task-lock 마찰 → finish 미완 → result.json 만으로 수거·callback.
- **fixture-callback-absent**: result.json 있고 cron-list callback 0 → terminal state+callback launch.
- **fixture-self-key**: executor self-key callback → 거부.
- **fixture-branch-rN**: branch `task-2731-r2` → task-id `task-2731` 안전 인식(suffix 보존·충돌 0).
- **fixture-worktree-mismatch**: worktree 경로 불일치 → preflight 탐지.
- **fixture-active-false**: flag OFF → no-op.
- **fixture-idempotent**: 동일 result.json 2회 → callback 1회.

## 6. forbidden actions (구현 시)
dev bot callback 자가생성 · executor self-key callback · 사후 callback 임의 발사 · canonical reset/clean/stash -u/checkout -f · owner-proof 핵심(resolve_authoritative_owner/verify_collector_authoritative) 변경 · **ACTIVE=true·systemd install/enable/start·activation_epoch** · production/legacy result 처리 · expected_files 밖 · spec 축약 · manual .done · same-PR post-Gemini push · task-2730 재오픈 · PR #198/#199 조작.

## 7. implementation plan (코드 경로 중심)
1. **result-json contract**: `terminal_intent`+`callback_schedule_created:false` 필수화, dev bot callback schedule 생성 코드 차단(prompt 의존 제거).
2. **OS runner detection**(설계/dry-run): systemd path/timer unit 파일 + dry-run harness(실 install 0). result.json 활동독립 감지 코드.
3. **collector closeout 결선**(anu_result_pickup_runner): owner-proof PASS → closeout → **callback launch 호출**(task-2730 closeout 에 callback 결선).
4. **callback launch audit**(anu_callback_launch_audit.py): ANU key 강제·self-key 거부·audit jsonl·executor_session_independent·idempotent(dedupe).
5. **preflight**(anu_finish_preflight.py): worktree path mismatch 탐지 + branch regex `-rN/+N` 안전 추출.
6. **task-lock 마찰 우회**: result.json 수거 경로를 lock·worktree·tracked 여부와 독립.
7. **regression**(isolated): §5 fixture 전부 + idempotency + owner 4-outcome + self-key 거부 + audit + ACTIVE=false no-op.
8. **dry-run harness**: systemd 미설치·flag OFF·isolated temp 에서 전 경로 검증(canonical 0 touch).

## 8. serial_only=true 근거
- systemd path/timer = single-flight(flock) 필수.
- callback launch audit = 직렬 트랜잭션(병렬 시 이중 callback race·dedupe race).
- result.json dedupe = 순차 ledger(동시 수거 시 중복 callback).
- owner-proof cron-history 조회 = 순차 안전.
- → 단일 executor 직렬 구현. 병렬 dev 분담 금지.

## 9. 범위 밖 (정정 반영)
- **systemd path+timer = 설계/파일/fixture/dry-run 검증까지만.** 실 install/enable/start = 범위 밖.
- ACTIVE=true / production activation / production queue 처리 = 범위 밖.
- "활동 독립 runner" = 코드+dry-run harness 검증. 실가동 = 별도 activation 승인(회장).

## 10. dispatch prompt 초안 (≤3900 bytes 목표 — 발사 미승인)
```
[task-2731 finish-callback 강제 결선 — executor 완료 callback 코드강제]
너는 페룬(dev6). dev1/dev2 closeout 미완(callback 미발사) 구조 결함 해소.
단일 지시서: memory/tasks/task-2731-finish-callback-enforcement.md (sha256 <확정시>). 설계 단일출처: finish_callback_enforcement_followup_task_packet_260610.md.
base=main 95941e4e. ★ worktree 경로 .worktrees/task-2731-dev6 명시 사용(CODE_ROOT 직접 체크아웃 금지). branch task/task-2731-dev6.
목표: executor 가 callback 안 쏘고 죽어도 OS runner 가 result.json 수거→collector closeout→ANU-owned callback launch 코드강제. dev bot prompt 의존 0·finish-task 끝단 단독 의존 0.
expected_files: 신규(anu_callback_launch_audit.py·anu_finish_preflight.py·test) + 수정(anu_result_pickup_runner.py callback 결선·anu_pickup_driver.py·result-json contract terminal_intent·guard branch -rN·devbot prompt). systemd unit=설계/dry-run만.
필수: branch regex -rN 안전인식·worktree mismatch preflight·task-lock 마찰 우회·self-key callback 거부·callback audit jsonl·idempotency. fixture: dev1/dev2 closeout 미완 재현.
금지: ACTIVE=true·systemd install/enable/start·activation_epoch·production/legacy 처리·dev bot callback 자가생성·self-key callback·사후 callback·canonical reset/clean/stash·owner-proof 핵심 변경·expected_files 밖·spec 축약·manual .done·same-PR push·task-2730 재오픈·PR #198/#199 조작.
완료조건: regression GREEN(isolated·canonical 0 touch)·dry-run harness(flag OFF·systemd 미설치)·Codex 재lint HIGH/CRITICAL=0·PR 생성·CI/Gemini gate. merge·activation=회장 승인 전 금지.
serial_only=true. PR open 시 CI_WATCH_HANDOFF.
★ finish/closeout 끝까지 완료(result.json+report+.done+ANU callback 전부 생성 후 종료). ANU key c119085addb0f8b7 callback(self-key 금지)·≤3900 bytes.
```

## 판정
- **TASK2731_FINISH_CALLBACK_ENFORCEMENT_PREP_PACKET_READY**. task id 후보 task-2731·executor dev6 페룬·serial_only=true. expected_files 후보·acceptance 12·fixture 8·forbidden·impl plan(코드 경로)·dispatch prompt 초안. systemd 설계/dry-run 한정(실 install 범위 밖). 발행/dispatch/구현 = 회장 별도 결정. ACTIVE=false·HARD BLOCK 유지.
