# task-2629 — CALLBACK_LIFECYCLE_CLASSIFIER_CORE (evidence-only decoupled · Phase 2)

- **task_id**: task-2629
- **work_level**: Lv.3 (신규 evidence-only classifier 모듈 + frozen fixture regression · core only)
- **dispatched_by**: ANU (회장 결정 2026-05-22 — schema 승인 + Phase 2 evidence-only decoupled 채택)
- **assignee**: dev6 페룬 (callback lifecycle context 연속 — task-2627/2628/2628+1)
- **base_branch**: origin/main 4187332c (★ clean base — anu_v3 불필요, decoupled)
- **single_source_spec**: memory/specs/system_callback_lifecycle_state_schema_260522.md (회장 승인 2축 schema)

---

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

> Phase 2 callback lifecycle classifier는 원안 anu_v3/ 직결 방식이 아니라, evidence-only decoupled 방식으로 진행한다. core와 wiring은 분리한다.
> 결정: A. evidence-only decoupled classifier 채택 B. core/wiring 분리 채택 C. frozen evidence snapshot fixture 채택 D. UNKNOWN/INSUFFICIENT_EVIDENCE 상태 추가 E. task-2625 delivery_outcome은 MANUAL_ANU_REVERIFY로 pin.
> 원칙: classifier core는 anu_v3 런타임 모듈을 import하지 않는다. result.json, schedule_history, cron owner key, escalate.reason, collector artifact, applied_count/callback_ack, git status snapshot 같은 evidence만 입력으로 받아 delivery_outcome / normal_callback_miss_cause / root_cause_tags를 판정한다.

---

## 1. 본 task 범위 = CORE ONLY (wiring 제외)

