# task-2636 — callback collector canonical-root resolver (Core hardening) · 보고서

- 담당: dev6 페룬 (claude-opus-4-7[1m])
- Level: Lv.3
- base: origin/main `1e24d79e` (PR #138 merged)
- 로컬 commit SHA: `730a0eb2c61906a52a8a2d6fa46aed09e6d4468d`
- branch: `task/task-2636-dev6` (worktree `/home/jay/.cokacdir/workspace/2DEA5DAB/wt-2636`)
- pushed/PR/merge: **0 (로컬 commit만)**
- executor 시작: 2026-05-23 10:43:41 KST
- executor 종료: 2026-05-23 10:50:30 KST
- spec: `memory/specs/system_callback_collector_canonical_root_spec_260523.md`
  - sha256: `6f0b04810cc458ea4cb12f3c1c9c511d14b1439917b7ef4f1ef91982e32d92c1` ✓

## 산출물 (~33 files / +1443)

### 신규 helper (2)
- `utils/canonical_root_resolver.py` — `resolve_canonical_root` / `resolve_path` / `detect_context_mismatch` / `find_artifact`
- `utils/anu_collector_action_trigger.py` — `is_callback_action_trigger` / `enqueue_collector_action`

### 최소 결선 수정 (2)
- `utils/callback_envelope_schema.py` — `canonical_root` optional 필드 + absolute path validator (5축 변경 0)
- `dispatch/finalize_hooks.py` — `canonical_root` 기본값 1줄 옵션

### fixture (24 + INDEX.md)
8 시나리오 × {evidence.json, expected.json, PROVENANCE.md}:
- canonical_root_explicit / canonical_root_missing_default / canonical_root_wrong_absolute
- relative_paths_resolve_correctly / absolute_paths_passthrough
- cwd_in_autoset_canonical_in_workspace
- sendfile_only_not_trigger / normal_callback_is_trigger

### regression (4 신규)
- `test_canonical_root_resolver.py` — 23 tests
- `test_collector_context_mismatch.py` — 15 tests
- `test_collector_action_trigger.py` — 21 tests
- `test_autoset_cwd_canonical_lookup.py` — 4 tests

→ **신규 63/63 PASS**, callback baseline 156/156 PASS, full `tests/regression` **1328 passed** (3 pre-existing `test_stash_origin_audit_compat` 무관, spec 명시).

## 자기검증 (canonical_root resolver 자기 사용)

```
validate_envelope ok=True errors=[]
envelope_byte_count=1076 (limit=3900)
resolve_canonical_root → /home/jay/workspace
resolve_path(result_path) → /home/jay/workspace/memory/reports/task-2636-result.json
resolve_path(report_path) → /home/jay/workspace/memory/reports/task-2636.md
is_callback_action_trigger → True
detect_context_mismatch(cwd=/home/jay/.cokacdir/workspace/2DEA5DAB/wt-2636) → mismatch=True decision=PROCEED_WITH_CANONICAL_ROOT
```

cwd ≠ canonical_root 라도 resolver는 canonical 사용. cwd 결정 권한 0. ✓

## 5축 schema 정합 (변경 0 + canonical_root 6번째 직교)

| axis | value |
|---|---|
| registration_intent | True |
| registration_attempted | True |
| registration_result_status | REGISTERED |
| callback_delivery_status | DELIVERED |
| collector_receipt_status | UNCONFIRMED |
| **canonical_root (6th)** | /home/jay/workspace |
| cron_schedule_id (non-null) | TASK2636SELFVRF |

## frozen anchor 정합

- ANCHOR-1: envelope.canonical_root 우선 · default /home/jay/workspace · cwd-based primary lookup 코드 경로 0 ✓
- ANCHOR-2: cwd != canonical 시 MISMATCH 기록 + canonical 사용 (cwd 결정 권한 0) ✓
- ANCHOR-3: 8 시나리오 fixture · 4 regression · MISMATCH event JSONL ✓
- ANCHOR-4: 5축 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 **무수정** ✓

## 안전 불변식

- ANU key `c119085addb0f8b7` 단일 출처 유지
- envelope UTF-8 ≤ 3900 bytes (실측 1076 bytes)
- live cokacdir/subprocess 실호출 0 (regression monkeypatch)
- merge/push/PR/cron/admin override 호출 0
- replacement_pr_runner / finish-task.sh / merge_ready_classifier / merge_ready_dryrun_executor 무수정
- expected_files 외부 수정 0
- 5축 schema 변경 0
- cwd-based primary lookup 코드 경로 0 (정적 가드 regression 단언 + spec ANCHOR-1)

## ANU callback envelope

`canonical_root=/home/jay/workspace` 명시 → collector가 envelope 기준으로 result/report 정확 resolve.
ANU key `c119085addb0f8b7` 단일 출처. 5축 정합 (REGISTERED + DELIVERED + UNCONFIRMED + schedule_id non-null).
