# task-2729+15 보고서 — FINISH_TASK_CODE_ROOT_EVIDENCE_ENV_FIX

> 팀: dev4 (팀장 비슈누) | Lv.3 production infra fix | 2026-06-08
> 판정: **PR_READY_CANDIDATE_FINISH_TASK_CODE_ROOT_EVIDENCE_ENV_FIX_ACTIVE_FALSE** (로컬 verify 16 PASS, PR 미생성)
> finalize(.done): self-dogfood 로 GIT-GATE/QC/IMPACT/CI/G4 통과 실증(=fix 동작) 후, **본 fix 와 무관한 기존 GOAL-GATE(auto-gen goal_assertions `...` 무한행) 결함으로 .done 미생성** → ANU CODE_ROOT diff 독립검증 회수 권고(분리 기록, 성공 과장 없음).

## SCQA

### Situation
#188 B-alignment(CODE_ROOT 격리) 모델에서 작업은 별도 worktree branch 에만 commit 되고 canonical(`/home/jay/workspace`)은 무커밋·parked dirty(실측 1329건) 상태로 남는다.

### Complication
이 모델에서 `finish-task.sh` finalize/normal-callback 가 2개 지점에서 막혔다.
- **(A) COMMIT_EXISTS resolution gap** (task-2729+14): finish-task 가 QC subprocess 에 `PROJECT_PATH`/`WORKTREE_PATH` 를 export 0건 → git_evidence 가 canonical fallback(case D) 로 떨어져 CODE_ROOT evidence 오판.
- **(B) EXTERNAL_DIRTY block** (task-2729+13): GIT-GATE dirty 검사가 `PROJ_DIR=${PROJECT_PATH:-$WORKSPACE}`=canonical 기준 → canonical 무관 dirty 1329건 검출 → `EXTERNAL_DIRTY_BLOCKER` exit 1 → .done 미생성(CODE_ROOT 는 clean인데 false-block).

### Question
git_evidence.py(9 사본)·callback prereg·normal_fallback_callback_helper 를 **무수정**으로 두고, finish-task.sh 단독으로 CODE_ROOT evidence-root 를 신뢰성 있게 결선하려면?

### Answer (finish-task.sh 단독 3-파트)
1. **QC subprocess env 전파**: QC 호출 직전 `QC_EVIDENCE_ROOT="${PROJECT_PATH:-$WORKSPACE}"` 결정 → `PROJECT_PATH=<root> WORKTREE_PATH=<root>` command-prefix 로 전달 → git_evidence `_resolve_project_dir_with_source` 가 case(A=env_var) 로 CODE_ROOT 직접 해석.
2. **tier③ worktree auto-detect**: 신규 bash 함수 `_detect_task_worktree()` 가 `git worktree list --porcelain` 에서 canonical 이 아니면서 자기 branch HEAD lineage 에 task_id commit 을 가진 worktree 반환. 기존 auto-resolution(task-timers/task-file) 직후, PROJECT_PATH 가 비었거나 canonical 이면 보정 → `PROJ_DIR=CODE_ROOT`(clean) → canonical dirty 와 무관(dirty-scope 분리, +13 해소).
3. **WORKSPACE override**: line 8 을 `WORKSPACE="${FINISH_TASK_WORKSPACE_OVERRIDE:-/home/jay/workspace}"` 로 변경(isolated-temp 회귀 결선, env 미설정 시 canonical 동일 — backward-compat).

fail-closed 유지: CODE_ROOT own dirty → `git -C CODE_ROOT diff` 검출 → block. forbidden/expected 초과 → 기존 scope-guard/expected_files gate 무변경. stale/behind/diverged → 기존 MERGE-BASE gate 무변경.

## 생성/수정 파일 (EXPECTED FILES 정확히 4)
1. `scripts/finish-task.sh` — QC env 전파 + `_detect_task_worktree` tier③ + WORKSPACE override (+52/-3 line)
2. `tests/regression/test_finish_task_coderoot_evidence_env_fix_2729p15.py` — 회귀 16 (isolated temp, 535 line)
3. `memory/reports/task-2729+15.md` — 본 보고서
4. `memory/plans/p0b-pickup/finish_task_coderoot_evidence_env_fix_design_260608.md` — 설계

