# task-2626 — CALLBACK_RUNTIME_ENFORCEMENT_WIRING

- **task_id**: task-2626
- **work_level**: Lv.2-3 (narrow additive runtime 결선 · callback 인프라 핵심)
- **dispatched_by**: ANU (회장 결정 2026-05-21 CALLBACK_RUNTIME_ENFORCEMENT_WIRING 승인)
- **assignee**: dev6 페룬 (callback context 연속 — task-2623/2624/2625)
- **base_audit_docs**:
  - memory/events/callback_self_key_root_cause_audit.json
  - memory/events/task-2625.independent_anu_reverify.result.json

---

## 0. PRE-DISPATCH SPEC (회장 verbatim 박제 · D-SPEC-EXACTNESS · paraphrase 금지)

회장 verbatim (2026-05-21):

> 배경: task-2625에서 normal callback과 fallback 모두 executor self-key로 등록·발화된 것이 확인됐다. build_anu_owned_callback_request / executor_completion_contract / completion_callback_fallback_cancel 모듈은 존재하지만, 실제 봇 callback/fallback 발사 경로에는 결선되어 있지 않다. 따라서 문서 doctrine이 아니라 runtime enforce로 전환한다.
> 목표: 봇이 callback/fallback을 self-key로 등록하지 못하게 하고, ANU-key callback/fallback만 허용하며, normal success 시 fallback cancel-on-success가 실제로 동작하도록 한다.

---

## 1. ★ BASELINE 사실 (ANU 사전 검증 — task-2623 교훈 · feedback_artifact_visibility_contract)

**결선 대상 callback 모듈 5개는 live workspace(/home/jay/workspace)에 untracked(`??`)로만 존재. origin/main 부재 + 로컬 commit 안 됨.**

- `dispatch/normal_fallback_callback_helper.py` (345 lines · working-tree blob 5b5599b3 · **untracked**)
- `dispatch/executor_completion_contract.py` (195 lines · working-tree blob 2a4fdbee · **untracked**)
- `dispatch/callback_owner_enforcer.py` (**untracked**)
- `dispatch/cron_dispatch_guard.py` (**untracked**)
- `dispatch/spec_template_validator.py` (**untracked**)
- `utils/completion_callback_fallback_cancel.py` (★ dispatch/ 아님 — utils/ 에 위치)

3-way clean (origin/main 추적됨):
- `scripts/finish-task.sh` (1363 lines · blob 8462723172ea)
- `dispatch/__init__.py` (4700 lines · blob 7f9d3779739a)
- `prompts/DIRECT-WORKFLOW.md` (588 lines · HEAD blob cfa750cc · ★ 로컬 HEAD 가 origin/main 보다 앞섬)

### 1.1 BASELINE 작업 기준 (Artifact Visibility Contract — 필수)
- **본 task 는 live workspace(/home/jay/workspace) 기준이다.** fresh worktree(origin/main) 에서 callback 모듈 "파일 부재" 로 HOLD 금지.
- untracked callback 모듈 5개를 **git add 하여 함께 커밋**한다 (결선 + 모듈 추적 동시).
- 작업 시작 전 위 8개 파일의 working-tree blob 을 재확인하라 (`git hash-object`). untracked 5개는 origin/main 부재가 정상 (HOLD 사유 아님).

---

## 2. 목표 (회장 verbatim)

- 봇이 callback/fallback 을 self-key 로 등록하지 못하게
- ANU-key callback/fallback 만 허용
- normal success 시 fallback cancel-on-success 가 실제로 동작

---

## 3. 범위 (회장 verbatim)

- narrow additive implementation
- callback/fallback runtime launcher 단일화
- ANU-key enforce
- executor self-key fail-closed
- cancel-on-success 결선
- canonical root 명시
- no live PR/merge/branch/main/credential write

---

## 4. expected_files (회장 verbatim 후보 — 봇이 실제 결선 위치 확정)

1. `dispatch/normal_fallback_callback_helper.py`
2. `dispatch/executor_completion_contract.py` 또는 신규 callback launcher wrapper
3. `scripts/finish-task.sh` 또는 실제 completion callback 발사 hook
4. `prompts/DIRECT-WORKFLOW.md`
5. `tests/regression/test_callback_runtime_enforcement_2626.py`

추가 허용(결선 필요 시): `utils/completion_callback_fallback_cancel.py` (cancel-on-success 결선) · `dispatch/__init__.py` (봇 spawn cron 등록처 결선). 봇이 최소 결선 경로 확정.

