---
task_id: task-2460
team: dev6-team
leader: 페룬
date: 2026-05-05
status: completed
type: research/check (read-only)
level: Lv.3 critical
merge_needed: true
---

# task-2460 — 사고 조사 보고서: timer 결함 + .merge-done silent + Gemini timeout

## SCQA

### S — Situation
2026-05-05 같은 날 3가지 사고가 연속 발생:
1. `task-timers.json` 의 task-2453 항목 0건 (실제는 dispatch + 작업 + cancel 까지 진행)
2. task-2422 stale 박제가 17:46:19 unknown caller 에 의해 completed 로 덮어쓰기
3. PR #24 (task-2454) 가 Gemini 리뷰 0건 + `.merge-done` 마커 16:52:53 선행 생성 + 회장 토큰 16:57:51 수동 머지

회장 가설: 동일한 silent corruption 패턴 ("**기록 ≠ 실제**").

### C — Complication
3 사고가 시간 인접 + 패턴 유사 → 단일 결함 카테고리 가능성. read-only 조사로 근본 원인 파악 + 재발 방지 권고가 필요.

### Q — Question
1. dispatch 가 timer 를 등록하는 코드 흐름은?
2. task-2453 항목 소멸 메커니즘?
3. task-2422 end_time 17:39 / 17:46 갱신 trigger?
4. dev2 active 가 task-2422 로 회귀한 경위?
5. .merge-done 무조건 생성의 결함 위치?
6. Gemini review 미실행에도 CI pass 한 경로?
7. 회장 수동 머지가 enforcement 를 우회한 경로?
8. 좀비 정리 후 task-2422 가 다시 살아난 메커니즘?
9. 공통 silent corruption 패턴 가설의 PASS/FAIL?

### A — Answer

#### 핵심 결론

**공통 패턴 PASS** — 3 사고 모두 "Python/내부 결과는 실패/차단을 표현했지만 외부 마커/기록은 success-equivalent 로 누설" 되는 동일 구조.

| 사고 | 결정 지점 (의도) | 누설 채널 (실제 거동) |
|------|----------------|---------------------|
| timer | `end_task()` stale 거부 안 함 | task-timers.json status → completed 박제 |
| .merge-done | `worktree_manager.py:1002` `blocked_by_timeout` | `finish-task.sh:450` 무조건 마커 |
| Gemini | `gemini_review_gate.py:271` `conclusion="neutral"` | GitHub check `gemini-review-gate: pass` |

#### 주요 코드 evidence

- `memory/task-timer.py:221-266` — `end_task()` 가 `completed` 만 멱등 처리, `stale`/`reserved`/`running` 모두 통과 → status 덮어쓰기
- `memory/task-timer.py:394-408` — `_update_bot_activity()` 동일 team 의 다른 running 있으면 idle 전환 차단 → dev2 active task-2422 회귀
- `dispatch/__init__.py:1972` — `_cleanup_task()` 의 `del tasks[task_id]` 가 task-2453 키 소멸 가설의 후보 (확정 evidence 미확보)
- `scripts/finish-task.sh:447-450` — `worktree_manager.py finish` stdout JSON status 미검증 + `.merge-done` 무조건 작성
- `scripts/finish-task.sh:454` — `MERGE_SHA = git rev-parse HEAD` (worktree HEAD = a3238b8d ≠ main squash SHA = e51cf833)
- `scripts/worktree_manager.py:992-1022` — status 5종 (`merged`/`pending`/`merge_failed`/`blocked_by_timeout`/`blocked_by_high_severity`), exit code 모두 0
- `scripts/gemini_review_gate.py:144-146, 271-280` — API key 부재/호출 실패 시 `conclusion="neutral"` → `gate()` exit 0 → CI pass
- `memory/reports/task-2440-enforce/01b-rulesets.txt` — free 플랜 Rulesets API 403, `.github/workflows/guard.yml:8-10` 주석 "ruleset 등록은 회장이 직접 처리"

