# task-2720+1 보고서 — PR #166 Gemini MEDIUM bounded auto-remediation

**작성자**: 헤르메스(dev1 팀장) / 구현: 불칸(백엔드)
**작성일**: 2026-06-01
**결과**: bounded fix 3건 + dismiss 3건 완료 · regression 29 PASS · 로컬 commit only(push/PR/merge 0)

---

## S (Situation)
PR #166(task-2720 P0-a, head `ce87c35c`)에 Gemini unresolved finding이 존재. 회장 인가(2026-05-31): Critical7 아님·핵심 안전게이트 통과(ANU key 0/active 0/5파일/secret 0). Gemini finding을 bounded 1회 fix/dismiss로 MERGE_READY_CANDIDATE 직전까지 자동수렴.

## C (Complication)
- 기준은 **severity가 아니라 validity/scope/repetition**. dead-code/style/over-engineering = dismiss, 실제 동작/보안 결함만 fix.
- 이식모듈(`anu_owned_callback_enforcement.py`)은 PR#163 blob 보존 대상 → 명백한 P0-a 버그 외 변경 금지.
- expected_files 5개 밖 수정 금지 · push/PR/merge 금지 · ANU key literal 노출 0 · bounded 1회(재발 시 LOOP_BOUNDARY).

## Q (Question)
Gemini 7개 코멘트(중복 포함)를 봇이 직접 조회·validity 판정하여, 실제 결함만 bounded fix하고 나머지는 근거와 함께 dismiss한 뒤, regression을 유지/증가시키고 로컬 commit까지만 수렴할 수 있는가?

## A (Answer)
FIX 3건 / DISMISS 3건. regression 28→29 PASS. 우리 fix diff = 3파일. ANU key literal 0. _parse_timestamp blob 보존. L1 실 subprocess 통과.

---

## Gemini Finding 판정 (PR #166 comments 직접 조회: `gh api .../pulls/166/comments`)

### FIXED (3건 — expected_files 내 bounded fix)
| 위치 | Gemini severity | 판정 근거 | 수정 |
|------|-----------------|-----------|------|
| pickup_runner.py:206 | HIGH | crash 시 `O_EXCL` lock 영구 잔존 → task_id 영구 `SKIP_LOCK` 차단(liveness 결함). 실제 robustness 결함. | `_STALE_LOCK_SECONDS=600` mtime 임계 회수(임계 초과 시 unlink+재획득, 회수 경쟁=정상 경합). `time.time()` 비교(주입 clock과 mtime 혼용 방지). 신규 regression 추가. |
| pickup_runner.py:346 | MEDIUM | `marker_path=done_path_out`(line 362)에서 실제 소비. except 내부 조건부 annotation = possibly-unbound 정적분석 fragility. | try 전 `done_path_out: Optional[str] = None` 초기화로 None-가드(동작 동일). |
| finish-task.sh:1561 | MEDIUM | result.json(P0-a 핵심 산출물) write subprocess의 `2>/dev/null`이 SyntaxError/ImportError/권한 오류 traceback을 silent 차단 → silent 실패 리스크 실재. | `2>/dev/null` 제거로 에러 표면화(`||` 비차단 유지). |

### DISMISSED (3건 — validity/scope 미달)
| 위치 | Gemini severity | dismiss 근거 |
|------|-----------------|--------------|
| 이식모듈:466 (_parse_timestamp tz-offset) | HIGH | PR#163 **blob 보존 대상**. 실제 envelope writer는 `_iso()`(line 145/689)가 `%Y-%m-%dT%H:%M:%SZ` **Z-suffix**로 작성 → `_parse_timestamp_value` format(1)이 정확히 파싱. tz-offset(+09:00)은 P0-a 실동작에서 미발생 → **P0-a를 명백히 깨지 않음** → blob 변경 금지 유지. |
| pickup_runner.py:219 (dedup prefilter) | MEDIUM | dedup 정확성 결함 0. `in` 문자열 prefilter는 순수 성능 최적화(현 ledger 규모 불필요) → style/optimization-only. |
| pickup_runner.py:232 (ledger O(N)→SQLite) | MEDIUM | SQLite/KV store 도입 = over-engineering(premature optimization). |

### bounded 경계
동일 semantic 재발 시 LOOP_BOUNDARY 보고 + 추가 fix 금지(회장 verbatim). 본 +1 = 1회 수렴.

---