---

## 5. 필수 구현 (회장 verbatim · 10항목)

1. normal callback 등록은 build_anu_owned_callback_request 경유
2. fallback safety-net 등록도 동일 helper 경유
3. owner key가 executor self-key면 argv=None 또는 fail-closed
4. ANU key(c119085addb0f8b7)만 authoritative collector key 허용
5. callback/fallback prompt는 UTF-8 3900 bytes 이하
6. callback prompt는 envelope-only
7. normal callback success 시 fallback cancel-on-success
8. fallback은 recovery-only, final report trigger 금지
9. canonical root=/home/jay/workspace 명시
10. self-key collector/adjudication 결과는 non-authoritative

---

## 6. 필수 regression (회장 verbatim · 10건)

test 파일: `tests/regression/test_callback_runtime_enforcement_2626.py` · mock-based · 실 cron 0 · 실 발사 0 · subprocess 0

1. ANU key normal callback → PASS
2. executor self-key normal callback → FAIL_CLOSED
3. ANU key fallback → PASS
4. executor self-key fallback → FAIL_CLOSED
5. normal callback success → fallback cancel-on-success
6. fallback fires after success → NO_OP, no ledger append
7. result exists + normal missing + fallback missing → DISPATCH_CONTRACT_VIOLATION
8. callback prompt >3900 bytes → CALLBACK_PROMPT_TOO_LARGE
9. canonical root missing → fail-closed or root correction
10. self-collector attempt → SELF_COLLECTOR_FORBIDDEN

---

## 7. 금지 (회장 verbatim)

- 실제 PR open
- merge
- branch/commit/push (단 §1.1 로컬 커밋은 BOT App token 부재로 push 불가 — 로컬 커밋만, push/PR/merge 0)
- credential raw exposure
- Track C task-2619 수정
- zombie cron 정리
- replacement PR runner 수정 (utils/replacement_pr_runner.py)
- merge queue 수정 (utils/merge_queue_executor.py)
- 기존 task-2625 산출물 삭제
- self-collector 결과 authoritative 승격

---

## 8. ★ 본 task 자체의 callback self-key 위험 (필수 인지)

본 task 가 만드는 enforcement 는 **머지/결선 완료 전까지 런타임에 적용 안 됨**. 따라서 본 task-2626 의 normal callback/fallback 도 여전히 executor self-key 로 발사될 위험이 잔존한다.

- 봇은 callback 발사 시 ANU key(c119085a) 발사를 시도하되, self-key 로 발사되더라도 산출물(코드) 자체는 ANU 독립 재검증으로 판정된다.
- 본 task 의 collector 결과가 self-key 면 NON_AUTHORITATIVE — ANU 독립 재검증 필수 (회장 인지).
- result.json 에 callback contract 9 fields 기록 (self-key 여부 관측).

---

## 9. 14단계 finalize + 8 직접 행동 (회장 doctrine 강제 · wrapper 처리 표현 금지)

### 9.1 finalize 14단계
1. expected_files 정확 일치 검증 (외부 lint 1회)
2. forbidden_paths 검증 (replacement_pr_runner/merge_queue_executor/task-2619/task-2625 산출물 무변경)
3. regression test PASS (10건 all pass · pytest tests/regression/test_callback_runtime_enforcement_2626.py)
4. 전체 suite 회귀 0 (기존 callback/dispatch regression PASS 카운트 유지)
5. lint/type check (ruff/mypy 프로젝트 기본)
6. commit (BOT identity · untracked callback 모듈 5개 git add 포함 · token raw 노출 0 · force/admin/rebase 금지)
7. push 시도 (BOT App token 부재 시 로컬 커밋 유지 · push/PR/merge 0 · §1.1)
8. PR create 시도 (App token 부재 시 skip · ANU 콜백으로 로컬 완료 보고)
9. CI (App token 부재 시 로컬 검증으로 대체)
10. Gemini review (App token 부재 시 해당없음)
11~13. (App token 부재 시 해당없음 — 로컬 커밋만)
14. 자기 callback 발사 시 ANU key 시도 + result.json 9 fields 기록 (§8)

### 9.2 봇 직접 행동 8항목
1. task md 본문 읽기 · 9-R 검증 · 본질 변형 0
2. expected_files 외 파일 변경 0 (단 §1.1 untracked 5개 git add 허용)
3. forbidden_paths 탐지 시 즉시 Critical7 보고 + halt
4. credential value 로그/PR/cron prompt 평문 노출 0
5. BOT_GITHUB_TOKEN (ghs_) 만 사용 시도 · OWNER_PAT/ghp_ 절대 금지
6. canonical root=/home/jay/workspace 명시 결선
7. result.json 에 callback contract 9 fields 모두 PRESENT
8. callback 발사 시 ANU key(c119085a) 시도 (§8 self-key 위험 인지)