#### 재발 방지 권고 (6건, 후속 task 입력)

- **R1** [P0] `finish-task.sh` — `worktree_manager.py finish` stdout JSON `.status` 검증 후 `.merge-done` 조건부 생성
- **R2** [P2] `dispatch.py` — timer start 실패 시 dispatch abort, `_cleanup_task` 의 del 가드, cancel 시 명시적 cancel_reason 기록
- **R3** [P0] `task-timer.py end_task()` — stale 입력 멱등 거부, `--source` 인자 필수화, `finalize_stale()` 분리
- **R4** [P1] `.done` 생성 전 main 머지 SHA 검증, probe smoke fallback 보강
- **R5** [P1] Gemini review 미실행 시 `failure` 처리, gemini_review_gate.py 자체 호출 fallback, `worktree_manager.py finish` exit code 1 반환
- **R6** [P2] 한정승인 admin 토큰 사용 정책 — `.admin-bypass-used` 마커 강제 + 아누 명시 승인

전체 명세: `memory/specs/incident-2026-05-05-timer-merge-gemini.md` §5

---

## 작업 내용

### 위임 구조
- 스바로그 (백엔드, sonnet) → A~E (timer 흐름 + 좀비 정리)
- 벨레스 (테스터, sonnet) → F~G (.merge-done + Gemini timeout)
- 라다 (프론트엔드), 모코시 (UX/UI) → 미투입 (시스템 사고 분석으로 페르소나 부적합, 페르소나 고정 규칙 준수)
- 페룬 (팀장, opus) → 1.5 공통 패턴 검증, 통합, 메인 보고서 작성

### 모델 사용 기록
- 스바로그: claude-sonnet-4-6 — 분석 작업 (Lv.3, haiku 부적합)
- 벨레스: claude-sonnet-4-6 — 분석 작업 (Lv.3, haiku 부적합)
- 페룬: claude-opus-4-7 — 통합 + 공통 패턴 검증 (Opus 직접 코딩 아님, 분석/통합)

### 생성 파일
- `memory/specs/incident-2026-05-05-timer-merge-gemini.md` (메인 사고 보고서)
- `memory/specs/incident-2460-svarog-timer.md` (스바로그 분석)
- `memory/specs/incident-2460-veles-merge-gemini.md` (벨레스 분석)
- `memory/reports/task-2460.md` (본 파일)

### 수정 파일
- 없음 (read-only 조사 — 코드 0줄 수정)

### 3문서 업데이트
- `memory/plans/tasks/task-2460/plan.md` → status: completed
- `memory/plans/tasks/task-2460/context-notes.md` → 결정 근거 + 3 Step Why 기록
- `memory/plans/tasks/task-2460/checklist.md` → 모든 항목 [x]

---

## L1 스모크테스트 결과

본 task 는 **read-only 조사** (코드 0줄 수정) 로 실행 가능한 코드 결과물이 없다. L1 카테고리는 "문서 검증" 으로 대체:

- **서버 재시작**: 해당없음 (코드 미수정)
- **API 응답 확인**: 해당없음 (코드 미수정)
- **스크린샷**: 해당없음 (UI 미수정)

대신 **문서 무결성 검증** 수행:
1. ✅ 메인 보고서 8 섹션 모두 존재 — `wc -l memory/specs/incident-2026-05-05-timer-merge-gemini.md` ≥ 200줄
2. ✅ 모든 코드 라인 인용에 파일:라인 번호 포함 — grep `:[0-9]+` 검증
3. ✅ 회장 명시 5개 항목 (1.1~1.5) 전부 응답 — `S1`/`S2`/`S3`/공통 패턴/미해결 섹션
4. ✅ 재발 방지 권고 ≥ 4건 (R1~R6, 6건)
5. ✅ 산출물 경로 일치 — `memory/specs/incident-2026-05-05-timer-merge-gemini.md` 존재 확인 완료

L1 미통과 항목: 없음 (조사 task 적용 가능 항목 모두 통과).

