# FINISH_TASK_GIT_EVIDENCE_CODE_ROOT_COMMIT_EXISTS_BLOCKER — ANU read-only DIAGNOSIS + FIX spec

> ANU 직접 read-only 진단 (회장 승인 2026-06-08). DIAGNOSIS only — 코드 수정 0, dispatch 0, canonical write 0.
> 판정: **FINISH_TASK_GIT_EVIDENCE_BLOCKER_DIAGNOSIS_COMPLETE_FIX_DISPATCH_READY**

## 1. 증상 (관측)
- CODE_ROOT-격리 task(#188 B-alignment 모델: 작업이 `${HOME}/p0b-pickup-main` worktree의 `task/task-2729+NN-devM` branch에만 commit, canonical 무커밋)에서 finish-task 의 normal callback/finalize 가 막힘.
- task-2729+13: callback-cause = `FINISH_TASK_GIT_GATE_BLOCKED`, **.done 미생성**.
- task-2729+14: **.done 생성**(23:26)·callback 마커 0·envelope 0·cron 0 → done-watcher 가 stale .done escalate(23:57). .followup.txt 에 봇 자가진단 "git_evidence COMMIT_EXISTS FAIL — canonical fallback".

## 2. read-only 재현 (핵심 정정 포함)
- **canonical 과 CODE_ROOT 는 .git common dir 공유**(`/home/jay/workspace/.git`). ⇒ commit 은 shared object store 에 있고 `--all` 로 상호 가시.
- `git -C canonical log --all --grep=task-2729+14` = **1건**(보임). `git -C canonical log --grep`(HEAD lineage only) = **0건**(canonical HEAD=task-2716 lineage 에 미존재 — 별도 branch·미머지).
- **git_evidence verifier 실측**(`teams/shared/verifiers/git_evidence.py`): COMMIT_EXISTS 는 `git log --oneline --all --fixed-strings --grep=<task_id>`(line 321) + **worktree→main fallback**(line 119 `_resolve_project_dir_with_source`: PROJECT_PATH/WORKTREE_PATH → task-timers worktree_path/project_path → fallback workspace). ⇒ **verifier 는 --all 사용·shared .git 에서 commit 발견 가능**. 현 재현 = canonical fallback 에서도 1건 발견(PASS 가능).

## 3. 근본 원인 (정확)
**단일 "canonical COMMIT_EXISTS 구조적 불가" 가 아님 — 2개 분리 블로커 + resolution gap.**
- **(A) COMMIT_EXISTS resolution gap (task-2729+14)**: verifier 는 --all/fallback 가능하나, 런타임에 **PROJECT_PATH/WORKTREE_PATH 미설정 + task-timers 에 worktree_path 미기록** → search_dir 가 canonical fallback(source=D)로 해석. 봇 보고 시점 COMMIT_EXISTS=FAIL(retry_count=2 — 초기 시도가 commit 전/search_dir 미해결). 즉 **CODE_ROOT 가 git evidence root 로 신뢰성 있게 결선되지 않음**(env/task-timers 미전달). 봇의 "canonical 구조적 불가" 표현은 부정확(commit 은 --all 로 가시) — 정확히는 **resolver 가 CODE_ROOT 를 가리키도록 결선 안 됨**.
- **(B) EXTERNAL_DIRTY at GIT-GATE (task-2729+13)**: finish-task GIT-GATE dirty 검사가 `PROJ_DIR=${PROJECT_PATH:-$WORKSPACE}`=canonical 에서 무관 dirty 1321 파일 검출 → `EXTERNAL_DIRTY_BLOCKER`(finish-task.sh:708) block → .done 미생성. CODE_ROOT worktree 는 clean 인데 canonical dirty 로 막힘.
- **공통 root**: finish-task/QC 의 git evidence·dirty root 가 default canonical(`${PROJECT_PATH:-$WORKSPACE}`) — #188 B-alignment(CODE=별도 worktree, canonical 무커밋·parked dirty) 모델과 구조적 불일치. CODE_ROOT 결선이 신뢰성 있게 전달되지 않으면 canonical fallback 으로 오판.

## 4. EXTERNAL_DIRTY_BLOCKER vs COMMIT_EXISTS_BLOCKER 분리
| | task-2729+13 | task-2729+14 |
|---|---|---|
| 단계 | GIT-GATE dirty 검사 | QC git_evidence COMMIT_EXISTS |
| 원인 | canonical 1321 무관 dirty | CODE_ROOT resolution 미결선 → canonical fallback |
| .done | 미생성 | 생성(이후 stale escalate) |
| 분류 | EXTERNAL_DIRTY_BLOCKER | COMMIT_EXISTS_RESOLUTION_GAP |
| 코드 가시성 | n/a | commit 은 --all 로 가시(구조적 불가 아님) |

## 5. 보정 옵션 (finish-task/QC 가 어떤 root 기준으로 evidence 를 봐야 하는가)
- **옵션 1 (결선·권장 1순위)**: CODE_ROOT-격리 task 시 finish-task/QC 에 **PROJECT_PATH 또는 WORKTREE_PATH = CODE_ROOT 명시 전달**(dispatch/finish 래퍼 결선). `_resolve_project_dir_with_source` case (A) → CODE_ROOT 해결 → `git -C CODE_ROOT log --grep` 직접 발견. GIT-GATE dirty 도 PROJ_DIR=CODE_ROOT(clean) → EXTERNAL_DIRTY 회피.
- **옵션 2 (task-timers 결선)**: task-timers.json 에 task 의 worktree_path/project_path 기록 보장(case B). dispatch 시 자동 기입.
- **옵션 3 (verifier shared-.git 강건화)**: COMMIT_EXISTS 가 canonical fallback 이어도 `--all`(이미 사용) 로 shared object store 검색 보장 + **commit-before-QC 타이밍 가드**(QC 전 commit 존재 재확인). resolution 실패 시 fail 대신 --all retry.
- **옵션 4 (dirty scope, +13)**: GIT-GATE/EXTERNAL_DIRTY 가 canonical 무관 dirty 를 expected_files/CODE_ROOT 기준으로 scope — clean CODE_ROOT worktree-격리 작업은 canonical parked dirty 와 무관하게 finalize 허용(EXTERNAL_DIRTY 분류는 이미 존재하나 block→allow 전환 필요).
- **옵션 5 (callback path 정합)**: callback prereg/enforce(envelope·cron)가 git_evidence verifier 결과와 정합(동일 root resolution) — verifier PASS 면 envelope/cron 등록까지 이어지도록.
- ★ ANU 권고: **옵션 1 + 옵션 4** 조합(결선 + dirty scope)이 최소·근본. 옵션 3 은 안전망.

## 6. FIX task 설계 (별도 봇 dispatch 대상 — 회장 승인 전까지 준비만)
- **title**: `FINISH_TASK_GIT_EVIDENCE_CODE_ROOT_RESOLUTION_FIX`
- **expected_files 후보 (≤5)**:
  1. `scripts/finish-task.sh` (PROJ_DIR/CODE_ROOT 결선 + EXTERNAL_DIRTY→CODE_ROOT-격리 finalize 허용 분기)
  2. `teams/shared/verifiers/git_evidence.py` (resolution 강건화 + commit-before-QC 가드, --all 유지)
  3. `tests/regression/test_finish_task_git_evidence_coderoot_resolution_fix.py` (CODE_ROOT 결선 시 COMMIT_EXISTS PASS / canonical dirty 무관 finalize / callback 등록 정합, isolated temp)
  4. `memory/reports/<fixtask>.md`
  5. `memory/plans/p0b-pickup/<fixtask>_design.md`
- **forbidden_paths**: canonical reset/clean/stash·task-2716 수정·canonical result.json 이동삭제·G4 reset·수동 .done·systemctl enable·activation·dispatch.py 핵심 로직 외 변경. ★ production code 수정이므로 PR gate 필수.
- **regression 후보**: CODE_ROOT 격리 task end-to-end finalize(temp) → .done + callback envelope/cron 등록 PASS / canonical 1321-equiv dirty 무관 / commit-before-QC.
- ★ **재귀 회피**: FIX task 자체도 CODE_ROOT 격리로 돌면 동일 블로커 → FIX 는 옵션 1 결선 적용 후 dogfood, 또는 ANU 가 finalize 보조.

## 7. 안전 (DIAGNOSIS 단계)
- read-only only: 코드 수정 0, canonical write 0, real spawn 0, systemctl 0, activation 0, dispatch 0, PR 0, 수동 .done 0, task-2716 무수정, G4 무변경.
- canonical 무손상: HEAD task-2716, result.json 140, flag/epoch ABSENT.

## 판정
**FINISH_TASK_GIT_EVIDENCE_BLOCKER_DIAGNOSIS_COMPLETE_FIX_DISPATCH_READY** — 원인 read-only 증명(2 분리 블로커 + resolution gap, shared-.git 정정), 보정 옵션 5 제시, FIX task 설계(expected_files/forbidden/regression) 가능. **FIX 코드 수정·봇 dispatch 는 회장 별도 승인 대기.**
