---
task_id: task-2471+1
type: hardening-fix
scope: silent_corruption_guard self-application + .done.escalated reason payload
status: completed
level: 4
priority: P1
created_at: 2026-05-07T05:30:00Z
---

# task-2471+1 — drink-your-own-champagne 정밀 진단 + 최소 수정

## 0. 본 task의 본질
회장 명시: "manual recovery 대상이 아니다. task-2471 hardening의 자기검증 실패 의심. drink-your-own-champagne 정밀 검증으로 처리한다."

## 1. 목표 (회장 §6 8건 + §7 3건)
1. task-2471 state DONE 정상 전이 (정상 transition 경로 확인)
2. PR #36 1f96ddcd 보존 검증
3. `.done` 보존
4. `.done.escalated` 부재 또는 사유 박제 후 명시 해소
5. `.g3-fail` 부재
6. silent corruption guard가 `.done` + `.done.escalated` 충돌 탐지/차단/보고
7. 빈 escalation marker 재발 방지 테스트 PASS
8. branch cleanup 결과 보고
9. (보강) `.done` + `.done.escalated` 동시 reject 테스트 PASS
10. (보강) `--task-id task-2471+1` dispatch 적용 증거
11. (보강) `.done.escalated` 발행 trigger 코드 경로 + 호출 stack 박제

## 2. 범위 (allowed_resources 준수)
- `scripts/done-watcher.sh` — 빈 escalation marker 결함 수정 (사유 박제) — 발행자 #1
- `scripts/finish-task.sh` — (a) `.done` 발행 직전 silent_corruption_guard 통합, (b) ESCALATED 분기 빈 marker 결함 수정 — 발행자 #2
- `utils/silent_corruption_guard.py` — `.done`+`.done.escalated` 동시 존재 탐지 + empty escalation marker 차단 함수 추가
- `tests/regression/test_done_escalated_conflict.py` — 신규 (보강 §1-b)
- `tests/regression/test_empty_escalation_marker.py` — 신규 (보강 §1)
- `.tasks/state/task-2471.json` — 정상 transition 경로로 COMMITTED → MERGED → DONE 전이 (가능한 한 cmd_pr_open/merge/done 사용)
- `memory/events/task-2471.done.escalated` — 사유 박제 후 archive로 이동
- `memory/events/task-2471+1.*` — 본 task 산출물

### 범위 외 (별도 task) — Codex 사전 검증 결과 박제
- **structural-1**: finish-task.sh를 taskctl 명령 호출 경로로 전면 전환 (state machine과 .done 발행을 단일 경로로 통일). 회장 명시 "최소 코드 수정" 원칙에 부합 위반 → 별도 task. 본 task는 hardening 호출만 추가하여 자기 적용 보장.
- **structural-2**: branch auto-delete를 taskctl 외 경로로도 트리거. PR 머지 후 finish-task.sh가 worktree_manager.finish를 호출하지만 task-2471의 PR은 외부에서 머지됐을 수 있음(원인 추적). 별도 task에서 처리.
- **structural-3**: 자동 ack 데몬 (회장 명시 별도 task).

## 3. 진단 결과 요약 (사전 분석)

### 3.1 빈 .done.escalated 발행자 (회장 §3.1)
**위치**: `scripts/done-watcher.sh:96-122`
**호출 stack**: cron (systemd 또는 crontab) → `done-watcher.sh` → bash for loop → embedded `python3 -c "os.open(O_CREAT|O_EXCL|O_WRONLY); os.close(fd)"`
**결함**: `.done` age >= 1800s (30분) 시 escalation marker를 만들되 fd close만 호출 → **0 byte 파일**. trigger/source/age 정보 미박제.
**시간 일치**: `.done` 03:55 + 30분 = 04:25 정확.

### 3.2 .done 발행자 + silent_corruption_guard 우회 원인
**경로**: `scripts/finish-task.sh:1003-1024` (직접 발행) + `memory/task-timer.py:_write_event_file:518-581` (atomic merge)
**우회 원인**: task-2471은 `silent_corruption_guard`를 `scripts/taskctl.py:cmd_done`에만 통합. 그러나 production 경로(`finish-task.sh`)는 `taskctl done`을 호출하지 않고 `.done`을 직접 발행. **hardening false security**.

### 3.3 state COMMITTED 잔류 원인
**경로**: `scripts/finish-task.sh`이 `taskctl status`만 호출 (line 439), `taskctl pr-open`/`taskctl merge`/`taskctl done`/`taskctl recover` 어느 것도 호출하지 않음.
**결과**: state machine은 RUNNING → COMMITTED 이후 영구 정지. RECOVERABLE_BLOCKED, MERGED, DONE으로 전이 불가.