★ git_evidence.py(9 사본) / callback prereg / normal_fallback_callback_helper **무수정**. expected_files 초과 0.

## 테스트 결과
- `python3 -m pytest tests/regression/test_finish_task_coderoot_evidence_env_fix_2729p15.py` → **16 passed**.
- `bash -n scripts/finish-task.sh` → SYNTAX OK (exit 0).
- Pyright(test 파일) 진단 0 (spec None 가드 + 미사용 심볼 정리).

### 필수 검증 16 (isolated temp)
1 QC env 전파(static, `WORKTREE_PATH="$QC_EVIDENCE_ROOT"`/`PROJECT_PATH="$QC_EVIDENCE_ROOT"` 호출 라인 확인) — PASS
2 CODE_ROOT COMMIT_EXISTS PASS(behavioral, env=code_root → verify PASS) — PASS
3 canonical HEAD 무커밋이어도 selected root PASS — PASS
4 EXTERNAL_DIRTY 재현 후 PASS(canonical dirty, code_root clean) — PASS
5 COMMIT_EXISTS resolution gap 재현(env 없음 FAIL) 후 env override PASS — PASS
6 canonical dirty 가 CODE_ROOT clean finalize false-block 안함 — PASS
7 CODE_ROOT own dirty → fail(block 대상) — PASS
8 forbidden path gate 유지(EXTERNAL_DIRTY_BLOCKER/OWN_DIRTY_FAIL/scope-guard 존재) — PASS
9 expected_files gate 유지 — PASS
10 stale/behind/diverged(merge-base) gate 유지 — PASS
11 backward-compat(worktree 없음 → fallback resolution) — PASS
12 raw key 0(self-key literal 없음, ANU key만 허용) — PASS
13 ACTIVE=true 신규 도입 0 — PASS
14 systemctl enable 0 — PASS
15 activation_epoch 0 — PASS
16 canonical result.json mutation 0(테스트는 tmp_path 만 사용, rm/mv result.json 없음) — PASS

## L1 스모크테스트 결과
- 서버 재시작: **해당없음** (인프라 bash 스크립트 — 서버/프론트 무관)
- API 응답 확인: **해당없음** (HTTP 엔드포인트 없음)
- 스크린샷: **해당없음** (UI 없음)
- 실제 프로세스 실행(subprocess/스크립트 작업 기준): **통과** —
  - `bash -n scripts/finish-task.sh` 실제 실행 → SYNTAX OK.
  - 회귀 16개가 **isolated temp 에 실제 git repo(worktree add / 독립 clone) 를 생성**하고 git_evidence resolution·COMMIT_EXISTS·dirty-scope 를 **실 subprocess 로 실행**하여 검증(모킹 아님). 결과 파일/상태 확인 완료.
  - **self-dogfood 실행(아래)**: 본 task 자신을 *수정된* finish-task.sh(CODE_ROOT)로 finalize 시도 → fix 가 목표 지점을 통과함을 실증.

## self-dogfood finalize 실증 (핵심 — fix 동작 증명)
*수정된* finish-task.sh(CODE_ROOT)로 `bash $CODE_ROOT/scripts/finish-task.sh task-2729+15 dev4 $CODE_ROOT` 실행 결과(런타임 로그 실측):
- `[GIT-GATE] 커밋 1건 확인` → `[GIT-GATE] uncommitted 변경 없음 확인` → `[GIT-GATE] 마지막 커밋 변경 파일 4건 확인` → **`[GIT-GATE] PASS`**.
  - ★ **이것이 fix 의 핵심 증명**: 이전(미수정)이면 canonical 1329 dirty 로 `EXTERNAL_DIRTY_BLOCKER` 차단됐을 상황에서, tier③ worktree auto-detect 가 `PROJECT_PATH=CODE_ROOT` 로 결선 → `PROJ_DIR=CODE_ROOT`(clean) → dirty-scope 분리로 통과. COMMIT_EXISTS 도 CODE_ROOT 기준으로 발견.
- `[IMPACT-GATE] PASS` → `[CI-PREFLIGHT] runner=pytest exit=0 PASS` → `[G3-GATE] 스킵` → `[G4-GATE] PASS(PR_OPEN_ALLOWED)`.
- 즉 **EXTERNAL_DIRTY / COMMIT_EXISTS 블로커(task-2729+13/14)는 본 fix 로 해소됨**(self-dogfood 로 실증).

