# task-2627 보고서 — CALLBACK_RUNTIME_ENFORCEMENT_FRESH_REFLECTION (B안)

- **task_id**: task-2627 / **team**: dev6-team / **work_level**: Lv.3
- **작성자**: 페룬(개발6팀장) / **작성일**: 2026-05-21 16:25 KST
- **판정**: ✅ **REFLECTION_READY** (3 risk를 코드+regression으로 closure · 로컬 reflection 커밋 · push/PR/merge 0 · 회장 승인 대기)

---

## S — Situation
task-2626이 commit 4a01553a(9 files / 3171 ins)로 callback runtime enforcement(단일 launcher + self-key fail-closed)를 구현했으나, **로컬 HEAD에만 존재하고 origin/main 미반영** 상태였다. ANU 독립 재검증(task-2626.independent_reverify)은 구현은 clean(REVERIFY_PASS)이나 runtime 결선이 PARTIAL이라고 판정했다. 회장은 2026-05-21 **B안(fresh origin/main replacement reflection)**을 승인했다.

## C — Complication
ANU 재검증이 지적한 **잔여 3 risk**:
1. 4a01553a main 미반영 → origin/main 기준 self-key callback 취약점 전역 잔존
2. cancel-on-success 모듈은 존재하나 호출처 결선 0 (finish-task/extract_followup/launch_callback grep 빈 출력)
3. finish-task §5.5 게이트가 non-blocking → 게이트 외 self-key 직접 발사 우회 가능

추가로, 로컬 HEAD(edf05f49)가 origin/main을 앞서고 task-2625 오염 위험이 있어 로컬 HEAD를 그대로 쓸 수 없었다(feedback_local_operational_patch_doctrine).

## Q — Question
fresh origin/main worktree에서 4a01553a의 clean 부분만 per-task diff로 재적용하면서, 3 risk를 코드와 regression으로 닫아 **production path 안전 반영 직전 상태(reflection ready)**에 도달할 수 있는가? main..HEAD divergence 오염 없이?

## A — Answer
**예. 달성했다.** fresh origin/main(4187332c) worktree에서 4a01553a의 9 expected_files를 per-task diff로 반영하고, 3 risk를 다음과 같이 closure했다.

### Risk 1 — fresh origin/main reflection (전역 취약 차단 준비)
- fresh worktree `git worktree add -b task/task-2627-dev6 origin/main` (HEAD=4187332c 검증).
- 7개 신규 파일(callback 모듈 5 + utils cancel + test)을 `git checkout 4a01553a -- <file>`로 clean 추출. 5개 pure-clean 파일은 4a01553a와 **blob 동일성 검증(byte-0)**.
- 실 발사 경로 `scripts/extract_followup.send_anu_cron`에 `enforce_callback_fire` owner-key fail-closed 게이트 결선 → self-key는 발사 차단, ANU key는 항상 PASS(production blast radius 0), ImportError-safe.

### Risk 2 — cancel-on-success 호출처 결선 (CLOSED)
- `dispatch/normal_fallback_callback_helper.py`에 `cancel_fallback_for_completed_task()` 추가 → `utils.completion_callback_fallback_cancel.cancel_fallback_on_success`의 **실 호출처 결선**(4a01553a까지 호출처 0이던 PARTIAL 해소).
- `finish-task.sh §5.5b`에서 `cancel-on-success` subcommand 호출. durable evidence/5조건 미충족 시 안전 no-op(오발 제거 0). 뒤늦은 fallback 발화는 cancel lock으로 NO_OP/no ledger append.

### Risk 3 — finish-task 게이트 blocking + 게이트 외 self-key hard-block (CLOSED)
- `enforce_callback_fire()` 단일 chokepoint: `gate_route != "finish_task_anu_gate"` + owner==executor self-key → **`GATE_BYPASS_SELF_KEY_FORBIDDEN` FAIL_CLOSED(argv None)**. gate_route 미지정도 self-key default-deny.
- `finish-task.sh §5.5`: `fire --gate-route finish_task_anu_gate`로 변경. FAIL_CLOSED 시 `callback-gate-blocked.json` 생성 + **task completion은 계속**(회장 §4 단서: self-key 등록만 차단, completion 죽이지 않음).
- 신규 `test_11`: 게이트 외 self-key 직접 발사 우회 → FAIL_CLOSED 검증.