### 3.4 silent_corruption_guard 자체 결함
**위치**: `utils/silent_corruption_guard.py` (verify_done_preconditions)
**결함**: PR mergedAt/mergeCommit/origin ancestry만 검사. `.done` + `.done.escalated` 동시 존재라는 자체 corruption 패턴은 미탐지.

### 3.5 origin branch auto-delete 미작동
`task/task-2471-dev2` 가 origin에 잔존 (43ce82ba). PR #36 merge가 `--delete-branch` 옵션으로 발생하지 않았거나 worktree_manager.finish가 호출 안 됨.

## 4. 최소 수정 계획 (회장 명시: 진단 우선, 무리한 hardening 추가 금지)

### F1 — done-watcher.sh 빈 marker 결함 수정 (필수, 발행자 #1)
escalation marker에 JSON 페이로드 박제: `{trigger, ts, source, done_path, age_seconds}`. atomic O_EXCL 보존.

### F1b — finish-task.sh ESCALATED 분기 빈 marker 결함 수정 (필수, 발행자 #2)
`scripts/finish-task.sh:449-454` `: > "$EVENTS_DIR/${TASK_ID}.done.escalated"` → JSON payload 박제 (`{trigger:"finish-task.sh:taskctl_state_escalated", state:..., ts:..., source:...}`).

### F2 — silent_corruption_guard에 신규 검사 2개 추가
- `check_done_escalated_conflict(task_id, *, events_dir=None) -> dict` (보강 §1-b): `.done` + `.done.escalated` 동시 존재 → reject
- `check_escalation_marker_payload(task_id, *, events_dir=None) -> dict` (Codex 권고): `.done.escalated`가 존재하면 0 byte 또는 JSON 미파싱 시 reject (사유 박제 강제)
- `verify_done_preconditions`에 두 검사 통합 (PR 검사 + done-conflict 검사 모두 fail-closed)

### F3 — finish-task.sh `.done` 발행 직전 silent_corruption_guard 호출 (자기 적용)
`finish-task.sh:1003` 직전에 `verify_done_preconditions` + `check_done_escalated_conflict` 둘 다 호출. fail 시 `.done` 차단.

### F4 — regression test 2건 신설
- `tests/regression/test_empty_escalation_marker.py` — done-watcher.sh shellcheck + integration: marker는 항상 사유 포함
- `tests/regression/test_done_escalated_conflict.py` — silent_corruption_guard가 동시 존재 시 reject

### F5 — task-2471 state COMMITTED → DONE 정상 전이
`taskctl pr-open`/`merge`/`done`을 본 task의 worktree에서 호출하여 정상 transition 박제. 이로써 task-2471 사후 정상화 + drink-your-own-champagne 자체 검증.

### F6 — `.done.escalated` 사유 박제 후 archive
지금은 0 byte. 사유 (`{trigger: "done-watcher.sh stale 30min loop", root_cause: "empty marker defect (line 105-115 os.close without write)", resolved_at: ..., fixed_in: "task-2471+1"}`) 박제 후 `memory/events/archive/`로 이동.

### F7 — branch cleanup
`gh api repos/.../git/refs/heads/task/task-2471-dev2 -X DELETE` (or `git push origin --delete`). 또는 정책상 보존이 옳다면 사유 보고.

## 5. 비범위 (touch 금지)
- task-2471 본 보고서, PR #36 본문 (회장 §8)
- task-2471 hardening 코드를 manual recovery 회피용으로 변경 (회장 §8)
- 자동 ack 데몬 신설 (별도 task)
- gh pr merge 직접 호출, git push --force, git push origin main 직접 호출 (회장 §5 forbidden)

## 6. 검증 게이트
- Codex 사전 검증 (Lv.3+ 필수): codex_gate_check.py
- 마아트 G2/G3 독립 검증
- pytest tests/regression/test_done_escalated_conflict.py + test_empty_escalation_marker.py PASS
- L1 스모크: taskctl status task-2471 = DONE, ls .done.escalated 부재 또는 archive

## 7. 모델 사용
- 오딘(opus): 진단/설계/통합/보고서
- 토르(sonnet): F1/F2/F3 코드 수정
- 헤임달(sonnet): F4 regression test 작성

## 8. 합격 매핑
회장 §6 1-8 + §7 9-11 모두 충족 검증.