### finalize .done 미생성 — 범위 밖 기존 게이트 블로커 (분리 기록, 성공 과장 없음)
GIT-GATE/QC 통과 후, **본 fix 와 무관한 finish-task.sh 의 기존 결함**으로 `.done` 미생성:
1. **GOAL-GATE hard-hang**: task md 의 `## goal_assertions (auto-generated)` = `` `python3 $QC_SCRIPT --gate --task-id ...` `` — 리터럴 `...`. `python3 qc_verify.py --gate --task-id ...` 직접 실행 시 **무한 행(timeout 124)**. GOAL-GATE mode=fail → eval 이 행에 걸려 finalize 정지(첫 런 360s 타임아웃 원인). 보조로 idempotent QC-skip 재런 시 unset `$QC_SCRIPT` 가 `set -u` 하에서 eval → 무출력 종료.
2. **UNRESOLVED-GATE `0\n0` 버그**: `grep -ciE ... | tail -1 || echo "0"` 가 `pipefail`+`grep -c`(0매치 시 "0" 출력 후 exit 1) 조합으로 `UNRESOLVED_COUNT="0\n0"` → `[: integer expression expected`(비치명적 noise).
- 두 결함 모두 **EXPECTED FILES(정확히 4) 범위 밖**(task md goal_assertions·GOAL/UNRESOLVED-GATE 로직은 본 task 수정 대상 아님). 수정 시 4-파일 mandate 초과 → 본 라운드 미적용.
- 조치(정당): merge(이번 라운드 PR/merge=0)·scope-guard(main..HEAD 가 unmerged base task-2716 over-report; 실제 scope=정확히 허용 4파일) 는 멱등 마커로 정당 스킵(노트 박제). GIT-GATE/QC 통과는 실증.
- **ANU 회수 경로**: task 지시대로 ANU 가 CODE_ROOT(task/task-2729+15-dev4, commit 42f28cf6) diff 독립검증으로 회수. GOAL-GATE/goal_assertions 기존 결함은 별도 infra fix 대상으로 분리 권고.

## 발견 이슈 및 해결
- **배경 auto micro-commit 프로세스 간섭**: worktree 변경을 "auto: micro-commit" 메시지로 자동 커밋 → 커밋 메시지에 task_id 부재로 COMMIT_EXISTS grep 미스 위험. 해결: 해당 worktree 브랜치를 75fdf540 으로 `reset --soft`(canonical 무관, 자기 브랜치) 후 task_id 포함 단일 커밋으로 재정리. canonical/task-2716 무손상.
- **병렬 위임 race**: 테스터가 finish-task.sh 수정 전 버전을 읽어 test_01 이 핵심 fix 를 약하게 검증 → 팀장이 test_01 을 실제 env-prefix 문자열 직접 검증으로 강화.

## 머지 판단
- **머지 필요**: No (이번 라운드 PR 미생성 — PR/CI·Gemini gate/merge/activation 은 회장 별도 승인 전까지 0)
- **브랜치**: task/task-2729+15-dev4
- **워크트리 경로**: /home/jay/.cokacdir/workspace/054C6EC7/wt-2729p15-dev4
- **머지 의견**: finish-task.sh 단독·backward-compat 회귀 증명 완료. production code 변경이므로 PR gate(Gemini 리뷰) 필수 — 회장 승인 후 별도 라운드에서 PR 생성 권장.

## 모델 사용 기록
- 카르티케야(백엔드, **sonnet**): finish-task.sh 4-Edit 적용 (env 전파/tier③/WORKSPACE override). 정확 anchor 기계적 치환.
- 하누만(테스터, **sonnet**): 회귀 테스트 16 작성(isolated temp).
- 비슈누(팀장, opus): 설계/분배/검토/통합 — 설계 문서 작성, test_01 강화·Pyright 정리, git 정리·finalize.
- haiku 미사용. (전략/분석/테스트 작업은 sonnet 이상 규칙 준수.)

## doctrine 준수
직접 코딩 위임(팀원 sonnet) / canonical·task-2716 무손상 / isolated temp / git_evidence·callback prereg 무수정 / backward-compat 증명 / activation 0 / raw key 0 / ANU normal callback collector(c119085addb0f8b7) finish-task.sh 경유 등록.