## 수정/생성 파일 (우리 fix = 3파일)

| 파일 | 변경 | grep 검증 | 상태 |
|------|------|-----------|------|
| dispatch/anu_result_pickup_runner.py | stale-lock 회수(206) + done_path_out None-가드(346) + `import time` + `_STALE_LOCK_SECONDS` | `_STALE_LOCK_SECONDS` 정의1/사용1, `_reclaimed` 4건, `done_path_out: Optional` try전 1건 | verified |
| scripts/finish-task.sh | executor-write-result `2>/dev/null` 제거(1561) | `2>/dev/null` near 1561 = 0건 | verified |
| tests/regression/test_anu_result_pickup_runner_2720.py | 신규 `test_fx_stale_lock_reclaimed` | grep 1건, 29 passed | verified |

> evidence(worktree 커밋): `memory/reports/task-2720.md`(§9 처리내역) · `memory/events/task-2720+1.done`.
> 이식모듈 `anu_owned_callback_enforcement.py`는 **미변경**(blob 보존) — diff에는 PR 기존분만 존재.

---

## L1 스모크테스트 결과 (실 subprocess 구동)
- **서버 재시작**: 해당없음 (백엔드 순수함수/subprocess 작업 — 상시 서버 없음)
- **API 응답 확인**: 해당없음 (CLI/모듈 실구동으로 대체)
  - `python3 -m dispatch.anu_result_pickup_runner pickup` 실 CLI: pipeline parse→lock→dedupe→sealed-key→key-gate 전 구간 구동. dummy key는 `SELF_COLLECTOR_FORBIDDEN`으로 정상 fail-closed(보안 게이트 정상).
  - **stale lock(mtime -700s)**: 회수 후 lock 게이트 통과(verdict FAIL@key = lock 통과 입증), lock 잔존 NO(finally 제거).
  - **fresh lock**: `SKIP_LOCK` 유지(오탐 회수 0 = 회귀방지).
  - **executor-write-result**(2>/dev/null 제거 후): `RESULT_JSON_WRITTEN`, result.json 생성=YES, schedule_created=false/callback_fired=false, 출력 표면화 확인.
- **스크린샷**: 해당없음 (프론트 작업 아님)
- L1 통과: 4개 항목 실행+통과(서버/스크린샷은 백엔드 subprocess 작업 특성상 해당없음).

---

## 검증 요약
- regression: `test_anu_result_pickup_runner_2720.py`(10) + `test_anu_owned_callback_enforcement_2717.py`(19) = **29 passed**(현 28→29).
- `python3 -m py_compile dispatch/anu_result_pickup_runner.py` OK · `bash -n scripts/finish-task.sh` OK.
- 우리 fix diff = 3파일. `git diff --name-only origin/main` = 5 code + 2 evidence.
- ANU key literal(sealed, 값 비표기) = **0건**(diff의 1건은 PR이 하드코딩 키를 *삭제*하는 라인). semantic 3조건 유지: T2626_ANU_KEY=0 / executor callback-launch 생성=0 / result.json 작성=1.

---

## 모델 사용 기록
- **불칸(백엔드)**: model=sonnet — 3건 코드 fix + regression 테스트 추가(코딩 작업, sonnet 기본값 준수).
- **헤르메스(팀장, Opus)**: 판정/검토/통합/L1/보고만 수행(직접 코딩 0, DIRECT-WORKFLOW 팀장 원칙 준수).
- haiku 미사용.

---

## 머지 판단
- **머지 필요**: No (회장 verbatim: push/PR/merge 금지 — ANU governance가 수행)
- **브랜치**: task/task-2720-dev1
- **워크트리 경로**: /home/jay/workspace/.worktrees/task-2720-dev1
- **로컬 commit**: `0d8c3522` (push 0)
- **머지 의견**: bounded fix 수렴 완료. ANU 독립 재검증 → FF push → OWNER `/gemini review` 1회 + dismiss thread resolve → CI 11/11 + unresolved 0 재확인 후 MERGE_READY_CANDIDATE 판정은 **ANU governance 영역**(봇 범위 외). merge는 회장 승인 전 금지.

## 발견 이슈 및 해결
- pyright `_sid is not accessed`(test 파일 line 180): 우리 변경(line 397) 무관한 기존 probe 함수의 의도적 `_`-prefix 미사용 인자 → 변경 불필요.
- 그 외 미해결 이슈 없음.
