# task-2360 봇 → 아누 자동 알림 파이프라인 (finish-task.sh 통합)

## 요약 (SCQA)

**S(Situation)**: 봇이 finish-task.sh로 .done을 만들고 completion.txt를 자동 생성하지만, 미해결/머지 필요/회장 결정 같은 강조 메모를 능동적으로 아누에게 전달할 수단이 없었다.

**C(Complication)**: 아누는 events 폴링 또는 회장 "작업체크" 명령으로만 인지 → 봇이 강조하고 싶은 항목이 묻힘. 기존 notify-completion.py는 단일 Telegram 메시지(완료 통보)만 발사.

**Q(Question)**: 봇이 .followup.txt로 강조 메모를 명시 작성하면 아누 chat에 별도 cron 메시지로 자동 발사되고, 누락 시 보고서에서 자동 추출되며, finish-task.sh 미호출 시 done-watcher 폴백으로 보장하는 인프라가 가능한가?

**A(Answer)**: **A+B+D 종합 채택 구현 완료**. extract_followup.py(헬퍼) 신규 + finish-task.sh 마지막 단계 통합 + done-watcher.py 5분 폴백 + docs/followup-format.md 봇 가이드 + DIRECT-WORKFLOW.md 6.5단계 추가. 9개 단위테스트 통과 + 6개 검증 시나리오 PASS + 라이브 cokacdir 발사 검증(cron 76E40647, task-2360 자기 자신 발송). 중복 방지 마커(.anu-notified) + 4000자 메시지 잘림 + DISABLE_ANU_FOLLOWUP=1 비상 OFF 안전장치 포함.

## 작업 내용

### 1. 헬퍼 모듈 (Phase 4)

**신규**: `/home/jay/workspace/scripts/extract_followup.py` (598 lines)

3단 우선순위 추출:
1. `memory/events/<task>.followup.txt` — 봇 강조 메모 (옵션 B)
2. `memory/reports/<task>.md` — 정규식 자동 추출 (옵션 A)
3. `memory/events/<task>.completion.txt` — 1줄 폴백

표준 5섹션 메시지: 핵심 결과 / ★ 회장 결정 필요 / ★ 머지 필요 / ★ 미해결 / ★ 다음 단계 권장.

CLI:
- `extract <task_id>` — dict JSON 출력
- `format <task_id>` — 메시지 텍스트 미리보기
- `send <task_id> [--dry-run] [--delay N]` — cokacdir cron 등록 + .anu-notified 마커

cokacdir `--at` 형식 검증 후 ISO `T`-구분자 → 공백 `'YYYY-MM-DD HH:MM:SS'`로 수정 (cokacdir 거부 사례 발견).

### 2. finish-task.sh 통합 (Phase 1)

**수정**: `/home/jay/workspace/scripts/finish-task.sh` (912~924 line)

위치: notify-completion.py 직후 (Step 6 신규 추가)
```bash
if [ "${DISABLE_ANU_FOLLOWUP:-0}" != "1" ]; then
    python3 "$WORKSPACE/scripts/extract_followup.py" send "$TASK_ID" 2>&1 \
        | tail -2 \
        || echo "[WARN] anu-followup send failed (non-fatal)"
fi
```

비상 OFF: `DISABLE_ANU_FOLLOWUP=1` 환경변수.

### 3. .followup.txt 포맷 + 가이드 (Phase 2)

**신규**: `/home/jay/workspace/docs/followup-format.md` (105 lines)

봇 작성 기준:
- 회장 결정/머지/미해결을 강조하고 싶을 때만 작성 (옵션)
- 안 만들어도 보고서에서 자동 추출
- 인식 헤더 정규식: 회장\s*(결정|승인|결재) / 머지\s*(필요|판단)? / 미해결 / 다음\s*단계 / 핵심\s*결과

### 4. done-watcher 폴백 (Phase 3)

**수정**: `/home/jay/workspace/scripts/done-watcher.py` (310~358 line)

`fire_anu_followup_fallback()` 함수 추가:
- `.done` 파일이 5분(`ANU_FOLLOWUP_GRACE_SECONDS=300`) 이상 묵었는데 `.anu-notified` 마커 없으면 `extract_followup.py send` 실행
- task_id 패턴 `^task-\d+(?:\.\d+)?$`만 처리 (안전)
- run_once + run_daemon 양쪽 통합
- DISABLE_ANU_FOLLOWUP=1 환경변수 존중

### 5. 워크플로우 갱신

**수정**: `/home/jay/workspace/prompts/DIRECT-WORKFLOW.md` (Step 6.5 신규)