---

## 생성/수정 파일 (reflection diff: origin/main..HEAD, 10 files / 3423 ins)
**신규 (4a01553a clean 추출, 7):**
- dispatch/callback_owner_enforcer.py, dispatch/cron_dispatch_guard.py, dispatch/executor_completion_contract.py, dispatch/spec_template_validator.py, utils/completion_callback_fallback_cancel.py (5개 blob 동일)
- dispatch/normal_fallback_callback_helper.py (629→776, enforce_callback_fire + cancel wrapper + fire/cancel CLI)
- tests/regression/test_callback_runtime_enforcement_2626.py (351→409, test_11)

**수정 (per-task diff base + 3 risk 보강, 3):**
- scripts/finish-task.sh (+33, §5.5 blocking 게이트 + cancel-on-success 결선)
- scripts/extract_followup.py (+34, send_anu_cron fail-closed 게이트 결선 — §5 추가허용)
- prompts/DIRECT-WORKFLOW.md (+16, doctrine closure)

**task 산출물 (canonical memory):** memory/events/task-2627.result.json, memory/reports/task-2627.md

### 변경 파일 (canonical 경로 — git 변경 대조용)
reflection 대상 파일의 canonical 경로 (동일 content가 local 브랜치 git 이력에 존재 · 본 task-2627 reflection 커밋은 worktree 브랜치 task/task-2627-dev6):
- /home/jay/workspace/dispatch/normal_fallback_callback_helper.py
- /home/jay/workspace/dispatch/callback_owner_enforcer.py
- /home/jay/workspace/dispatch/cron_dispatch_guard.py
- /home/jay/workspace/dispatch/executor_completion_contract.py
- /home/jay/workspace/dispatch/spec_template_validator.py
- /home/jay/workspace/utils/completion_callback_fallback_cancel.py
- /home/jay/workspace/scripts/finish-task.sh
- /home/jay/workspace/prompts/DIRECT-WORKFLOW.md
- /home/jay/workspace/tests/regression/test_callback_runtime_enforcement_2626.py

### 수정 파일별 검증 상태
(reflection 산출물은 worktree에 존재 — grep 검증은 worktree 절대경로 기준)

| 파일 | 변경 내용 | grep 검증 | 상태 |
| --- | --- | --- | --- |
| /home/jay/workspace/.worktrees/task-2627-dev6/dispatch/normal_fallback_callback_helper.py | enforce_callback_fire 단일 게이트 + cancel_fallback_for_completed_task 결선 + fire/cancel CLI | grep "enforce_callback_fire" OK | verified |
| /home/jay/workspace/.worktrees/task-2627-dev6/scripts/finish-task.sh | §5.5 blocking 게이트 + cancel-on-success 결선 | grep "finish_task_anu_gate" OK | verified |
| /home/jay/workspace/.worktrees/task-2627-dev6/scripts/extract_followup.py | send_anu_cron fail-closed 게이트 결선 | grep "enforce_callback_fire" OK | verified |
| /home/jay/workspace/.worktrees/task-2627-dev6/prompts/DIRECT-WORKFLOW.md | callback runtime 강제 doctrine closure | grep "GATE_BYPASS_SELF_KEY_FORBIDDEN" OK | verified |
| /home/jay/workspace/.worktrees/task-2627-dev6/tests/regression/test_callback_runtime_enforcement_2626.py | test_11 게이트 외 self-key 우회 FAIL_CLOSED (총 11건) | grep "test_11_gate_bypass_self_key_direct_fire_fail_closed" OK | verified |

---