- ✅ **포함**: evidence snapshot → (delivery_outcome, normal_callback_miss_cause, root_cause_tags, evidence_completeness) 판정하는 **순수 함수 classifier** + frozen fixture 3건 + regression.
- ❌ **제외(wiring)**: executor completion contract / result.json fields 10~14 emit 결선은 **본 task 아님**. task-2627 callback enforcement integration bundle 로 이관(완료보고 #7 에 handoff 목록만 작성).
- 이유: core 는 anu_v3/callback enforcement 미머지 산과 무관하게 **독립 머지 가능**해야 함. 새 미머지 local-only dependency 산 만들지 말 것(회장 verbatim).

---

## 2. 구조 (회장 verbatim)

### 2.1 Core classifier
- evidence snapshot → 2축/태그 분류
- **순수 함수** · **read-only** · **anu_v3 dependency 없음**
- task-2625 / 2628 / 2628+1 frozen fixture regression 포함
- 먼저 독립적으로 구현 가능

### 2.2 Wiring (본 task 제외 — handoff)
- executor completion contract / result.json fields 10~14 emit 결선
- task-2627 callback enforcement layer 와 함께 integration bundle 로 처리
- callback reflection / anu_v3 dependency closure 순서와 충돌하지 않게 별도 단계로 관리

---

## 3. 구현 위치 (decoupled 확정)

- **core 위치 권고**: `utils/callback_lifecycle_classifier.py` (utils/ 는 origin/main 존재·저결합·anu_v3 무관 → decoupled 적합).
- **enum 상수**: 같은 모듈 내 또는 `utils/callback_lifecycle_states.py` (delivery_outcome / normal_callback_miss_cause / root_cause_tags / evidence_completeness 단일소스).
- ★ **anu_v3 import 0** 필수 — `import anu_v3` / `from anu_v3` 한 줄도 금지. (완료보고 #6 에서 grep 0 증명)
- 봇이 더 나은 decoupled 위치 판단 시 근거와 함께 제시 가능(단 anu_v3 의존 0 불변).

---

## 4. Schema (회장 승인 2축 + 보강 D)

### 4.1 delivery_outcome (축A)
NORMAL_CALLBACK_RECEIVED / FALLBACK_COLLECTOR_APPLIED / DUPLICATE_FALLBACK_NO_OP / MANUAL_ANU_REVERIFY / NOT_YET_COLLECTED / **UNKNOWN(INSUFFICIENT_EVIDENCE)**

### 4.2 normal_callback_miss_cause (축B)
NONE / ENVELOPE_PREPARED_NOT_FIRED / FINISH_TASK_GIT_GATE_BLOCKED_BEFORE_CALLBACK / SELF_KEY_FAIL_CLOSED_BEFORE_FIRE / SELF_KEY_FIRED_NON_AUTHORITATIVE / CALLBACK_DELIVERY_GAP / CALLBACK_CONTRACT_VIOLATION / **UNKNOWN(INSUFFICIENT_EVIDENCE)**

### 4.3 root_cause_tags (축C · 다중)
FOREIGN_DIRTY_BLOCKER / BOT_APP_TOKEN_ABSENT / REFLECTION_NOT_MERGED / GIT_GATE_SHARED_WORKSPACE_MISFIRE / SCOPE_GUARD_MAIN_HEAD_DIVERGENCE / CALLBACK_ENVELOPE_ONLY / (evidence-derivable 추가, append-only)

### 4.4 필수 보강 (회장 verbatim)
- delivery_outcome 에 UNKNOWN 또는 INSUFFICIENT_EVIDENCE 추가
- normal_callback_miss_cause 에도 UNKNOWN/INSUFFICIENT_EVIDENCE 추가 가능
- **evidence_completeness 필드를 둔다** (예: COMPLETE / PARTIAL / MISSING + 어떤 증거 소스가 결핍인지)
- **증거가 없으면 추정하지 말고 UNKNOWN 으로 둔다** (환각/추정 금지 doctrine)
- **CALLBACK_DELIVERY_GAP 은 residual 로만 사용** (ANU key 등록+발사됐는데 collector 미spawn + BEFORE_FIRE 원인 전무일 때만)

### 4.5 분류 규칙 (스펙 §3.1/§5/§6 준수)
- SELF_KEY_FAIL_CLOSED_BEFORE_FIRE(안전 차단·argv=None) vs SELF_KEY_FIRED_NON_AUTHORITATIVE(실제 발사·incident) **반드시 분리** (cron owner key 가 self 이고 발사됨 → 후자 / 등록 차단됨 → 전자).
- self-key fail-closed 가 token 부재·reflection 미머지 등과 **공존** 시 축B=ENVELOPE_PREPARED_NOT_FIRED(umbrella) + 축C 태그(SELF_KEY_FAIL_CLOSED_BEFORE_FIRE 포함).
- 축B precedence = 상태기계상 가장 이른 차단 전이 (스펙 §5).

---

## 5. Fixture 원칙 (회장 verbatim · frozen)

live workspace 파일을 직접 읽지 말고, frozen evidence snapshot 을 사용하라. 경로:
- `tests/fixtures/callback_lifecycle/task-2625/`
- `tests/fixtures/callback_lifecycle/task-2628/`
- `tests/fixtures/callback_lifecycle/task-2628_plus_1/`

각 fixture 에 classifier 입력 증거 고정: result.json(또는 excerpt), schedule_history excerpt, cron owner key snapshot, escalate.reason, collector artifact 존재 여부, applied_count/callback_ack, git_status snapshot.

- 캡처 방법: 가능한 한 live 증거(memory/events/task-262*.* · 스펙 §7)에서 read-only 캡처해 freeze. 결핍 증거는 스펙 §7 문서화된 사건 증거에 충실한 snapshot 으로 구성(임의 날조 금지 · 사건 사실에 근거).
- ★ regression 은 frozen snapshot 만 입력 → live workspace 상태(foreign dirty 정리·reflection 머지 등)에 **비의존**(결정적).

---

## 6. 필수 매핑 (회장 verbatim · 단일값 assert)

1. **task-2625**: delivery_outcome=MANUAL_ANU_REVERIFY · normal_callback_miss_cause=SELF_KEY_FIRED_NON_AUTHORITATIVE · root_cause_tags=[REFLECTION_NOT_MERGED] · classification=incident
2. **task-2628**: delivery_outcome=MANUAL_ANU_REVERIFY · normal_callback_miss_cause=FINISH_TASK_GIT_GATE_BLOCKED_BEFORE_CALLBACK · root_cause_tags=[FOREIGN_DIRTY_BLOCKER, GIT_GATE_SHARED_WORKSPACE_MISFIRE]
3. **task-2628+1**: delivery_outcome=FALLBACK_COLLECTOR_APPLIED · normal_callback_miss_cause=ENVELOPE_PREPARED_NOT_FIRED · root_cause_tags=[SELF_KEY_FAIL_CLOSED_BEFORE_FIRE, BOT_APP_TOKEN_ABSENT, REFLECTION_NOT_MERGED]

추가 권고 fixture(선택): UNKNOWN/INSUFFICIENT_EVIDENCE 케이스 1건(증거 결핍 입력 → UNKNOWN + evidence_completeness=MISSING) — 보강 D 검증.

---

## 7. regression

test: `tests/regression/test_callback_lifecycle_classifier.py` · pure function · 실 cron 0 · 실 발사 0 · subprocess 불요(또는 hermetic) · live 의존 0
- 3 fixture → 기대 (축A,축B,축C) 단일값 assert (회장 매핑 정확)
- evidence 결핍 → UNKNOWN + evidence_completeness assert
- CALLBACK_DELIVERY_GAP residual 규칙 1건(ANU key 발사+collector 부재+BEFORE_FIRE 원인 0 → DELIVERY_GAP / BEFORE_FIRE 원인 있으면 DELIVERY_GAP 아님) assert

---

## 8. 작업 환경 (foreign dirty 회피)

- **fresh worktree on origin/main**: `git worktree add <path> origin/main` (4187332c · clean · anu_v3 불필요 — core 는 decoupled 라 plain origin/main 에서 빌드 가능). 여기서 신규 파일 작성·검증·micro-commit.
- 메인 workspace foreign dirty 5건(memory/specs ×3 + replacement_pr_runner.py + 그 test) **건드리지 말 것**.
- finish-task GIT-GATE 가 메인 foreign dirty 로 차단되면 → **FOREIGN_DIRTY_BLOCKER** 분류·반복 루프 금지·micro-commit + reflection-ready 종료.

---

## 9. expected_files (전부 신규 — origin/main 부재)

1. `utils/callback_lifecycle_classifier.py` (core 순수 함수)
2. `utils/callback_lifecycle_states.py` (enum 상수 · 또는 #1 내 포함)
3. `tests/fixtures/callback_lifecycle/task-2625/*` (frozen evidence)
4. `tests/fixtures/callback_lifecycle/task-2628/*`
5. `tests/fixtures/callback_lifecycle/task-2628_plus_1/*`
6. `tests/regression/test_callback_lifecycle_classifier.py`
7. `memory/events/task-2629.result.json`
8. `memory/reports/task-2629.md`

---

## 10. 금지 (회장 verbatim)

- anu_v3 런타임 dependency 추가 금지
- callback 재발사 금지
- marker 삭제 금지
- foreign dirty 정리 금지
- push/PR/merge 금지 (회장 승인 전 금지)
- production enforcement 완료 판정 금지
- live workspace 상태에 의존하는 비결정 regression 금지
- schema/spec 만 만들고 완료 처리 금지

추가(doctrine): hook 우회 금지 · dual-purpose writer/daemon/long watcher 금지 · self-collector 결과 authoritative 승격 금지 · flat enum 회귀 금지

---

## 11. 완료 보고 (회장 verbatim · 8항목)

result.json + report 가 답변:
1. classifier core 위치
2. core/wiring 분리 구조
3. UNKNOWN / evidence_completeness 반영 방식
4. frozen fixture 구성
5. 3 fixture regression 결과
6. anu_v3 dependency 가 없는지 확인 (grep `anu_v3` 0 증명)
7. wiring 을 task-2627 integration bundle 로 넘길 항목 (handoff 목록)
8. push/PR/merge 필요 여부와 회장 결정 항목

---

## 12. 판정 기준 (회장 verbatim)

- schema/spec 만 만들고 완료 처리하지 말 것.
- classifier regression 3개가 통과해야 Phase 2 ready 로 본다.
- task-2627 reflection / task-2628+1 dependency closure 와 integration 순서가 정리되기 전에는 production enforcement 완료 판정 금지.

---

## 13. callback contract (필수 — self-key 위험 인지)

- normal callback 을 **ANU key(c119085addb0f8b7) 전용** 발사 시도. executor self-key(1e41a2324a3ccdd0) 면 fail-closed → NON_AUTHORITATIVE → ANU 독립 재검증.
- callback prompt **envelope-only · UTF-8 ≤3900 bytes**.
- result.json 에 callback contract 9 fields 기록(task-2627 §10 동형).
- envelope: task_id=task-2629 / result_path / report_path / sha256 / collector_role=ANU / owner_key=c119085addb0f8b7 / canonical_root=/home/jay/workspace / summary.
- ★ 본 task 산출물(classifier)을 self-key 미수신 분류에 dogfooding 하지 말 것(분류 대상과 도구 혼용 금지 — 본 task 의 callback 도 미발사 시 ANU fallback 수집).

---

## 14. doctrines (필수)

- D-SPEC-EXACTNESS · 단일소스 = system_callback_lifecycle_state_schema_260522.md
- evidence-only decoupled (anu_v3 import 0)
- frozen fixture (live 비의존 결정적 regression)
- 추정 금지 → UNKNOWN/INSUFFICIENT_EVIDENCE (환각 금지)
- CALLBACK_DELIVERY_GAP residual only
- no-dual-purpose / no-daemon (read-only classifier)
- fresh worktree clean base (foreign dirty 회피)
- BOT App token 부재 → 로컬 micro-commit + reflection-ready
- attempt-1 only · spec drift 시 HOLD + 회장 escalate

---

## allowed_resources

```yaml
allowed_resources:
  paths:
    - "utils/callback_lifecycle_classifier.py"
    - "utils/callback_lifecycle_states.py"
    - "tests/fixtures/callback_lifecycle/**"
    - "tests/regression/test_callback_lifecycle_classifier.py"
    - "memory/events/task-2629.result.json"
    - "memory/reports/task-2629.md"
    - "memory/tasks/task-2629.md"
  forbidden_paths:
    - "anu_v3/**"
    - "dispatch/**"
    - "utils/replacement_pr_runner.py"
    - "utils/merge_queue_executor.py"
    - "tests/regression/test_replacement_pr_runner_2510.py"
    - "tests/regression/test_anu_v3_dependency_isolation_2628.py"
    - "memory/specs/**"
    - "memory/tasks/task-2619.md"
    - "memory/events/task-2625.*"
    - "memory/events/task-2628.*"
    - "memory/events/task-2628+1.*"
    - "anu_v2/**"
    - ".github/**"
    - ".env*"
    - "*.pem"
    - "*.key"
  commands:
    - "pytest"
    - "python3 -m py_compile"
    - "ruff"
    - "git"
  merge_policy: "manual"
  ttl_hours: 24
```

> 주: memory/events/task-2625.* / task-2628.* / task-2628+1.* 를 forbidden 에 둔 것은 **수정 금지**. fixture 캡처를 위한 **read** 는 허용(원본 무변경 → frozen snapshot 은 tests/fixtures/ 에 신규 생성).
> anu_v3/** / dispatch/** forbidden = core 가 그쪽을 import/수정하지 않음을 강제(decoupled).

## 15. 산출물

- utils/callback_lifecycle_classifier.py (+ states 상수)
- tests/fixtures/callback_lifecycle/{task-2625,task-2628,task-2628_plus_1}/ frozen evidence
- tests/regression/test_callback_lifecycle_classifier.py (3 fixture + UNKNOWN + DELIVERY_GAP residual)
- memory/events/task-2629.result.json (8항목 + callback 9 fields)
- memory/reports/task-2629.md
- 로컬 micro-commit (push/PR/merge 0 · 회장 승인 대기)

끝