- "[옵션] `.followup.txt` 작성" 단계를 finish-task.sh 호출 직전에 명시
- Step 7 finish-task.sh 설명에 "→ 아누 후속 알림(cron)" 추가
- 멱등성 마커 목록에 `.anu-notified` 추가

### 6. 단위 테스트 (회귀 방어)

**신규**: `/home/jay/workspace/scripts/tests/test_extract_followup.py` (9 tests)

- followup.txt 우선순위
- 보고서 자동 추출 폴백
- followup.txt 부분 채우면 보고서로 보충
- completion.txt 폴백
- 4000자 메시지 잘림
- .anu-notified 중복 방지
- `--at` 공백 구분자 형식 (cokacdir 호환)
- 5섹션 모두 포함
- 헤더 없는 followup.txt 핸들링

## 모델 사용 기록

- claude-opus-4-7[1m] (이참나, dev7-team) — task-2360 단독 진행 (서브에이전트 미사용)
- 대규모 참조 파일(finish-task.sh 916줄, DIRECT-WORKFLOW.md 527줄)은 offset/limit 분할 읽기로 컨텍스트 절약

## 변경 파일 목록

### 신규
- `scripts/extract_followup.py` (598 lines)
- `docs/followup-format.md` (105 lines)
- `scripts/tests/test_extract_followup.py` (9 tests)

### 수정
- `scripts/finish-task.sh` (Step 6 추가, +13 lines)
- `scripts/done-watcher.py` (fallback 함수 + run_once/daemon 통합, +60 lines)
- `prompts/DIRECT-WORKFLOW.md` (Step 6.5 추가 + Step 7 보강, +6 lines)

## 검증 결과

### 시나리오 검증 (6건)

1. **정상 흐름 (옵션 A)**: task-2354 보고서에서 자동 추출 → dry-run 발송 OK (source=report, msg_len=1659).
2. **followup.txt 포함 (옵션 B)**: task-2999 임시 followup.txt 우선 사용 검증 (source=followup_txt, 모든 섹션 정확 매핑).
3. **followup 부분 + 보고서 보충**: 단위 테스트 `test_followup_supplements_missing_report_sections` 통과.
4. **중복 방지**: `.anu-notified` 마커 존재 시 `status=already-notified`, exit 0. 단위 테스트 + 라이브 검증.
5. **메시지 길이 제한**: 50개 200자 bullet → 3783자(< 4000)로 자동 잘림 + "(생략 — 보고서 참조)" 표시. 단위 테스트 PASS.
6. **finish-task.sh 누락 폴백**: `.done` mtime 10분 전 + DISABLE_ANU_FOLLOWUP 미설정 → done-watcher가 폴백 발사 OK.

### 메시지 포맷 샘플 5건

| task | source | 길이 | 비고 |
|------|--------|------|------|
| task-2354 | report | 1659 chars | 회장 결정 6건 + 머지 4건 + 미해결 9건 |
| task-2356 | report | 1109 chars | 머지 Yes + 미해결 3건 + 다음 단계 3건 |
| task-2999 (sim) | followup_txt | 539 chars | 헤더 명시 작성 |
| task-2360 (live) | followup_txt | 539 chars | 라이브 cron 발사 검증 |
| 4000자 초과 (sim) | followup_txt | 3783 chars | 우선순위 잘림 적용 |

### 단위 테스트

```
$ pytest scripts/tests/test_extract_followup.py
9 passed in 0.09s
```

### 회귀 점검

- `pytest scripts/tests/test_done_watcher.py`: 13 passed, 1 failed (사전 시점 하드코딩 이슈, 본 task 무관).
- `pytest scripts/tests/test_done_protocol.py`: 사전 실패 케이스(notify-completion이 cokacdir → Telegram API로 전환된 후 stale assertion). 본 task 변경 적용 전후 동일 — `git stash` 검증 완료.

### L1 스모크테스트

- **서버 재시작**: 해당없음 (인프라 스크립트 변경)
- **API 응답 확인**: 해당없음 (CLI 스크립트)
- **라이브 cron 발사 검증**:
  - 명령: `python3 scripts/extract_followup.py send task-2360 --delay 60`
  - 등록: cokacdir id=76E40647, schedule=2026-05-02 16:30:26 absolute
  - 실행: cron.log `[16:30:51] [execute_schedule] id=76E40647 ... type=absolute ... END` 확인
  - 마커 생성: `memory/events/task-2360.anu-notified` 존재 확인
  - 발송 로그: `logs/anu-notify.log` `[16:29:26] task-2360: 아누 cron 발송 완료 (delay=60s)` 기록
- **스크린샷**: 해당없음 (텍스트 메시지 인프라)

