[자문] Spec Gate 24h 에스컬레이션 흐름 설계
TL;DR
check_timeouts() 단일 함수 확장 + 배치 DM + mark 선행. PR #122 구현 완료.
질문 / 결정 사항
Spec Gate 48h ON_HOLD 전 24h 시점에 Jarvis DM 경보를 보내는 에스컬레이션 흐름 설계. 타이머 등록/취소 메커니즘과 트리거 조건 결정.
전제 (사용자 확인)
- **P1** 폴링 정밀도 ±1h 허용 (별도 정밀 스케줄러 불필요)
- **P2**
check_timeouts()단일 함수 확장 방식 채택 - **P3** 배포 직후 24h+ 기존 항목 일괄 DM 발송 허용 (의도적 동작)
옵션 비교
Option A: `check_timeouts()` 단일 함수 확장
장점
- 신규 데몬 스레드 없음
- 기존 1h 폴링 루프 재사용
- 48h→on-hold 로직과 순서 보장이 자연스럽게 처리됨
단점
- ±1h 폴링 딜레이 (24h~25h 사이 임의 시점)
트레이드오프
- 단순성 우선 / 정밀도 포기
---
Option B: 별도 `check_escalation()` + 데몬 스레드
장점
- 관심사 분리 명확
단점
- 데몬 스레드 추가 → 운영 복잡도 증가
- 48h 체크와의 실행 순서 보장 필요 (별도 동기화)
트레이드오프
- 복잡성 증가 대비 실익 미미 (폴링 방식이므로 정밀도 동일)
권장안
선택: Option A
근거:
if elapsed >= 48h ... elif elapsed >= 24h순서로 54h+ 항목 중복 알림 자동 방지- 기존 Lock 기반 RMW 패턴 그대로 재사용 (
mark_escalated동일 구조) - 배치 DM: 동일 폴링 사이클 다수 티켓을 단일 메시지로 묶어 DM 폭탄 방지
맹점 / 리스크
DM 발송 실패 시 escalated_at 불일치
mark_escalated()선행 → DM 실패 시 재발송 없음- 반대로 mark 후행(DM 성공 후 mark)이면 중복 DM 가능
- 판단: 중복 DM이 더 나쁜 UX → mark 선행 채택
봇 다운타임 Skip 케이스
- 봇이 23h~49h 동안 중단 후 재시작 시 24h 경보 생략, 곧바로 on-hold
- 현재 48h 시스템도 동일 동작 → 기존 설계 방침 그대로 허용
구현 변경 요약
| 변경 | 내용 |
|---|---|
| `SpecGatePendingStore.write()` | `escalated_at: null` 명시 추가 |
| `SpecGatePendingStore.mark_escalated()` | Lock 내 RMW로 escalated_at 기록 |
| `_make_deeplink()` | `archives/{channel}/p{ts_nodot}` 딥링크 생성 |
| `_send_escalation_dm()` | 배치 DM: 건수 + 딥링크 + 남은 시간 |
| `check_timeouts()` | `escalation_hours` 파라미터 추가, elif 분기 추가 |
| `SPEC_GATE_ESCALATION_HOURS` | 환경변수 (기본 24) |
참고 자료
- PR: https://github.com/hangseung/pantheon/pull/122
- Gemini cross-check: atomicity(Lock 기존 보유 확인), rate-limit(개인 시스템 negligible), skip edge case(허용)