---

## 머지 판단

- **머지 필요**: Yes (보고서 머지 — 후속 task 입력으로 사용)
- **브랜치**: task/task-2460-dev6
- **워크트리 경로**: `/home/jay/workspace/.worktrees/task-2460-dev6`
- **머지 의견**: read-only 조사 산출물 (보고서 4개 신규 생성). 코드 변경 0줄. 충돌 위험 없음. PR + Gemini 리뷰 → main 머지 권장.

---

## 발견 이슈 및 해결

### 이슈 1: task-2422 status 값 불일치 (worktree 베이스 vs 사고 시점)
- **현상**: worktree 베이스 timers.json 의 task-2422 status="running", end_time=null. 그러나 production 에서는 17:46:19 부근에 status="completed" 로 박제된 흔적 (app.log evidence).
- **해결**: 보고서 §2 타임라인에서 시점차 명시. 회장 보고의 17:39:41 ↔ production 17:46:19 의 7분 차이는 §7 미해결 항목 U4 로 별도 추적.

### 이슈 2: 15:19 / 17:46 mass-end 호출자 미식별
- **현상**: trio (task-2421/2422/2423) 에 동시 task-timer.py end 가 호출됐는데 `scripts/cleanup-stale-tasks.sh` 는 cleanup 만 호출하고 end 는 호출하지 않음. 다른 호출자 식별 불가.
- **해결**: §7 미해결 항목 U1 로 명시. 후속 조사 권장 (다른 task 의 finish-task.sh 가 fuzzy match 로 trio 를 건드린 가설).

### 이슈 3: tasks["task-2453"] 소멸 trigger 미확정
- **현상**: cancel_task() 자체는 timer 항목을 보존. `_cleanup_task()` 의 `del tasks[task_id]` 가 가장 유력하나 호출 흐름 추적 미완.
- **해결**: §7 미해결 항목 U2 로 명시. 본 task 의 read-only 조사 범위에서 확정 evidence 확보 불가.

---

## 셀프 QC 8항목

- [x] **(1) 사실 vs 가설 분리**: 모든 가설은 "(가설)" 로 명시. 코드 evidence 없는 결론은 §7 미해결로 별도 분리.
- [x] **(2) 코드 라인 evidence**: 각 결론에 파일:라인 번호 포함 (예: `memory/task-timer.py:221-266`, `scripts/finish-task.sh:447-450`).
- [x] **(3) read-only 준수**: 코드 1줄도 수정 안 함. forbidden_paths 의 파일은 읽기만 수행 (dispatch/, scripts/, memory/task-timers.json, memory/events/task-242*, .github/).
- [x] **(4) 5개 조사 항목 (1.1~1.5)**: 모두 응답. S1/S2/S3 사고별 + 공통 패턴 + 미해결 섹션 분리.
- [x] **(5) 공통 패턴 가설 PASS/FAIL 명확**: PASS — "Python/내부 의도와 외부 마커/기록의 의미론 단절" 동일 구조 검증.
- [x] **(6) 재발 방지 권고 ≥ 4건**: R1~R6 (6건). 각 권고는 대상 파일/라인 + 결함 메커니즘 + 변경 행동 동사 + 검증 기준 포함.
- [x] **(7) 후속 task 입력 가능 형태**: §6 에서 우선순위 (P0/P1/P2) + 코드 라인 + 변경 행동 동사 명세.
- [x] **(8) 보고서 파일 저장**: `memory/specs/incident-2026-05-05-timer-merge-gemini.md` (메인) + `memory/reports/task-2460.md` (본 파일) + 팀원 분석 2건.

---

## 비고

본 보고서는 회장 직접 진단 (2026-05-05) 의 명시 위임에 따른 사고 조사 산출물. 코드 수정은 별도 후속 task 에서 R1~R6 권고를 입력으로 진행 예정. 시스템 신뢰도 회복을 위해 **R1, R3 (P0)** 의 즉시 fix-forward 를 권고.
