# task-2636 — callback collector canonical-root resolver (Core hardening · read-only · merge 실행 0)

- Level: Lv.3 (거버넌스 자동화 · collector canonical-root hardening)
- 담당: dev6 페룬
- base: origin/main **1e24d79e** (PR #138 callback registration enforcement 머지 반영분)
- 단일소스 spec: `memory/specs/system_callback_collector_canonical_root_spec_260523.md`
  - sha256: `6f0b04810cc458ea4cb12f3c1c9c511d14b1439917b7ef4f1ef91982e32d92c1`
  - 정독 전 sha256sum 검증 필수, 불일치 시 중단·ANU 보고
- 사고 배경: task-2635+1 처리 중 ANU collector autoset cwd false-negative (CALLBACK_COLLECTOR_CONTEXT_MISMATCH). 산출물 정상 존재 (/home/jay/workspace) 하나 cwd /home/jay/.cokacdir/workspace/autoset 기준 탐색이라 미발견.
- 승인 범위: **canonical-root hardening 까지만**. real auto-merge executor 활성화 미승인(회장 명시).

## 목표
ANU collector 가 어떤 cwd 에서 spawn 되더라도 `canonical_root=/home/jay/workspace` 기준으로 result/report 를 resolve. autoset/fresh workspace cwd false-negative 차단. CALLBACK_COLLECTOR_CONTEXT_MISMATCH 기록. sendfile-only ≠ normal callback trigger 분리.

## 필수 구현 (spec §3, §5, §6, §7 정본)

### 신규 helper 2
1. `utils/canonical_root_resolver.py`
   - `resolve_canonical_root(envelope, default="/home/jay/workspace") -> str`
   - `resolve_path(envelope, path_field, canonical_root=None) -> str`
   - `detect_context_mismatch(envelope, current_cwd=None) -> {mismatch, cwd, canonical_root, recorded_event_id?}`
   - `find_artifact(envelope, path_field) -> {found, resolved_path, lookup_attempts, classification}`
2. `utils/anu_collector_action_trigger.py` (또는 `dispatch/collector_hooks.py`)
   - `is_callback_action_trigger(envelope) -> bool`
   - `enqueue_collector_action(envelope) -> action_record`

### 최소 결선 수정
- `utils/callback_envelope_schema.py` — `canonical_root` optional 필드 추가 + validator (absolute path 단언)
- `dispatch/finalize_hooks.py` — envelope build 시 `canonical_root=CANONICAL_ROOT_DEFAULT` 기본값 채움 (옵션)

### fixture 신규 (8 시나리오)
`tests/fixtures/callback_collector_canonical_root/<scenario>/{evidence.json, expected.json, PROVENANCE.md}` (8×3=24)
- `canonical_root_explicit` — envelope.canonical_root + relative result_path → canonical resolve
- `canonical_root_missing_default` — None → default /home/jay/workspace
- `canonical_root_wrong_absolute` — wrong path → mismatch log + envelope honors explicit
- `relative_paths_resolve_correctly` — relative paths → absolute resolved
- `absolute_paths_passthrough` — absolute paths → passthrough (canonical_root 무시)
- `cwd_in_autoset_canonical_in_workspace` — cwd autoset vs canonical workspace → MISMATCH event 기록 + canonical 사용
- `sendfile_only_not_trigger` — delivery=sendfile_only → is_callback_action_trigger=False
- `normal_callback_is_trigger` — delivery=anu_cron_callback + REGISTERED + DELIVERED → is_callback_action_trigger=True

### 신규 regression 4
- `tests/regression/test_canonical_root_resolver.py` — resolve_canonical_root/resolve_path 8 fixture parametrized
- `tests/regression/test_collector_context_mismatch.py` — cwd != canonical_root mismatch event 기록 + cwd == canonical 시 미기록
- `tests/regression/test_collector_action_trigger.py` — normal=True · sendfile=False · NOT_REGISTERED=False · queue push 단언
- `tests/regression/test_autoset_cwd_canonical_lookup.py` — chdir 후 canonical 산출물 발견 단언 (monkeypatch os.getcwd · subprocess 0)

### CALLBACK_COLLECTOR_CONTEXT_MISMATCH 기록 위치
- `memory/events/callback_collector_context_mismatch.jsonl` (append-only)
- schema `callback_collector_context_mismatch.v1`
- decision="PROCEED_WITH_CANONICAL_ROOT" (cwd 결정 권한 0)

## 안전 불변식 (spec §10)
- ANU key `c119085addb0f8b7` 하드코딩 단일 출처 유지 (변경 0)
- envelope UTF-8 ≤3900 bytes 유지
- live cokacdir/subprocess 실호출 0 (monkeypatch/dry-run)
- merge/push/PR/cron/admin override 호출 0
- replacement_pr_runner / finish-task.sh / merge_ready_classifier / merge_ready_dryrun_executor **무수정**
- expected_files 외부 수정 0
- 5축 status schema **변경 0** (canonical_root 는 6번째 직교 필드)
- cwd-based primary lookup 코드 경로 0 (정적 가드)

## 필수 regression
- 신규 4 regression 전부 PASS
- 기존 callback consistency 40 + task-2635 helper 68 + baseline shadow 156 + dryrun 58 + classifier 50 = 372/372 유지
- full `tests/regression` new fail 0 (3 pre-existing test_stash_origin_audit_compat 무관)
- live workspace/GitHub/git 의존 0

## 자동수렴 (spec §11)
- Gemini medium/style/quality + expected_files 내부 + Critical7 0 + credential expansion 0 → 자동수렴
- expected_files 내부 non-critical HIGH 자동수렴, 동일 함수 HIGH 반복 시 LOOP_BOUNDARY → 회장 보고
- 회장 보고 트리거: Critical7 / credential expansion / expected_files 밖 / admin override / replacement_pr fail / smoke fail

## 금지 (spec §12 · 회장 verbatim)
- real auto-merge executor 구현 금지
- auto-merge 활성화 금지
- NL intake 코드 구현 금지
- foreign dirty 정리 금지
- replacement_pr_runner.py 수정 금지
- finish-task.sh 수정 금지
- production service task 와 혼합 금지
- PR #138 산출물과 섞기 금지 (별개 hardening · 5축 변경 0)

## expected_files (task-2636 범위 · spec §9)
- `utils/canonical_root_resolver.py` (신규)
- `utils/anu_collector_action_trigger.py` (신규 · 또는 dispatch/collector_hooks.py)
- `utils/callback_envelope_schema.py` (canonical_root 필드 추가)
- `dispatch/finalize_hooks.py` (envelope canonical_root 기본값 1줄 · 옵션)
- `tests/fixtures/callback_collector_canonical_root/<8 시나리오>/{evidence.json,expected.json,PROVENANCE.md}` (24 files)
- (선택) `tests/fixtures/callback_collector_canonical_root/INDEX.md`
- `tests/regression/test_canonical_root_resolver.py`
- `tests/regression/test_collector_context_mismatch.py`
- `tests/regression/test_collector_action_trigger.py`
- `tests/regression/test_autoset_cwd_canonical_lookup.py`

총 ~32 files. 프로덕션 영향: schema 1 optional 필드 + finalize_hooks 1줄(옵션). 5축 변경 0.

## finalize 프로토콜 (★ BOT App token 부재 — 로컬 한정 · spec §13)
1. base = 최신 origin/main 1e24d79e clean worktree
2. 신규 helper 2 + fixture 24 + regression 4 + schema/hook 최소 수정 전부 PASS + full new-fail 0 + 기존 372 유지
3. **로컬 commit만** (push/PR/merge 금지). finish-task.sh project_path 없이 로컬 종결
4. ANU normal completion callback — **신규 canonical_root resolver 자기검증 사용**: envelope `canonical_root=/home/jay/workspace` 명시 + collector 가 resolve 정확
5. envelope 5축 정합 유지 (REGISTERED + DELIVERED + UNCONFIRMED + schedule_id non-null + canonical_root 명시)
6. callback envelope UTF-8 ≤3900 bytes
7. executor 시작/종료 ts·로컬 commit SHA 명기

## frozen anchor (D-SPEC-EXACTNESS · spec §14)
- ANCHOR-1: "envelope.canonical_root 우선 · default /home/jay/workspace · cwd-based primary lookup 코드 경로 0"
- ANCHOR-2: "cwd != canonical_root 시 CALLBACK_COLLECTOR_CONTEXT_MISMATCH 기록 + canonical_root 사용"
- ANCHOR-3: "8 시나리오 fixture · 4 regression · MISMATCH event JSONL"
- ANCHOR-4: "5축 status schema 직교 유지 — canonical_root 는 6번째 별개 필드 · 5축 변경 0"
- ANCHOR-5: "sendfile-only ≠ normal callback trigger 분리 (is_callback_action_trigger 단언)"
- ANCHOR-6: "replacement_pr_runner / finish-task.sh / merge_ready_classifier / merge_ready_dryrun_executor 무수정 · real auto-merge executor 미승인"