## 발견 이슈 및 해결

### 자체 해결

1. **cokacdir `--at` 형식 거부** — 초기 `'%Y-%m-%dT%H:%M:%S'` (ISO T-구분자)로 작성했으나 cokacdir가 `invalid --at value`로 거부. 실시간 검증 후 `'%Y-%m-%d %H:%M:%S'` (공백 구분자)로 수정. 단위 테스트 `test_at_format_uses_space_separator`로 회귀 방어.

2. **테스트 cron 정리 중 실수로 실 schedule 삭제** — 테스트 정리 시 `CC712188`(codegraph-anu Phase 2.5 KPI 측정) 일정을 함께 삭제. `/home/jay/.cokacdir/debug/cron.log`에서 원본 prompt + 시점(2026-05-08 09:00:00)을 추출해 신규 id `3EA43774`로 즉시 복구. 무손실 회복.

3. **mock cokacdir 테스트 부작용** — done-watcher 폴백 테스트 중 task-2355에 `.anu-notified` 잘못 생성. 마커 제거하여 다음 finish-task.sh가 정상 발사하도록 정리.

### 범위 외 미해결

1. **사전 시점 하드코딩 테스트 실패** — `test_done_watcher.py::test_idle_timestamp_updated`가 "2026-03"으로 하드코딩됨. 본 task 무관, 기존 코드 잔존. 별도 task로 정리 권장.

2. **test_done_protocol.py 일부 stale assertion** — notify-completion.py가 cokacdir → Telegram API 직접 호출로 전환되었으나 테스트는 cokacdir 호출을 검증. 본 task 무관. 별도 task 권장.

## 머지 판단

- **머지 필요**: Yes (단, **워크트리 미사용 인프라 직접 수정**)
- **브랜치**: main 직접 commit (Lv.2 인프라 — projects/ 외부)
- **워크트리 경로**: 해당없음 (workspace 자체 인프라 수정)
- **이미 커밋**: `7da70265 [task-2360] 이참나: extract_followup.py 헬퍼 신규`
- **추가 commit 예정**: finish-task.sh + done-watcher.py + docs + DIRECT-WORKFLOW + tests
- **머지 의견**: PR 생성 없이 main commit. 변경 파일 모두 워크스페이스 인프라(.cokacdir 외부)이며 프로젝트 코드 영향 없음. 차주 운영 후 메시지 길이/품질 통계 점검 권장.

## 셀프 QC 8항목

1. **요구사항 매핑**: A+B+D 종합 채택 — 헬퍼 모듈, finish-task.sh 통합, .followup.txt 포맷, done-watcher 폴백, 봇 가이드, 워크플로우 갱신 모두 구현 ✅
2. **변경 영향 평가**: 기존 notify-completion.py는 그대로 유지. 신규 cron 채널만 추가됨. DISABLE_ANU_FOLLOWUP=1 비상 OFF 보유 ✅
3. **에러 핸들링**: cokacdir 실패 시 non-fatal warn, .anu-notified 마커 미생성 → 다음 사이클 폴백 가능 ✅
4. **보안**: 셸 인젝션 방지 위해 subprocess.run 리스트 인자, 환경변수 + cokacdir CLI만 사용. ANU_KEY 하드코딩 X (env + 폴백 const 한정) ✅
5. **테스트**: 9개 단위 테스트 + 6개 시나리오 검증 + 라이브 cron 발사 ✅
6. **문서**: docs/followup-format.md + DIRECT-WORKFLOW.md Step 6.5 추가 ✅
7. **회귀**: 기존 done-watcher .done idle 처리 + .failed 처리 영향 없음 (별도 함수 추가) ✅
8. **로그**: logs/anu-notify.log 기록 + done-protocol.log에 폴백 발사 기록 ✅

## 다음 단계 권장

- 차주(2026-05-09 이후) 운영 후 `logs/anu-notify.log` 메시지 길이/실패율 통계 점검 → 4000자 cap 적정성 평가
- bot persona 자동 매핑 강화 (task-timers.json에 명시 안 된 경우 fallback)
- 사전 시점 하드코딩 테스트 정리 (별도 task)

## 참조

- 회장 결정 시점: 2026-05-02 (오늘)
- 전제 task: task-2348 (finish-task.sh 루프 fix), task-2350 (BG cleanup), task-2352 (cancellation enforcement) — 모두 머지 완료
- 검증 보고서 추출 대상: task-2354, task-2356
- 라이브 cron 발사 ID: 76E40647 (task-2360 자기 자신 검증)
- 복구된 schedule: 3EA43774 (codegraph-anu Phase 2.5 KPI, 2026-05-08 09:00:00)
