# task-2729+2 (P1) — NORMAL_CALLBACK_REGISTRATION reconciliation 보고서

- 팀: dev2-team (팀장 오딘)
- 작업 레벨: normal (게이트 지시 Lv.2)
- base: origin/main `d925b873`
- 브랜치: `task/task-2729+2-dev2`
- PR: **#175** (https://github.com/Jeon-Jonghyuk/dev_workspace/pull/175) — OPEN, merge_policy=none(회장 승인 대기)

---

## S (Situation)
ANU normal callback 4-source validator(`utils/normal_callback_registration_validator.py`)는
finish-task.sh 등에서 envelope 작성만으로 .done 이 생성되던 우회를 차단하는 단일 진입점이다.
4-source AND: schedule_id + schedule_history status=ok + owner_key(==ANU) + inbound/receipt.

## C (Complication)
실제 등록이 성공해도 4-source 가 FAIL 했다(task-2729+1 재현: "schedule_id missing/empty + owner_key missing").
**근본 원인(audit)**: production 의 normal callback envelope 는 registrar
(`utils/anu_callback_registrar` + `dispatch/finalize_hooks.finalize_with_callback_registration`)가
실제 `cokacdir --cron` 등록 후 얻은 **real schedule_id 를 `cron_schedule_id` 필드**로,
**ANU key 를 `anu_key` 필드**로 기록한다(`merge_registrar_result_into_envelope`).
그러나 validator 는 canonical `schedule_id`/`owner_key` 필드명으로만 읽어
**필드명 불일치**로 Source 1(schedule_id)·Source 3(owner_key)이 FAIL 했다.
→ registration marker ↔ validator input envelope 의 schema/field alias 불일치.

## Q (Question)
fake schedule_id/owner_key 주입 없이, 실제 등록된 real sid/key 를 어떻게 validator 4-source 와 정합시키는가?

## A (Answer)
validator **input mapping 정합**(검증 약화 0): canonical 필드가 부재/공백일 때만
registrar alias 를 회수한다.
- Source 1: `schedule_id` 부재 시 → `cron_schedule_id` alias 회수
- Source 3: `owner_key` 부재 시 → `anu_key` alias 회수

canonical 우선이므로 present-but-wrong 값은 절대 alias 로 fallthrough 하지 않으며,
placeholder/blocked schedule_type/path-traversal/self-key→NON_AUTHORITATIVE/!=ANU→FAIL/
schedule_history status=ok 검사는 회수된 값에 **그대로** 적용된다 → **검증 기준 약화 0**.
fake 생성 0 — 실제 registrar 가 기록한 real `cron_schedule_id`/`anu_key` 를 정합 전파(reconciliation)할 뿐이다.

---

## 변경 파일 (실제 2 files / expected ≤4)
1. `utils/normal_callback_registration_validator.py` (+11)
   - `_check_schedule_id`: canonical `schedule_id` 부재 시 `cron_schedule_id` alias (line ~130)
   - `_check_owner_key`: canonical `owner_key` 부재 시 `anu_key` alias (line ~231)
2. `tests/regression/test_normal_callback_registration_reconciliation_2729p1.py` (신규, 5건)

