# task-2550+1 context-notes

## PR #100 (task-2550) audit 본질

PR #100 (head 4067d8c43833) 은 anu_v2.worktree_cleanup + post_merge_smoke_runner 통합 v0
를 8 파일 (코드 4 + 3문서 + 보고서) 로 구현했으나, Gemini fresh review 에서 6 unresolved
findings (HIGH 1 + medium 4 unique + duplicate 1) 가 잔존했다. 모두 code-changing 결함:

### HIGH (1건) — security/correctness 본질

**worktree_cleanup.py:147** — `task_id in str(headRefName)` substring 매칭
- task_id="task-25" 가 headRefName="task/task-2550-dev5" 에 substring 매칭 (True) — BUG
- 결과: task-25 worktree 정리 시 task-2550 의 MERGED PR 을 잘못 채택 → unrelated PR 의
  state 가 적용되어 task-25 가 잘못 정리되거나 차단되는 본질 risk.
- replacement chain 분리도 깨짐: task-2550 가 task-2550+1 의 PR 에 잘못 매칭.

### medium (4건, duplicate 1)

1. **worktree_cleanup.py:418** — `abs(hash(candidate.path)) % 10**8`
   - Python `hash()` 는 PYTHONHASHSEED 의존 (Python 재시작마다 random).
   - audit log 파일명 hash 가 재현 불가 → 동일 path 의 충돌 회피 의도 무효화.

2. **worktree_cleanup.py:227** — `pgrep -f <worktree_path>`
   - pgrep -f 는 process argv 의 substring 매칭만 수행.
   - `cd <worktree>` 한 process 의 CWD 는 argv 에 없으면 미감지.
   - safety_5 (not_in_use) 의 본질이 약해짐 — process 가 worktree 안에서 작업 중인데
     "사용 중 아님" 으로 잘못 판정.

3. **post_merge_smoke_runner.py:541** — `r.all_safe and not r.is_main and not r.dirty`
   - `r.all_safe` 는 6대 safety 모두 PASS 여야 True.
   - dry-run (apply=False) 에서는 safety_6 (apply_explicit) 가 항상 False → all_safe 항상 False.
   - 결과: dry-run 의 핵심 가치 `cleanup_candidates` (후보 가시화) 가 항상 0.
   - 회장 가시성 의도 (얼마나 많은 worktree 가 cleanup 대상인지) 가 무력화.

4. **duplicate** of #3 — Gemini 5번째 findings 가 #3 과 동일 문제 재지적. dedupe.

## fix 설계 의사결정

### HIGH fix — 두 단 게이트

단일 regex 만으로는 replacement chain (task-2550 vs task-2550+1) 분리가 어려움:
- regex `(^|sep)task-{num}(sep|$)` 에서 `+` 를 boundary 로 두면 task-2550+1 의 + 도 boundary
  → task-2550 가 task-2550+1 headRef 에 매칭 (BUG 잔존).

해결: 두 단 게이트.
1. regex 경계 — substring 오탐 (task-25 vs task-2550) 차단
2. `_extract_task_id(headRef) == task_id` 동등 — replacement chain 분리

### medium #1 fix — sha256 결정론

`hashlib.sha256(path.encode()).hexdigest()[:8]` 로 교체. 모든 환경에서 동일 path → 동일
hash. PYTHONHASHSEED 무관.

### medium #2 fix — pgrep + lsof AND 게이트

기존 pgrep 단독 → pgrep + lsof 두 단 AND. 어느 하나라도 사용 감지 시 FAIL.
- pgrep: argv 매칭 (실행 명령에 path 포함된 process)
- lsof +D: file-handle / CWD 매칭 (path 안의 파일을 연 process 또는 CWD 가 path 인 process)
- 단락 평가: pgrep match → lsof 호출 X (성능 + 회귀 분리)

lsof 부재 (FileNotFoundError) 시 보수적 FAIL — 운영 환경 정합성.

### medium #3 fix — helper 분리

`is_safe_ignoring_apply(result)` module-level helper:
- safety_results 의 condition 1~5 만 PASS 검사 (safety_6 apply_explicit 제외)
- main / dirty / safety_results 빈 list 면 False
- 호출자 (post_merge_smoke_runner) 에서 `cleanup_candidates = sum(1 for r in results if is_safe_ignoring_apply(r))` 로 가시성 회복

## 발사 조건 (회장 §명시 6/7단계 우선순위)

본 task 는 task-2554+1 merge + PR #103 처리 + OWNER_TRIGGER_ONLY_CAPABILITY main 반영 후
dispatch 가능. 발사 시점 (2026-05-12 KST):
- PR #103 (task-2549+1) MERGED 4e05acdd ✓
- PR #108 (task-2556 executor 자동 entry point) MERGED b6dca5d1 ✓
- PR #106 (task-2554+2 OWNER_TRIGGER_ONLY_CAPABILITY) MERGED cd1aeba4 ✓
- executor 자동 entry point verification PASS ✓

## 분리: STATE_FILE_MISSING (별도 follow-up)

PR #100 audit 에서 task-2550.done.escalated marker 의 state file 이 누락된 operational
defect 발견. 이는 finish-task.sh 의 state file 생성 로직 누락이며, merge gate 와 분리된
별도 task. 본 task-2550+1 에서는 처리 X.

## 회귀 회피 — 같은 PR push doctrine

PR #100 에 직접 commit/push 하지 않음. PR #100 head 4067d8c4 보존. 본 task 는 새 branch
(`task/task-2550+1-dev5`) 에서 새 PR 으로 진행.

## long polling doctrine 상속

본 task 는 control-plane task. polling_policy.py 상수 (5min~15min cooldown × 30min first
timeout × 1회 recheck × 종료) 1:1 상속. long polling 0.

## capability 자동 호출 채널

본 PR 의 Gemini review 가 누락 (FIRST_MISSING) 또는 staled (STALE) 시 OWNER_TRIGGER_ONLY_
CAPABILITY (anu_v2/owner_trigger_only.py) 가 OWNER_GEMINI_TRIGGER_TOKEN 으로 자동 호출됨.
PR #108 (task-2556) 의 executor 자동 entry point 통합이 이를 가능케 함. capability 5번째
실전 활용 — 자동 호출 발생 시 본 보고서에 박제 예정.