---

## 10. callback contract 9 필수 fields (result.json)

1. callback_prompt_utf8_bytes (int) 2. callback_prompt_chars (int) 3. callback_cron_id (string|null) 4. callback_registration_status 5. callback_role ("COLLECTOR_ANU") 6. envelope_only_compliance (bool) 7. fallback_prompt_utf8_bytes (int) 8. fallback_safety_net_registered (bool) 9. fallback_safety_net_role_single_purpose ("RECOVERY_ONLY_NO_FINAL_REPORT_TRIGGER")

callback prompt UTF-8 ≤3900 bytes · envelope-only · 초과 시 CALLBACK_PROMPT_TOO_LARGE fail-closed.

---

## 11. doctrines (필수)

- callback/fallback 발사를 봇 자율 텍스트 지시에 의존 금지 → runtime 코드 enforce (본 task 핵심)
- build_anu_owned_callback_request 단일 launcher 경유 (normal+fallback 둘 다)
- executor self-key → fail-closed (argv=None)
- cancel-on-success 코드 결선 (completion_callback_fallback_cancel.py)
- canonical root=/home/jay/workspace 명시 (self-key spawn canonical 미발견 방지)
- self-collector 결과 NON_AUTHORITATIVE
- baseline 3-way blob 재검증 (feedback_audit_baseline_3way_blob_verify_260521)
- artifact visibility: live workspace 기준 (feedback_artifact_visibility_contract — untracked 5개 git add)
- attempt-1 only · spec drift/baseline 불일치 시 HOLD + 회장 escalate (task-2623 페룬 선례)

---

## 12. 완료 보고 (회장 verbatim · 9항목)

result.json + report 가 답변:
1. runtime 결선 위치
2. self-key 차단 결과
3. ANU-key callback/fallback 등록 결과
4. cancel-on-success 결선 결과
5. canonical root 처리 결과
6. regression 결과 (10건)
7. Codex audit 결과 (HIGH/CRITICAL 0)
8. PR/merge/credential/write 0 증거
9. 남은 risk (본 task callback 자체 self-key 위험 포함)

---

## allowed_resources (본 task의 capability)

```yaml
allowed_resources:
  paths:
    - "dispatch/normal_fallback_callback_helper.py"
    - "dispatch/executor_completion_contract.py"
    - "dispatch/callback_owner_enforcer.py"
    - "dispatch/cron_dispatch_guard.py"
    - "dispatch/spec_template_validator.py"
    - "dispatch/__init__.py"
    - "utils/completion_callback_fallback_cancel.py"
    - "scripts/finish-task.sh"
    - "prompts/DIRECT-WORKFLOW.md"
    - "tests/regression/test_callback_runtime_enforcement_2626.py"
    - "memory/events/task-2626.result.json"
    - "memory/reports/task-2626.md"
    - "memory/tasks/task-2626.md"
  forbidden_paths:
    - "utils/replacement_pr_runner.py"
    - "utils/merge_queue_executor.py"
    - "memory/tasks/task-2619.md"
    - "memory/events/task-2625.*"
    - "anu_v2/**"
    - ".github/**"
    - ".env*"
    - "*.pem"
    - "*.key"
  commands:
    - "pytest"
    - "python3 -m py_compile"
    - "ruff"
    - "git"
  merge_policy: "manual"
  ttl_hours: 24
```

## 13. 산출물

- 결선된 callback 모듈들 (untracked 5개 git add 포함)
- 신규 callback launcher (필요 시)
- `tests/regression/test_callback_runtime_enforcement_2626.py` (10 regression)
- `memory/events/task-2626.result.json` (9 fields + 9항목 완료 보고)
- `memory/reports/task-2626.md`
- 로컬 커밋 (push/PR/merge 0 · BOT App token 부재)

## 14. callback envelope (참고)

```
task_id=task-2626
result_path=memory/events/task-2626.result.json
report_path=memory/reports/task-2626.md
sha256=<task md sha256>
collector_role=ANU
owner_key=c119085addb0f8b7
summary=CALLBACK_RUNTIME_ENFORCEMENT_WIRING result · 9항목 result.json 참조
```

끝