# task-2729+3 — FINISH_TASK_MERGE_POLICY_ENFORCEMENT (finalize harness fix)
## 회장 인가 (2026-06-05, A안 승인) — CHAIR_REQUIRED 충족
finish-task.sh 가 merge_policy=none 을 명시적으로 honor 하여, PROJECT_PATH 가 있어도 merge/pr-gate/Gemini trigger side-effect 없이 **finalize-only** 진행하게 한다. fresh origin/main(14ff8339) base. **ACTIVE 무관(harness). production ACTIVE 전환 금지.**

## AUDIT 결과 (read-only, ANU 규명 — 근거)
- **merge block = finish-task.sh line 481~585**: 주석 "2. 머지 (멱등성: .merge-done 있으면 스킵, **project_path 없으면 스킵**)". `if [ -n "$PROJECT_PATH" ]` 게이트 후, taskctl/hygiene-guard/pre-push/qc-check 통과 시 **`worktree_manager.py finish "$PROJECT_PATH" ... --action auto`**(line 580-581) 실행 = 실 merge + PR gate + owner_gemini trigger(전부 worktree_manager 내부). `.merge-done` 생성.
- **merge_policy 미참조**: finish-task.sh 가 merge_policy 를 전혀 읽지 않음(grep 0) → task md allowed_resources merge_policy=none 무효. = FINISH_TASK_STRICT_MODE_MERGE_POLICY_GAP.
- **PROJECT_PATH auto-보정(line 122-164)**: arg3 비어도 task-timers.json `worktree_path` / task-file `## worktree:` 에서 자동 설정 → caller 가 finalize-only 의도해도 merge 무성 개방. = FINISH_TASK_PROJECT_PATH_SIDE_EFFECT_RISK.
- **유지 대상(finalize-only 에서도 실행)**: QC, scope-guard, callback enforcement(4-source), G3 gate(963-1023, Lv.3+ 검증), `.done` 생성 — 이들은 merge block 밖 별도 경로.

## 목표 (surgical — 기본 동작 보존)
1. finish-task.sh 가 **merge_policy=none** 또는 **explicit finalize-only** 일 때 **merge block(481-585) 만 스킵**(worktree_manager finish/PR gate/Gemini trigger 미실행). 나머지(QC/scope/callback/G3/.done) 전부 유지.
2. merge_policy 출처: task md allowed_resources `merge_policy:` 값(이미 모든 task md 존재). **resolver helper** 로 파싱(아래 expected_files #3).
3. **--finalize-only 플래그(또는 FINALIZE_ONLY=1 env)** 도 동일하게 merge block 스킵 — ANU finalize-retry 가 declarative 의존 없이 안전 finalize 가능.
4. ★ **audit-first 로 둘 중 더 안전한 1차 방식 결정**(권장: merge_policy gate 를 1차, --finalize-only 를 보조). 기본(merge_policy 미지정/tiered/auto)은 **기존 동작 100% 보존**.
5. merge block 스킵 시 명확 로그 + `${TASK_ID}.finalize-only` 마커(merge 미실행 증거). `.merge-done` 은 생성하지 않음(merge 안 했으므로).

## expected_files (lock, ≤3)
1. `scripts/finish-task.sh` — merge block 진입에 merge_policy=none/finalize-only 게이트 추가(surgical, 최소 줄). 기존 PROJECT_PATH/멱등 로직 보존.
2. `tests/regression/test_finish_task_merge_policy_enforcement.py` (신규) — 아래 8 회귀.
3. (필요 시) `scripts/harness/v36/merge_policy_resolver.py` (신규 helper) — task md allowed_resources 에서 merge_policy 파싱(단일 책임, 순수 함수, mock 가능).
- ★ 3 초과·다른 harness 파일 수정 필요 시 CHAIR_REQUIRED 보고.

## 필수 regression (8 — 회장 verbatim)
1. PROJECT_PATH 있음 + merge_policy=none → **worktree_manager finish 미호출**(mock 호출수 0).
2. PROJECT_PATH 있음 + merge_policy=none → **PR gate / Gemini trigger 미호출**.
3. PROJECT_PATH 있음 + merge_policy=none → **QC/scope/callback enforcement/.done 경로 유지**(호출됨).
4. 기존 일반 code task(merge_policy=tiered/auto/미지정) → **기존 동작 유지**(merge block 정상 진입).
5. `--finalize-only`(또는 FINALIZE_ONLY=1) → merge side-effect 0(1과 동일).
6. ACTIVE=false 유지(harness — capability ACTIVE 변경 0).
7. ANU key raw 노출 0.
8. force/rebase/admin/merge 미실행(테스트 내 실제 git push/merge 0, 전부 mock/dry).

## 금지 (회장 verbatim)
실제 PR merge · PR #174 코드 변경 · 수동 .done · fake schedule_id/history · admin/force/rebase · ACTIVE=true · unrelated cleanup. expected_files 밖(3 초과) → CHAIR_REQUIRED.

## doctrine
same-PR Gemini 후 push 금지(위반 시 Option A) · bot /gemini 무효 · long-polling 금지 · finalize 14단계+8항목 명시 · PR open 시 CI_WATCH_HANDOFF · normal callback = ANU key c119085addb0f8b7. non-Critical Gemini/Codex 지적 = validity/scope/repetition 자동 수렴.

## PR 생성 전 재확인 → ANU
CI 11/11 · fresh Gemini HIGH/CRITICAL 0 · diff expected_files(≤3) 내 · forbidden 0(본 task 는 finish-task.sh 허용) · ANU key raw 0 · ACTIVE 변경 0. 통과 시 PR → owner_gemini → review-settle → MERGE_APPROVAL_CANDIDATE. merge 회장 승인 전 금지.

## 산출물
1. PR(fresh origin/main 14ff8339 base, ≤3 files). 2. `memory/reports/task-2729+3.md`(side-effect 경로 문서 + before/after + 8 회귀 증거). 3. `memory/events/task-2729+3.done`.
4. PASS 시 → ANU 가 P0-A(PR#174) finalize 재시도: PROJECT_PATH 있어도 merge_policy=none 으로 merge side-effect 0 → robust PASS → P0-A 완료 → P0-B 자동 진행.

## allowed_resources (본 task의 capability)

```yaml
allowed_resources:
  paths:
    - "scripts/finish-task.sh"
    - "tests/regression/test_finish_task_merge_policy_enforcement.py"
    - "scripts/harness/v36/merge_policy_resolver.py"
    - "memory/reports/task-2729+3.md"
    - "memory/events/task-2729+3.*"
  forbidden_paths:
    - "bot_settings.json"
    - ".env.keys"
    - ".github/**"
    - "utils/replacement_pr_runner.py"
    - "scripts/worktree_manager.py"
    - "scripts/ci_watch_handoff_runner.py"
    - "utils/pr_watcher_terminal_state_classifier.py"
    - "memory/events/*.cron-*"
  commands:
    - "pytest"
    - "python3 -m py_compile"
    - "bash -n"
  merge_policy: "tiered"
  ttl_hours: 48
```