---
scope: task
task_id: task-2630
title: "L4 wiring — callback lifecycle classifier 실결선 (CALLBACK_RUNTIME_ENFORCEMENT L4)"
owner: dev6 페룬
base: origin/main 94540f67
local_commit: 9061c7c251c056fc9458d171eed9210c7918806c
executor_window: "2026-05-22 19:03:58 ~ 2026-05-22 19:2x KST"
status: LOCAL_COMMIT_READY_FOR_ANU_REATTACH (push/PR/merge 0)
---

# task-2630 — L4 wiring: callback lifecycle classifier 실결선

## S (Situation)

origin/main 94540f67(PR #132 머지분)에 callback lifecycle 분류의 L1+L2+L3 계층이 이미 반영돼 있다.
- L3 classifier core: `utils/callback_lifecycle_classifier.py` (evidence-only 순수함수, 2축+root_cause)
- L1+L2: `dispatch/executor_completion_contract.py`(executor completion contract), `dispatch/normal_fallback_callback_helper.py`(9-field per-callback contract, 회장 §10)
- frozen fixtures: `tests/fixtures/callback_lifecycle/{task-2625,task-2628,task-2628_plus_1,unknown_insufficient}/`

단일소스 스펙: `memory/specs/system_callback_lifecycle_state_schema_260522.md` (§8 fields 10~14, §9 wiring 후보).

## C (Complication)

classifier core는 존재하나 **실제 결선(wiring)**이 안 돼 있었다. executor completion contract는 result.json에 9-field per-callback contract만 emit할 뿐, lifecycle 분류(축A/B/C)를 append하지도, `memory/events/<task>.callback_lifecycle.json` artifact를 생성하지도 않았다. 결과적으로 "callback이 안 온 경우"를 단일 사건으로 뭉개는 위험(진행 트리거 오판·책임귀속 오류·DELIVERY_GAP 남용)이 런타임에서 잔존.

제약: BOT App token 부재 → push/PR/merge 불가(로컬 commit only). expected_files 밖 수정 금지. foreign dirty 5건 미접촉. callback 재발사 금지. production enforcement 완료 판정 보류(L4 merge 후 별도).

## Q (Question)

classifier core를 executor completion contract에 어떻게 **append-only**로 결선해, 기존 9-field contract를 보존한 채 result.json fields 10~14를 emit하고 idempotent artifact를 생성하면서, 동시에 모든 [금지]·[frozen anchor]를 준수할 수 있는가?

## A (Answer) — 구현

expected_files 2개만 수정(코드 1 + 테스트 1). 신규 모듈/파일 신설 0 (최소 범위).

### 1) `dispatch/executor_completion_contract.py` — L4 wiring 섹션 append (+213/-1)

기존 본문(task-2553+32 contract)은 무손실 보존하고, 파일 끝에 wiring 섹션만 추가:

- `LIFECYCLE_RESULT_FIELDS` — result.json fields 10~14 키 단일소스
  (delivery_outcome / normal_callback_miss_cause / root_cause_tags /
  lifecycle_state_evidence / classified_by / applied_count)
- `CALLBACK_CONTRACT_9_FIELDS` — 보존 대상 9-field 매니페스트(단일소스는
  `normal_fallback_callback_helper._contract_fields`; drift guard 테스트로 동기 검증)
- `classify_completion_lifecycle(evidence)` — **classifier 결선 진입점** (필수구현 2)
- `append_lifecycle_fields(contract_9_fields, evidence)` — **append-only 확장**
  (필수구현 1/4 · ANCHOR-2): 입력 dict 불변, 9-field와 키 충돌 시 `ValueError`(덮어쓰기 차단)
- `callback_stage_separation(lse)` — **gate PASS / notification sent / collector received
  분리 기록**(필수구현 5 · §0 핵심구분)
- `build_callback_lifecycle_artifact(task_id, evidence)` — artifact dict(순수·결정적),
  `artifact_kind="callback_lifecycle_classifier"`로 fallback collector artifact와 구분(필수구현 6)
- `write_callback_lifecycle_artifact(task_id, evidence, events_dir=None)` —
  `memory/events/<task>.callback_lifecycle.json` **idempotent** writer(필수구현 3):
  결정적 직렬화(sort_keys+고정 indent+trailing newline) → 동일 입력 byte-identical,
  내용 동일 시 재기록 skip(mtime 보존). cron/subprocess/발사 0 — 파일 1개만 생성.
- `default_events_dir()` — `WORKSPACE_ROOT` env override 우선(테스트 live-workspace 의존 0)

신규 의존: `utils.callback_lifecycle_classifier`(순수함수·I/O 0·anu_v3 import 0) 하나뿐.
기존 tracked module 본문 미수정.

### 2) `tests/regression/test_callback_lifecycle_wiring_2630.py` — 신규 통합 테스트 (17 PASS)

회장 필수 regression 10항 전부 커버(`tests/dispatch` shadow 우회는 기존
test_callback_runtime_enforcement_2626.py와 동일 `_load_real` 패턴):
1. task-2625 → SELF_KEY_FIRED_NON_AUTHORITATIVE (SELF_KEY_FAIL_CLOSED와 분리)
2. task-2628 → FINISH_TASK_GIT_GATE_BLOCKED_BEFORE_CALLBACK + FOREIGN_DIRTY_BLOCKER
3. task-2628+1 → FALLBACK_COLLECTOR_APPLIED + ENVELOPE_PREPARED_NOT_FIRED
4. unknown_insufficient → UNKNOWN/INSUFFICIENT_EVIDENCE (추정 0)
5. 9-field 보존 + manifest drift guard + append-only 충돌 거부
6. fields 10~14 append(9+6=15 keys) + stage separation + fallback artifact 구분
7. callback_lifecycle.json idempotent(byte-identical·단일 파일)
8. live workspace 의존 0 (WORKSPACE_ROOT override·tmp_path)
9. foreign dirty 미접촉(sentinel 불변·생성 파일 1개)
10. callback 재발사 0 (subprocess/os.system monkeypatch 미호출·발사 함수 부재)

## 검증 결과

- **신규 wiring 테스트: 17 passed** (필수 10항 전부 통과)
- **full tests/regression: 신규 fail 0** — 868 passed / 3 failed / 11 skipped.
  3 fail은 base 94540f67에서도 동일한 pre-existing 실패(`test_stash_origin_audit_compat.py`,
  finish-task.sh stash audit 관련, 본 L4와 무관). base 851→868(+17 신규) 외 변동 0.
- **full tests/ collection error 7건** = base 94540f67에서 byte-identical 재현(scripts.conftest
  부재 등 인프라 이슈, 본 변경과 무관).
- **실 closeout 1건 데모(task-2630 자신)**: 9-field + fields 10~14가 result.json에 동시 존재(15 keys),
  `memory/events/task-2630.callback_lifecycle.json` 생성+idempotent(2회 byte-identical).
  delivery_outcome=NOT_YET_COLLECTED, miss_cause=ENVELOPE_PREPARED_NOT_FIRED,
  root_cause_tags=[BOT_APP_TOKEN_ABSENT, REFLECTION_NOT_MERGED], classification=normal
  (pre-ANU-fire snapshot — ANU-key authoritative callback은 closeout 최종 단계 발사).

## frozen anchor 준수

- ANCHOR-1: fields 10~14 append emit + `memory/events/<task>.callback_lifecycle.json` 생성 ✓
- ANCHOR-2: 9-field 대체 아님·append-only(충돌 시 ValueError) ✓
- ANCHOR-3: CALLBACK_DELIVERY_GAP residual-only + SELF_KEY_FAIL_CLOSED ↔
  SELF_KEY_FIRED_NON_AUTHORITATIVE 분리 유지(classifier 보존, 테스트 assert) ✓
- ANCHOR-4: production enforcement 완료 판정 보류(L4 merge 후 별도) ✓

## 금지 준수

L4 범위 밖 수정 0 · foreign dirty 5건 미접촉 · replacement_pr_runner 미접촉 ·
callback 재발사 0 · marker 삭제 0 · production enforcement 완료 판정 0 · admin override 0 ·
push/PR/merge 0.

## credential (3계층 doctrine)

BLOCKING_SECRET(ghp_/ghs_/PEM/AWS) 신규유입 0. 추가 라인에 key 식별상수 literal 0
(classifier가 `DEFAULT_ANU_KEYS`를 import 재사용 — 신규 노출 아님). red-team-auto-review:
risk_level=low, passed=true.

## 산출물 귀속

- executor: dev6 페룬
- executor window 시작: 2026-05-22 19:03:58 KST
- base: origin/main 94540f67
- local commit: 9061c7c251c056fc9458d171eed9210c7918806c (isolated clean worktree)
- 다음 단계: ANU가 OWNER 권한으로 로컬 commit 재적층 → push → PR open → Gemini auto-remediation → 회장 8항목 보고 → 회장 별도 merge 승인.