### audit 후 최소 locus 확정 (expected_files 중 2개 제외 — 사유 명시)
- `scripts/harness/v36/callback_preregistration.py` (expected #1, "주 locus 후보"): **origin/main d925b873 에 부재**.
  (현재 main 은 finish-task.sh 가 이 파일을 호출하지 않으며, 파일은 다른 lineage(task-2708 계열, 미머지)에 존재.)
  → PR 에 포함 시 313줄 **orphan-add** = diff hygiene 위반·범위 초과·다른 lineage 오염.
  reconciliation 은 validator alias 로 **완결**되므로(registrar 가 이미 real `cron_schedule_id`/`anu_key` 를 envelope 에 기록) 제외.
  (write-back 로직은 해당 파일이 main 에 머지된 이후 별도 phase 로 추가 가능 — 본 task 범위 밖.)
- `dispatch/normal_fallback_callback_helper.py` (expected #2): launch_callback 은 Layer A NO-CRON(zero subprocess)
  설계로 real schedule_id 를 반환하지 않는다(실 등록은 registrar 담당). 수정 시 기존 callback/fallback behavior
  무손상 원칙 위배 → **무수정**(byte-0 보존).
- 결론: 검증 약화 없이 정합을 달성하는 **최소 locus = validator alias + 회귀 테스트 2 files**.

---

## 회장 7 완료 조건 대비
1. envelope 에 schedule_id/owner_key 정합: ✅ validator 가 registrar 의 `cron_schedule_id`/`anu_key` 를 schedule_id/owner_key 로 정합 회수(write-back 불필요 — registrar 가 source).
2. schedule_history status=ok ↔ envelope schedule_id 매칭: ✅ L1 dogfood 실증(real sid E74F8E6E, status=ok jsonl 매칭).
3. inbound receipt 검증: ✅ Source 4 그대로 유지.
4. fake/self collector 차단 유지: ✅ self-key→NON_AUTHORITATIVE 회귀 통과(test 3), canonical 우선으로 alias 우회 불가(test 4).
5. 기존 callback/fallback 무손상: ✅ canonical dogfood 통과(test 5), helper 무수정.
6. ANU key raw 노출 0: ✅ 상수 `ANU_KEY` 참조만, 신규 raw 하드코딩 0.
7. ACTIVE=false 유지: ✅ 코드 alias 추가만, 활성 플래그 변경 0.

---

## L1 스모크테스트 결과 (필수 기록)
- **서버 재시작**: 해당없음 (validator 는 순수함수 라이브러리 — 서버 미관여).
- **API 응답 확인**: 해당없음. 대신 **validator 실동작 + L1 dogfood** 수행.
- **L1 dogfood (실 등록 envelope → validator PASS 1회 실증, fake sid 금지)**:
  - 실존 schedule_history sid `E74F8E6E`(chat 6937032012, `"status":"ok"` jsonl, **fake 아님**) 사용.
  - registrar 스타일 envelope(`cron_schedule_id=E74F8E6E`, `anu_key=ANU_KEY`, canonical 필드 없음) 구성.
  - real `schedule_history_dir=/home/jay/.cokacdir/schedule_history` + inbound receipt.
  - **결과: verdict=PASS**, sources={schedule_id:PASS(alias 회수 E74F8E6E), schedule_history:PASS(jsonl status=ok), owner_key:PASS(alias 회수 ANU), inbound_receipt:PASS}.
  - → 실제 4-source chain 이 alias 정합으로 **real sid 기반 PASS** 실증.
- **CLI 검증**: `python3 -m utils.normal_callback_registration_validator` 동일 envelope 로 Source 1/2/3 PASS 확인(inbound 만 기본 dir 차이로 별도 검증).

## 테스트 결과
- 신규 회귀 5건 PASS (py_compile PASS):
  1. `test_validator_pass_via_registrar_aliases` — alias 만으로 4-source PASS
  2. `test_sid_history_match_and_mismatch` — sid↔schedule_history 매칭/부재
  3. `test_self_key_non_authoritative_preserved` — alias 경로 self-key 차단 유지
  4. `test_canonical_precedence_no_weakening` — canonical 우선(검증 약화 0)
  5. `test_existing_canonical_dogfood_pass` — 기존 canonical 동작 무손상
- 기존 회귀 무손상: validator 관련 51/51 PASS. 전체 96 passed / 4 pre-existing FAIL
  (`dispatch.finalize_hooks` 모듈 해석 — `tests/dispatch/__init__.py` shadowing, **base d925b873 에서도 동일**, 본 변경 무관 — stash 비교로 확인).

## Capability Delta (callback registration reconciliation)
- **IMPLEMENTED**: validator alias 정합 (Source 1/3) ✅
- **VERIFIED**: 신규 5건 + L1 dogfood real-sid 4-source PASS ✅
- **WIRED**: validator 는 finish-task.sh 의 NORMAL-CALLBACK-ENFORCE 게이트가 호출하는 단일 진입점 — 결선 그대로 ✅
- **ACTIVE**: false 유지 (활성 플래그 변경 0) ✅

---

## 발견 이슈 및 해결 / 머지 판단
### 발견 이슈
1. **callback_preregistration.py 가 base 부재**: 팀원이 초기에 다른 워크스페이스(task-2716 브랜치)에서 복사 → orphan-add 발생. → audit 후 **제외**, 브랜치 정리(soft reset + 복원)로 diff 를 2 files 로 정합. ✅
2. **EXTERNAL_DIRTY_BLOCKER**: 공유 main workspace(`/home/jay/workspace`)가 `task/task-2716-pr-diff-hygiene-guard-dev4` 브랜치에 체크아웃 + **1247개 uncommitted 변경** 상태(다른 팀 dev4 영역, 미접촉 의무).
   - start_task_guard 검증 #1~#6,#8,#9(본 worktree 관련) **전부 PASS**, 오직 #7(main workspace==main 요구)만 외부 조건으로 FAIL.
   - 커밋/푸시는 훅 내장 **공식 감사 바이패스**(`TASKCTL_BYPASS` + reason, evidence JSON 기록)로 진행. 다른 팀 워크스페이스/브랜치는 **미접촉**.
   - 이 blocker 는 **P0-A(PR#174)도 동일하게 gating** 중(같은 외부 조건).

### 머지 판단
- **머지 필요**: Yes (단, **회장 승인 후**)
- **브랜치**: `task/task-2729+2-dev2`
- **워크트리 경로**: `/home/jay/workspace/.worktrees/task-2729+2-dev2`
- **머지 의견**: 2 in-scope files, 검증 약화 0, 신규/기존 회귀 PASS, L1 real-sid PASS. forbidden 0. **merge_policy=none → 회장 승인 전 머지 금지**. Gemini fresh HIGH/CRITICAL 0 확인 후 회장 승인 대기(P0-A 와 동일 posture).

## 모델 사용 기록
- 토르(백엔드, **sonnet**): validator alias + (초기) callback_preregistration — orphan 파일은 팀장이 audit 후 제외.
- 헤임달(테스터, **sonnet**): 회귀 테스트 6→5건(write-back 의존 케이스는 범위 외로 팀장이 정리).
- 팀장(오딘, opus): audit·설계·범위 확정·브랜치 정리·L1 dogfood·PR. (haiku 미사용 — 시스템 안전 크리티컬 작업)

## Gemini PR 리뷰 (G3)
- PR #175 fresh Gemini 리뷰: **HIGH/CRITICAL 0건**. MEDIUM 4건(테스트 섹션 주석 번호 ↔ docstring 번호 불일치, **cosmetic**).
- **no-push-after-gemini doctrine** 준수 → MEDIUM 은 same-PR push 금지로 **deferred**(차후 phase 정리 가능). 기능/보안 영향 0.
- 판정: 미수정 HIGH 0건 → **MERGE_APPROVAL_CANDIDATE** (회장 승인 대기, merge_policy=none).

## ANU normal callback 계약 이행 (SELF_COLLECTOR 금지 준수)
- 독립 ANU key(`c119085addb0f8b7`, **self-key 아님**)로 normal callback collector cron 등록.
- **real schedule_id = `155C413D`** (cokacdir `--once`, status=ok). self-key 미사용 → SELF_COLLECTOR_FORBIDDEN 회피.
- ANU collector agent **별도 spawn 확인**(`/home/jay/.cokacdir/workspace/155C413D` 생성 = 발사 증거).
- validator envelope `memory/events/anu_callback/task-2729+2-normal-completion.json` 에 real `cron_schedule_id=155C413D` + `anu_key` 기록 → 본 fix(alias)로 정합.
- schedule_history `155C413D.log` status=ok 는 collector agent 완료 후 비동기 기록(라이브 finalize chain). 본 fix 의 정합성은 L1 dogfood(real sid E74F8E6E, 4-source PASS)로 이미 실증됨.
- NOT_REGISTERED/SENDFILE_ONLY/SELF_COLLECTOR 위반 0.

## 완료 경로(finish-task.sh) 상태
- `scripts/finish-task.sh` 는 WORKSPACE=`/home/jay/workspace`(EXTERNAL_DIRTY_BLOCKER: task-2716-dev4 + 1247 dirty)에서 동작 → 안전 실행 불가.
- merge 단계는 task-timers worktree_path=None 이라 스킵되나, 오염된 main workspace 에서의 git 게이트가 불안정.
- **P0-A(PR#174)와 동일 posture**: .done 자동생성은 외부 blocker + 회장 머지 게이트로 보류. 수동 .done 생성 금지 doctrine 준수(생성하지 않음).
- 회장/ANU 조치 필요: (a) PR #175 머지 승인, (b) 공유 main workspace 의 task-2716 오염 해소 후 finalize 재시도.

## 산출물
1. PR #175 (origin/main d925b873 base, 2 files) — MERGE_APPROVAL_CANDIDATE.
2. 본 보고서 (`memory/reports/task-2729+2.md`).
3. ANU collector cron 155C413D + envelope (`anu_callback/task-2729+2-normal-completion.json`).
4. 패치 파일 전달(sendfile).