## 테스트 결과
- **regression 11 passed in 0.11s** (회장 §4 11건 정확, test_11 신규). mock-only · 실 cron 0 · 실 발사 0 · subprocess 0.
- py_compile PASS (helper, extract_followup) · bash -n PASS (finish-task.sh) · callback 5모듈 import OK.

## ★ L1 스모크테스트 결과 (필수 기록)
- **서버 재시작**: 해당없음 (시스템 dispatch 모듈, 서버 아님)
- **API/CLI 응답 확인**:
  - (a) ANU key + 정식 게이트 → `verdict=PASS status=ANU_OWNED_READY argv=ANU-keyed rc=0`
  - (b) self-key + 게이트 외 → `verdict=FAIL_CLOSED status=GATE_BYPASS_SELF_KEY_FORBIDDEN argv=None rc=2`
  - (c) cancel-on-success marker 부재 → `classification=SKIPPED_NORMAL_FAILED cron_remove_invoked=False rc=0` (안전 no-op)
- **스크린샷**: 해당없음 (CLI/모듈 작업)

## callback contract 9 fields (result.json §10)
ANU-key 단일 게이트 발사 검증값: utf8_bytes=63, chars=63, cron_id=task-2627::normal, status=ANU_OWNED_READY, role=COLLECTOR_ANU, envelope_only=true, fallback_utf8=0, fallback_registered=false, fallback_role=RECOVERY_ONLY_NO_FINAL_REPORT_TRIGGER. **self-key 아님(ANU-owned)**. 단, main 미반영 단계이므로 collector/adjudication은 NON_AUTHORITATIVE → ANU 독립 재검증 대상.

---

## 발견 이슈 및 해결
1. **anu_v3 dependency closure (★ 회장 결정 필요)**: callback 모듈은 `anu_v3.dispatch_callback_contract` 등 5개를 import하나 **origin/main에 anu_v3 0 files**(local HEAD에 10 files tracked). 테스트는 `tests/conftest.py`가 `/home/jay/workspace`를 sys.path 주입하여 통과하나, **실 main 반영 시 anu_v3 동반 반영이 import precondition**. 본 task expected_files 밖이므로 해결하지 않고 회장 결정 항목으로 명시 → result.json §9.
2. **pyright reportMissingImports**: worktree 경로 미해석 경고. 런타임 PYTHONPATH/conftest로 resolve, py_compile/pytest PASS → 실 에러 아님.
3. **extract_followup blast radius**: 실 발사 경로는 모든 팀 통지에 쓰이므로, self-key/gate-bypass에 한해서만 차단하고 그 외 게이트 이상은 ANU key로 진행(ImportError-safe)하여 production 위험 최소화.

## 머지 판단
- **머지 필요**: No (회장 승인 전 push/PR/merge 금지)
- **브랜치**: task/task-2627-dev6
- **워크트리 경로**: /home/jay/workspace/.worktrees/task-2627-dev6 (reflection HEAD 49143cff)
- **머지 의견**: 3 risk가 코드+regression(11 PASS)으로 closure됨. **REFLECTION_READY**. 실 origin/main 반영은 (1) 회장/OWNER 승인 + (2) anu_v3 dependency 동반 반영을 precondition으로 별도 수행. main 반영 전에는 production enforcement 완료 판정 금지(회장 §8).

## 모델 사용 기록
- 스바로그(백엔드): sonnet — 3 risk closure 코드 4파일 (helper/finish-task/extract_followup/doctrine)
- 벨레스(테스터): sonnet — test_11 + regression 검증
- 페룬(팀장): opus — 설계/통합/L1 스모크/검증/보고. 디자인 작업 없음(디자인팀 호출 불요).
- haiku 미사용.

## 게이트
- **G1 설계**: affected_files 전부 allowed_resources 내 · forbidden path diff 0 · 다른 팀 겹침 없음.
- **G2 구현**: 팀 테스터(벨레스) 기능 테스트 11건 PASS + L1 스모크.
- **G3 머지**: PR 미생성(회장 승인 전 금지). reflection ready 보고로 대체.

## 세션 통계
- 총 도구 호출: 0회

