[자문] 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(허용)