docs(dcy-033): 제품군 알람 전달 체계 discovery + REQ-098~101 (trex-core 소유분)

confirm-discovery + draft-req + confirm-req 완료. 알람 envelope wire 계약(098)·
trex.alerts AMQP exchange 가산·격리(099)·core CRITICAL emitter 8종
best-effort 비차단(100)·baseline 정합(101). 전송=trex.alerts AMQP 확정,
paging=ALARM-INVENTORY §A 전체. delivery는 M2 이후 큐잉.
orb/tsl/kdh emitter·trex-notifier는 각 repo 별도.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
This commit is contained in:
2026-06-21 14:56:14 +09:00
co-authored by Claude Opus 4.8
parent f1b649cf2a
commit ebca9be0d1
7 changed files with 585 additions and 1 deletions
@@ -0,0 +1,215 @@
# 0. 문서 상태
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
- 상태: confirmed
- 문서 ID: dcy-033_20260621_suite-alarm-delivery-notifier
- 이슈명: 제품군 알람 전달 체계 — 모듈 발신 + 중앙 notifier(trex-notifier) → 텔레그램(P1 아웃바운드)
- 작성 시각(KST): 2026-06-21 (KST)
- 마지막 갱신 시각(KST): 2026-06-21 (KST, draft-req trex-core-dev)
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: REQ-098 (알람 envelope wire 계약), REQ-099 (trex.alerts exchange 토폴로지), REQ-100 (core CRITICAL emitter 배선), REQ-101 (알람 발신 흐름 baseline 정합)
# 1. 계획 상태 요약
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
- 연계 문서 현황:
- 관련 점수 파일: 미생성
- 관련 TODO/메모: `/home/genichin/workspace/trex-lead/ALARM-INVENTORY.md`(0단계 알람 인벤토리 스윕 결과 — core/orb/tsl/kdh ~100개 조건, 본 Discovery의 source-of-truth), `repos/trex-notifier`(부트스트랩 완료된 신규 서비스 repo)
- 해석:
- TREX 제품군엔 운영자에게 이상을 즉시 알리는 구조화된 알람 채널이 없다. ~100개 알람 후보가 로그(또는 예외)로만 존재하고, CRITICAL 상황도 운영자가 능동 인지해야 한다. M2(tsl 매도 라이브)에서 운영자가 첫 거래일을 수기 감시해야 하는 부담도 이 갭에서 나온다.
- 토론으로 큰 방향은 확정됐다: 신규 `trex-notifier`가 알람을 중앙 수집해 텔레그램으로 전달(P1 아웃바운드만). 본 Discovery는 그 알람 **전달 계약과 아키텍처**를 trex-core 변경 단위로 정식화한다(envelope는 trex-core 소유 wire 계약).
# 2. 요구사항 판정 결과
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
- 판정: 신규
- 근거
- dcy-029(운영 관측성 — 로깅/PnL 뷰), dcy-031(이벤트 발행 신뢰성)은 인접하나 "이상을 운영자에게 능동 푸시"하는 알람 전달 체계는 어디서도 다루지 않았다. 기존은 전부 pull(로그/CLI) 기반이다.
- 제품군 횡단 신규 컴포넌트(trex-notifier)와 신규 wire 계약(알람 envelope)을 도입하므로 독립 주기로 추적하는 편이 명확하다.
# 3. 문제점의 요약
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다.
- 현재 상태
- trex-core에 구조화 알람 채널 부재 — 운영자 표면은 CLI `trex-core report` + 구조화 로그(Loki)뿐(`daemon.py:248`에 alert 주석 1건). 푸시 메커니즘 없음.
- 인벤토리 스윕(ALARM-INVENTORY.md) 결과: core/orb/tsl/kdh에 ~100개 알람 후보. 대부분 WARN/ERROR로 로깅되나 **일부 CRITICAL은 예외 raise만 되고 로그조차 없다**(예: 토큰 격리, RPC 타임아웃 누적). 등급도 모듈마다 제각각.
- 연쇄/상관 실재: kis-data-hub 인증실패/다운 1건 → orb `kdh_auth_failure` + tsl `kis_auth_failure_abort` 동시 발생. 단 core safety-sell은 직접 KIS(다른 키)라 독립.
- `trex-notifier` repo는 부트스트랩 완료(골격+거버넌스+baseline, root commit `078d37d`, private). 단 전송 방식·의존성은 본 Discovery 확정까지 비어 있다.
- 기대 상태
- 각 모듈이 이상 조건을 표준 **알람 envelope**로 발신하면, 중앙 `trex-notifier`가 등급 판정·중복/상관 억제 후 CRITICAL은 텔레그램 즉시 푸시, WARN은 일일 다이제스트로 전달한다.
- 크래시·정지로 자기 알람을 못 내는 데몬은 **하트비트(침묵=장애)** 로 탐지된다.
- 알람 전달이 기존 wire 계약(slots RPC/trex.events)·로그 저장 경로를 건드리지 않는다(가산·격리).
- 이해관계자
- 사장님 — 운영자(알람 최종 수신자), 범위·보안·외부노출 결재자
- 팀장(Claude) — Discovery/게이트, 인벤토리 작성자
- trex-core-dev — envelope 계약 + core emitter delivery
- 각 모듈 dev(orb/tsl/kdh) — 모듈별 emitter delivery
- service-dev — trex-notifier 서비스 delivery
- 제약사항
- 기존 wire 계약(slots RPC, trex.events) 스키마 변경 금지 — 알람은 가산적 신규 채널로만.
- orb/tsl/kdh는 `trex_core`를 import하지 않는다 — 공유는 **규약(envelope)** 이지 런타임 코드가 아니다(모듈당 thin emitter).
- notifier는 로그 firehose 게이트웨이가 아니다 — 로그 저장은 인프라 슈퍼(Vector/Promtail→Loki) 담당. notifier는 알람만.
- core는 텔레그램을 직접 호출하지 않는다(외부 의존·시크릿 격리). 시크릿(봇 토큰)은 notifier 1곳.
- 알람 채널이 의존하는 인프라(RabbitMQ/Loki)가 장애나면 그 경로 알람이 못 나간다 → 하트비트 필요.
## 주요 사용자 시나리오
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
- 운영자: tsl 매도 라이브 첫날, `sell_signal_rejected_terminal`(포지션 못 팜) 발생 → 즉시 텔레그램 CRITICAL 푸시로 인지하고 개입. 현재는 로그를 들여다봐야만 알 수 있다.
- 운영자: kis-data-hub 인증 만료 → orb·tsl 동시 알람을 notifier가 "시세 허브 장애 — 2개 모듈 영향" 1건으로 묶어 전달(폭풍 억제).
- 운영자: tsl 데몬이 크래시해 침묵 → 하트비트 부재로 notifier가 "tsl 데몬 응답 없음" 알람.
- 운영자: 장마감 후 WARN 다이제스트("오늘 백필 2건, watchdog 인수 0건, stale 양보 0건")로 비긴급 이상을 한 번에 검토.
# 4. 이번에 정의할 변경
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다.
- 변경 1 — **알람 envelope 계약(trex-core 소유)**: `source / severity(critical·warn·info) / code / title / fields / occurred_at / schema_version` 표준 메시지 스키마 정의. 전 모듈이 이 규약으로 발신.
- 변경 2 — **알람 전송 채널**: 전용 **`trex.alerts` AMQP exchange**(확정, 사장님 2026-06-21) — 가산·격리(기존 slots RPC/trex.events 불변). 모듈은 기존 RabbitMQ 연결로 발행, notifier가 단일 소비자.
- 변경 3 — **모듈별 emitter**: core/orb/tsl/kdh 각 repo에 thin 알람 발신기(기존 연결 재사용, ~십수 줄). 인벤토리 CRITICAL 집합부터 배선.
- 변경 4 — **trex-notifier P1**: 알람 수집 → 등급 판정 → 중복/상관 억제 → 텔레그램(CRITICAL 즉시 / WARN 다이제스트) + 하트비트.
## 영향 범위
변경이 미칠 코드/문서/운영 범위를 명시한다.
- 코드 영향:
- trex-core: 알람 emitter(신규) + `trex.alerts` 채널 선언(채택 시). 기존 execution/safety_sell 등의 CRITICAL 지점에서 emit 호출.
- trex-orb-v1 / trex-tsl-v1 / kis-data-hub: 각 thin emitter + 주요 알람 지점 emit 호출(각 repo 별 batch).
- trex-notifier: classifier/deduplicator/sender/heartbeat 구현 + 진입점 + 의존성(텔레그램·AMQP 라이브러리) 확정.
- 문서 영향:
- trex-core `docs/interface-contract.md` — 알람 envelope + `trex.alerts` 채널 계약 신설.
- trex-core `docs/runtime-flows.md` — 알람 발신 흐름 추가(baseline 갱신).
- trex-notifier `docs/runtime-flows.md` — TBD였던 전송 방식 확정 반영.
- `docs/discovery/index.md` 갱신.
- 운영 영향:
- 운영자에게 텔레그램 알람 수신 채널·등급 정책이 신규 운영 절차로 추가(런북).
- 봇 토큰·화이트리스트 chat id 운영 비밀로 관리(notifier 1곳).
# 5. 요구사항 목록
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
## 기능 요구사항
- FR-1: 표준 알람 envelope 스키마(`source/severity/code/title/fields/occurred_at/schema_version`)를 trex-core가 wire 계약으로 정의하고 `docs/interface-contract.md`에 명시한다.
- FR-2: 각 모듈(core/orb/tsl/kdh)이 인벤토리 CRITICAL 집합(ALARM-INVENTORY.md §A)의 조건 발생 시 envelope로 알람을 발신한다.
- FR-3: trex-notifier가 알람을 수집해 등급별로 처리한다 — CRITICAL은 텔레그램 즉시 푸시, WARN은 일일 다이제스트, INFO는 제외(로그만).
- FR-4: trex-notifier가 동일 근본원인의 상관 알람을 묶고(예: 시세 허브 장애 → 다모듈), 반복 알람을 rate-limit/중복 억제한다.
- FR-5: trex-notifier가 하트비트(dead-man's switch)로 데몬 침묵=장애를 탐지·알린다.
- FR-6: trex-core `docs/runtime-flows.md`에 알람 발신 흐름을, trex-notifier `docs/runtime-flows.md`에 확정 전송 방식을 반영한다(baseline 갱신).
## 비기능 요구사항
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
- NFR-1: 알람 채널은 **가산·격리** — 기존 wire 계약(slots RPC, trex.events) 스키마를 변경하지 않는다. orb/tsl/kdh 기존 동작 무회귀.
- NFR-2: 보안 — 텔레그램 봇 토큰 시크릿은 notifier 1곳에만, 수신 대상은 화이트리스트(사장님 chat id). core/orb/tsl/kdh에 외부 시크릿 유입 없음.
- NFR-3: 신뢰성 — 알람 경로(notifier/RabbitMQ)가 죽어도 모듈 본업(매매)과 로그 저장은 영향 없음. notifier는 SPOF가 아니다.
- NFR-4: 모듈측 비용 최소 — emitter는 thin(기존 연결 재사용), `trex_core` import 없이 envelope 규약만 공유.
## 범위 경계
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
- In Scope
- 알람 envelope 계약 + 전송 채널 + 모듈 emitter(CRITICAL 집합) + trex-notifier P1(수집·등급·상관/중복억제·텔레그램·하트비트).
- Out of Scope
- **인바운드 명령 게이트**(텔레그램으로 시스템 제어) — P2/P3 후속 별도 discovery(보안·인가·감사).
- **로그 저장/Loki 적재** — 인프라 슈퍼(Vector/Promtail) 담당. notifier 경유 금지.
- 인벤토리 ~100개 code 전량 배선 — CRITICAL 우선, WARN 다이제스트는 점진. INFO 제외.
- 후보 C(core가 외부 푸시 직접 호출), 기존 slots RPC/trex.events 스키마 변경.
# 6. 리스크/가정 목록
불확실성과 전제 조건을 분리해서 적는다.
## 리스크
- R-1 (Medium): 알람 폭풍 — 장애 시 다모듈·매사이클 알람이 쏟아져 알람 피로 유발.
- 완화 방안: notifier 상관/중복억제 + rate-limit(FR-4) + 등급화(CRITICAL만 즉시 푸시).
- R-2 (Medium): 알람의 역설 — 알람 채널이 의존하는 인프라(RabbitMQ/notifier) 장애 시 그 경로 알람 미전달.
- 완화 방안: 하트비트(침묵=장애, FR-5). 매매 안전망(core safety-sell)은 알람과 독립이라 알람 실패가 거래 보호를 깨지 않음.
- R-3 (Low): 보안 — 봇 토큰 유출 시 알람 수신 노출(P1은 읽기 방향이라 제어 위험은 없음).
- 완화 방안: 토큰 1곳 격리(NFR-2), 화이트리스트, 인바운드 명령은 범위 밖(P3로 분리).
## 가정
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
- A-1: 모듈들은 이미 RabbitMQ 연결을 보유(core=broker, orb/tsl=intent/slots RPC)하므로 AMQP emitter는 기존 연결 재사용으로 저비용이다.
- A-2: 알람은 저빈도 이벤트라 전용 채널의 트래픽·복잡도 부담이 작다.
- A-3: 인프라 로그 슈퍼(Vector/Promtail→Loki)는 알람과 별개로 존재/구성된다(로그 저장은 본 범위 밖).
- A-4: trex-notifier는 단일 컨테이너 내 bare 프로세스로 다른 모듈과 공존 배포된다.
## 오픈 질문
답을 아직 모르는 항목을 적는다.
- OQ-1: 모듈→notifier 알람 전송 방식 — **전용 `trex.alerts` AMQP exchange**(권고: 실시간·격리·소비자 상관억제) vs **로그-tee**(슈퍼가 alert 라인 tee, 모듈측 코드 0이나 Loki 의존·지연).
- 상태: Answered — **전용 `trex.alerts` AMQP exchange로 확정**(사장님 2026-06-21).
- 처리 방안: trex-notifier는 `trex.alerts` 소비자. 각 모듈은 기존 RabbitMQ 연결로 발행. 가산·격리(orb/tsl 기존 계약 불변). trex-notifier 의존성(AMQP 클라이언트)·runtime-flows에 반영.
- 종료 조건: 충족됨.
- OQ-2: P1 즉시-푸시(CRITICAL paging) 대상 code 집합 확정 — ALARM-INVENTORY.md §A 제안 집합을 사장님이 검토해 실제 폰을 울릴 항목을 확정.
- 상태: Answered — **ALARM-INVENTORY.md §A 전체(약 25개 code)를 P1 CRITICAL paging으로 채택**(사장님 2026-06-21). 항목 축소 없음 — 알람 폭풍은 항목 삭제가 아니라 notifier 상관/rate-limit(FR-4)으로 제어.
- 처리 방안: §A의 core/tsl/orb/kdh CRITICAL code 전체가 즉시-푸시 대상. delivery에서 각 모듈 emitter가 해당 code 배선.
- 종료 조건: 충족됨.
# 7. 초기 성공 기준
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다.
- S-1: 확정된 CRITICAL 조건 발생 시 운영자 텔레그램에 알람이 도달한다(엔드투엔드).
- S-2: 동일 근본원인의 다모듈 알람이 1건으로 묶이고, 반복 알람이 rate-limit된다(폭풍 억제).
- S-3: 데몬 침묵(크래시/정지) 시 하트비트 부재로 장애가 탐지된다. 기존 wire 계약·매매 동작은 무회귀.
## 측정 방식 및 데이터 출처
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
- S-1 측정 방식: CRITICAL 조건 주입(테스트/시뮬) → 텔레그램 수신 확인 / 데이터 출처: notifier 로그 + 텔레그램 메시지.
- S-2 측정 방식: 다모듈 동시 알람·반복 알람 주입 → 묶임/억제 단언 / 데이터 출처: notifier 상관·rate-limit 테스트.
- S-3 측정 방식: 데몬 하트비트 중단 시뮬 → 장애 알람 발생 단언 + 기존 wire/매매 회귀 스위트 / 데이터 출처: notifier 테스트 + 각 모듈 회귀 테스트(interface-contract diff 0).
# 8. REQ로 넘기기 전 확인 체크
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다.
- [x] 범위(In Scope/Out of Scope) 확정
- 기준: P1(아웃바운드 알람)만 In Scope, 명령 게이트·로그 저장 제외에 사장님 동의 — 확정(토론 합의)
- [x] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~S-3 측정안 합의
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: 현재 High 리스크 없음(R-1/R-2 Medium) — 폭풍억제·하트비트 책임자(notifier service-dev) 지정
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-1(전송=`trex.alerts` AMQP)·OQ-2(paging=§A 전체) 모두 Answered(사장님 2026-06-21)
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: confirm-discovery 시 팀장이 CONFIRMED_BY로 기록 — 팀장(Claude)
# 9. 파일 처리 결과
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다.
- 처리 결과: 생성
- 생성 경로: docs/discovery/dcy-033_20260621_suite-alarm-delivery-notifier.md
- 참조 문서:
- /home/genichin/workspace/trex-lead/ALARM-INVENTORY.md (인벤토리 스윕 — 현재상태/요구사항 근거)
- docs/discovery/dcy-029_20260616_operational-observability-logging-and-pnl-view.md (운영 관측성 인접)
- docs/interface-contract.md, docs/runtime-flows.md (envelope/채널 계약 + 발신 흐름 baseline 갱신 대상)
# 10. 사용자 결정 필요 항목 요약
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다.
## DECIDE
- (해소) 알람 전송 방식 = 전용 `trex.alerts` AMQP exchange — 사장님 확정(2026-06-21, OQ-1).
- (해소) P1 CRITICAL paging 집합 = ALARM-INVENTORY.md §A 전체(약 25개 code) — 사장님 확정(2026-06-21, OQ-2).
## CONFIRM
- (확정) P1(아웃바운드 알람)만 범위, 인바운드 명령 게이트·로그 저장은 후속 분리 — 사장님 확정(2026-06-21).
## DATA
- {{DATA_TELEGRAM_BOT}} — 텔레그램 봇 토큰 + 수신 chat id(화이트리스트). delivery 단계 필요(사장님 BotFather 생성).
# 11. Discovery Freeze
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다.
## 섹션 참조
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
## Freeze 플래그
- Handoff Decision: REQ Drafting 진행 가능
- Handoff Rationale: confirm-discovery 완료. OQ-1(전송=`trex.alerts` AMQP)·OQ-2(paging=§A 전체) 모두 사장님 확정 → Open OQ 0건. 범위(P1 아웃바운드)·성공기준·리스크 책임자 확정. DATA(텔레그램 봇 토큰)는 delivery 단계 필요(비차단). 단 **구현(delivery)은 첫 tsl REL(M2) 이후 큐잉** — draft-req는 병렬 가능하나 delivery 우선순위는 M2 뒤.
- Ready for REQ Drafting: true
- Confirmed By: 팀장(Claude)
- Confirmed At (KST): 2026-06-21 (KST)
+1
View File
@@ -38,3 +38,4 @@
| dcy-030_20260616_slippage-limit-price-tick-size-snapping | 20260616 | slippage 지정가 KRX 호가단위 미반영 → KIS 거부 (tick-size 스냅, 상한 준수 방향, issue #28) | [Discovery](./dcy-030_20260616_slippage-limit-price-tick-size-snapping.md) | confirmed |
| dcy-031_20260619_event-publisher-connection-reliability | 20260619 | 이벤트 발행 연결 신뢰성 — idle heartbeat 사망 후 첫 발행 유실 + DLQ 가시성 (OPS-005) | [Discovery](./dcy-031_20260619_event-publisher-connection-reliability.md) | confirmed |
| dcy-032_20260621_fill-missing-price-cost-basis-backfill | 20260621 | 체결가 누락(브로커 평균가 0/None) → 슬롯 평단·원장 cost basis 0 오염(무경고·영구) → 지정가 백필 + 로깅 + 알람 검토 | [Discovery](./dcy-032_20260621_fill-missing-price-cost-basis-backfill.md) | confirmed |
| dcy-033_20260621_suite-alarm-delivery-notifier | 20260621 | 제품군 알람 전달 체계 — 모듈 발신 + 중앙 notifier(trex-notifier) → 텔레그램(P1 아웃바운드). envelope 계약+전송(trex.alerts AMQP 확정)+등급/상관억제/하트비트 | [Discovery](./dcy-033_20260621_suite-alarm-delivery-notifier.md) | confirmed |
@@ -0,0 +1,82 @@
# req-101: 알람 발신 흐름 baseline 문서 정합
- Status: Approved
- Type: Documentation
- Priority: Medium
- Owner: trex-core-dev
## Intent
- dcy-033 요구에 따라 `docs/runtime-flows.md`에 trex-core 알람 발신 흐름을 추가하고, `docs/interface-contract.md`의 알람 채널 섹션(REQ-098/099)이 완성되도록 Change Log를 갱신해 baseline 정합을 완료한다.
- REQ-098·099·100 구현으로 변경되는 아키텍처 흐름(알람 발행 경로)이 baseline 문서에 반영되지 않으면 향후 Discovery·batch design에서 오래된 문서를 참조하는 오류가 생긴다.
## Requirement
- `docs/runtime-flows.md`에 `Flow: 알람 발신 (trex.alerts, dcy-033)` 섹션을 추가해야 한다. 섹션에는 다음 내용이 포함되어야 한다.
- 알람 발생 → `AlarmEmitter.publish_alarm()` → `MessagingTransport.publish_alarm_event()` → `trex.alerts` exchange → routing key `alarm.{severity}.{source}` 흐름.
- best-effort 비차단 정책: 발행 실패 시 ERROR 로그 후 core 본업 계속.
- transport 미연결 시 silent skip.
- trex-core CRITICAL code 8종 발생 지점 목록(코드 이름과 모듈 경로).
- 기존 `trex.events` 발행 흐름과의 차이(알람 채널은 독립 exchange, mandatory=False, DLQ 없음).
- `docs/interface-contract.md`의 Change Log에 dcy-033/REQ-098~101 배치로 인한 알람 채널 추가 항목을 기록해야 한다.
- 두 문서의 `Last Updated (KST)` 헤더를 구현 batch 완료 시점으로 갱신해야 한다.
## Acceptance Criteria
1. Given REQ-098·099·100이 구현 완료됐을 때, When `docs/runtime-flows.md`를 검토하면, Then `Flow: 알람 발신 (trex.alerts, dcy-033)` 또는 동등한 제목의 섹션이 존재해야 한다.
2. Given 해당 섹션이 추가됐을 때, When 내용을 확인하면, Then AlarmEmitter → MessagingTransport → trex.alerts exchange의 발행 흐름과 best-effort 비차단 정책이 명시되어야 한다.
3. Given 섹션이 추가됐을 때, When core CRITICAL code 목록을 확인하면, Then ALARM-INVENTORY §A core 행의 8개 code(`fill_price_unresolvable`, `sell_skipped_no_balance`, `core_holding_broker_flat`, `ledger_delivery_failed`, `kill_switch_fired`, `publish_unavailable_after_reconnect`/`dlq_publish_failed`, `portfolio_hub_unavailable`, `schema_verification_failed`/`migration_failed`)와 발생 모듈 경로가 모두 기술되어야 한다.
4. Given 섹션이 추가됐을 때, When `trex.events` 발행 흐름과 비교하면, Then 알람 채널(`trex.alerts`)이 별도 exchange이고 `mandatory=False`·DLQ 없는 점이 명시되어야 한다(기존 흐름 섹션 내용은 변경 없음).
5. Given REQ-098~101 구현이 완료됐을 때, When `docs/interface-contract.md`의 Change Log를 확인하면, Then dcy-033/알람 채널 추가에 해당하는 항목이 기록되어야 한다.
6. Given 두 baseline 문서가 갱신됐을 때, When `Last Updated (KST)` 헤더를 확인하면, Then 구현 batch 완료 시점(KST)으로 갱신되어야 한다.
## Impacted Area
- Docs:
- `docs/runtime-flows.md` — 알람 발신 흐름 섹션 추가(기존 섹션 불변)
- `docs/interface-contract.md` — Change Log 항목 추가, Last Updated 갱신
- Modules: 코드 변경 없음(이 REQ는 문서 정합만)
- Tests: 문서 전용 REQ이므로 코드 테스트 불필요. batch verification에서 섹션 존재·내용 수동 검토.
## Notes
- 이 REQ는 REQ-098·099·100 구현이 완료된 동일 batch에서 함께 처리한다(문서는 구현 후 정합).
- `docs/runtime-flows.md`의 기존 섹션(데몬 lifecycle, slots RPC, intent admission, 이벤트 발행/구독, safety-sell 등)은 변경하지 않는다.
- dcy-033 FR-3(baseline 갱신) 대응.
- dcy-033 성공기준 S-3(기존 wire 계약 무회귀) — 이 REQ 자체는 문서 전용이지만 기존 섹션 diff=0이 AC-4에서 암묵 검증된다.
## Change Log
### CHG-20260621-01
- Date: 2026-06-21
- Author: trex-core-dev
#### Change Summary
- REQ-101 신규 생성(Proposed). dcy-033 FR-3 대응: 알람 발신 흐름 baseline 문서 정합.
#### Intent
- REQ-098~100 구현으로 도입된 알람 발신 흐름을 runtime-flows.md와 interface-contract.md에 반영해 baseline을 최신 상태로 유지한다.
#### Acceptance Criteria Delta
- Added: AC-1~6 (신규 생성)
- Updated: (없음)
- Removed: (없음)
#### Impacted Area Delta
- Docs:
- `docs/runtime-flows.md` 알람 발신 섹션 추가
- `docs/interface-contract.md` Change Log + Last Updated 갱신
- Modules: 없음
- Tests: 없음(문서 전용)
#### Delivery Trace Delta
- Batches: (미정, M2 이후 큐잉 — REQ-098~100과 동일 batch)
- Releases: (미정)
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: REQ-098~100 구현 batch에서 마지막 단계로 처리
#### Validation Plan
- `docs/runtime-flows.md`에 알람 발신 섹션 존재 확인(grep)
- 섹션 내용 수동 검토(AlarmEmitter 흐름, 8 code 목록, mandatory=False, DLQ 없음)
- `docs/interface-contract.md` Change Log 항목 추가 확인
- 기존 섹션 diff=0 확인(git diff 수동 검토)
@@ -0,0 +1,87 @@
# req-099: trex.alerts AMQP exchange 토폴로지 + 가산·격리 불변식
- Status: Approved
- Type: Integration
- Priority: High
- Owner: trex-core-dev
## Intent
- 알람 전달 채널로 전용 `trex.alerts` topic exchange를 선언하고, 토폴로지(exchange 타입·이름·라우팅 규약)와 소비자/발행자 역할을 `docs/interface-contract.md`에 명시한다.
- 이 채널은 **가산·격리** 원칙을 따른다: 기존 exchange/큐(trex.events/trex.intent.req/trex.slots.req/trex.control.req 등) 스키마와 동작은 일절 변경하지 않으며, `trex.alerts`는 독립 추가 채널로만 존재한다.
- trex-core 데몬이 기동 시 `trex.alerts` exchange를 멱등 선언(declare)하도록 `MessagingTransport.declare_topology()`를 확장한다.
## Requirement
- trex-core `MessagingTransport.declare_topology()`는 아래 AMQP 엔티티를 멱등으로 선언해야 한다.
- exchange: `trex.alerts`, type=`topic`, durable=`True`
- 모듈(trex-core/orb/tsl/kdh)은 기존 RabbitMQ 연결에서 `trex.alerts` exchange로 알람 envelope를 발행한다. routing key는 `alarm.{severity}.{source}` (예: `alarm.critical.trex-core`).
- trex-notifier는 `trex.alerts` exchange의 단일 소비자로서, 바인딩 패턴 `alarm.#`(또는 severity별 `alarm.critical.#`)으로 자신의 큐를 바인딩해 수신한다(notifier 큐 선언·바인딩은 trex-notifier 소유).
- trex-core가 알람을 발행(publish)할 때: `mandatory=False`(소비자 없어도 unroutable 에러 없이 조용히 drop) — notifier 다운이 core 발행을 막지 않도록 한다.
- 기존 AMQP 토폴로지(trex.events topic exchange, trex.events.dlq, trex.intent.req/trex.slots.req/trex.control.req 큐, trex.direct/trex.broadcast exchange)는 수정하지 않는다.
- `docs/interface-contract.md`의 알람 채널 섹션(REQ-098)에 exchange 타입·이름·라우팅 규약·발행자/소비자 역할을 명시한다.
## Acceptance Criteria
1. Given trex-core 데몬이 기동될 때, When `declare_topology()`가 호출되면, Then `trex.alerts`(topic, durable=True) exchange가 멱등 선언되어야 한다(이미 존재하면 no-op).
2. Given `trex.alerts` exchange가 선언됐을 때, When 기존 exchange/큐 목록(trex.events, trex.events.dlq, trex.intent.req, trex.slots.req, trex.control.req, trex.direct, trex.broadcast)을 확인하면, Then 이들 엔티티의 type·durable·인수(arguments)가 변경되지 않아야 한다.
3. Given trex-core가 알람을 발행할 때, When notifier 큐가 바인딩되지 않아 unroutable인 경우, Then `mandatory=False`이므로 발행이 조용히 성공하고(silent drop) TransportUnavailableError 또는 UnroutableError가 발생하지 않아야 한다.
4. Given `docs/interface-contract.md` 알람 채널 섹션(REQ-098)이 추가됐을 때, When 토폴로지 명세를 확인하면, Then exchange 이름(`trex.alerts`), 타입(`topic`), durable 여부, routing key 형식(`alarm.{severity}.{source}`), 발행자(모든 모듈), 단일 소비자(trex-notifier)가 명시되어야 한다.
5. Given `declare_topology()`가 `trex.alerts` exchange를 포함하도록 수정됐을 때, When 기존 topology 선언 관련 단위 테스트를 실행하면, Then 기존 테스트가 모두 통과하고 신규 exchange 선언에 대한 단위 테스트가 추가되어야 한다.
6. Given trex-core가 알람을 발행할 때, When publishing 시 transport가 `disconnected` 또는 `reconnecting` 상태이면, Then 알람 발행은 silently skip되어야 하며 core 본업(슬롯 전이·매매 집행)을 블로킹하지 않아야 한다(best-effort 비차단 — REQ-100 연계).
## Impacted Area
- Docs: `docs/interface-contract.md` — 알람 채널 섹션 토폴로지 내용 추가(REQ-098 섹션 확장)
- Modules:
- `trex_core/adapters/broker/rabbitmq.py` 또는 topology 선언 담당 모듈 — `trex.alerts` exchange 선언 추가
- `trex_core/application/` messaging 관련 모듈 — 알람 발행 헬퍼(REQ-100 연계)
- Tests:
- `tests/adapters/` 또는 `tests/application/` — `declare_topology` 단위 테스트에 `trex.alerts` exchange 선언 assertion 추가
- 기존 topology 선언 테스트 회귀 확인
## Notes
- `trex.alerts` exchange는 `trex.events`와 별개로 선언된다. 둘은 다른 exchange 이름을 가지므로 바인딩·라우팅이 교차하지 않는다.
- notifier가 없는 환경(개발/테스트)에서도 core는 정상 동작해야 한다. `mandatory=False` + best-effort 발행이 이를 보장한다.
- RabbitMQ topology attribute 충돌(기존 큐·exchange와 durable/type 불일치) 시 PRECONDITION_FAILED — 이는 기존 엔티티 수정 금지 불변식을 위반한 경우로, 이 REQ의 AC-2로 사전 방지된다.
- orb/tsl/kdh는 자신의 RabbitMQ 연결에서 `trex.alerts` exchange로 직접 발행한다. core는 이들의 연결을 관여하지 않는다(각 모듈 REQ 범위).
- trex-notifier의 큐 이름·바인딩 패턴·prefetch 등은 trex-notifier repo REQ에서 정의한다.
- dcy-033 NFR-1(가산·격리) 대응 — AC-2가 직접 검증 기준.
- dcy-033 성공기준 S-3(기존 wire 계약 무회귀) 연계.
## Change Log
### CHG-20260621-01
- Date: 2026-06-21
- Author: trex-core-dev
#### Change Summary
- REQ-099 신규 생성(Proposed). dcy-033 FR-2 대응: `trex.alerts` AMQP exchange 토폴로지 + 가산·격리 불변식.
#### Intent
- `trex.alerts` 전용 exchange를 선언하고, 기존 AMQP 계약을 건드리지 않으면서 알람 채널을 가산적으로 추가한다.
#### Acceptance Criteria Delta
- Added: AC-1~6 (신규 생성)
- Updated: (없음)
- Removed: (없음)
#### Impacted Area Delta
- Docs:
- `docs/interface-contract.md` 알람 채널 섹션(REQ-098)에 토폴로지 내용 추가
- Modules:
- topology 선언 담당 모듈에 `trex.alerts` exchange 추가
- Tests:
- topology 선언 단위 테스트 신규 assertion 추가
#### Delivery Trace Delta
- Batches: (미정, M2 이후 큐잉)
- Releases: (미정)
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: REQ-098/100/101과 단일 batch 편성 예정
#### Validation Plan
- RabbitMQ mock/stub에서 `declare_topology()` 호출 후 `trex.alerts` exchange 선언 assertion
- 기존 exchange/큐 선언 테스트 회귀 통과 확인
- `mandatory=False` 발행 시 unroutable 상황에서 예외 없음 확인(단위 테스트)
@@ -0,0 +1,108 @@
# req-100: trex-core 알람 emitter — CRITICAL code 발신 배선
- Status: Approved
- Type: Integration
- Priority: High
- Owner: trex-core-dev
## Intent
- ALARM-INVENTORY.md §A trex-core 행의 CRITICAL alarm code 8종을 trex-core 소스 내 발생 지점에서 REQ-098 envelope 형식으로 `trex.alerts` exchange(REQ-099)에 발행하도록 배선한다.
- 알람 발행은 **best-effort 비차단**: 발행 실패(transport 단절·예외)가 core 본업(슬롯 전이·매매 집행·원장 기록)을 막지 않아야 한다.
- trex-core는 텔레그램 토큰 등 외부 시크릿을 보유하지 않는다. core는 `trex.alerts` exchange에 발행만 하고, 텔레그램 전달은 trex-notifier 책임이다.
## Requirement
- trex-core는 아래 8개 CRITICAL code의 발생 지점에서 REQ-098 envelope을 `trex.alerts` exchange로 발행해야 한다.
| code | 발생 위치 (ALARM-INVENTORY §A 기준) | 조건 |
|---|---|---|
| `fill_price_unresolvable` | `execution.py:261` | 체결가 미해결 → 슬롯 PENDING 고착 |
| `sell_skipped_no_balance` | `execution.py:128` | 매도 dispatch skip (브로커 잔고 0) → PENDING_SELL 고착 |
| `core_holding_broker_flat` | `reconciliation.py:47` | 슬롯 보유인데 브로커 잔고 0 (정산 불일치) |
| `ledger_delivery_failed` | `ledger_outbox.py:218-245` | 원장 영구 실패/데드레터/409 충돌 |
| `kill_switch_fired` | `killswitch.py:72` | 긴급 청산 발동 |
| `publish_unavailable_after_reconnect` / `dlq_publish_failed` | `rabbitmq.py:262,293` | 재연결 후에도 발행 불가 / DLQ 발행 실패 |
| `portfolio_hub_unavailable` | `portfolio.py:156` / `snapshot_scheduler.py:90,98` | 포트폴리오/현금 정보 부실화 |
| `schema_verification_failed` / `migration_failed` | `reset.py:152` | 리셋·마이그레이션 무결성 실패 |
- 알람 emitter는 application 레이어에 `AlarmEmitter` (또는 동등한 이름의 thin class/함수)로 구현하며, `MessagingTransport` 포트를 통해 발행한다(직접 pika 호출 금지 — 의존성 역전 유지).
- 발행 실패(TransportUnavailableError 또는 기타 예외) 시 ERROR 로그를 남기고 예외를 삼킨다(re-raise 금지). 슬롯 전이·주문 집행·원장 기록 경로는 알람 발행 실패에 영향받지 않는다.
- envelope `source` = `"trex-core"`, `severity` = `"critical"`, `schema_version` = `"1"`. `occurred_at`은 발행 시점 KST ISO-8601.
- `fields`는 code별 최소 컨텍스트를 포함한다(예: `fill_price_unresolvable`이면 `{"slot_id": "...", "symbol": "..."}`). 빈 dict도 허용.
- trex-core 코드 어디에도 텔레그램 봇 토큰, chat_id 등 외부 알람 시크릿을 import·로드·참조하지 않는다.
## Acceptance Criteria
1. Given trex-core가 기동돼 `MessagingTransport`가 연결된 상태에서, When 위 8개 code 각각의 조건이 발생하면, Then 해당 code를 담은 REQ-098 envelope(`source="trex-core"`, `severity="critical"`, `schema_version="1"`)이 routing key `alarm.critical.trex-core`로 `trex.alerts` exchange에 발행되어야 한다.
2. Given 알람 emitter가 `publish_alarm()`을 호출할 때, When `MessagingTransport`가 `disconnected` 또는 `reconnecting` 상태(TransportUnavailableError 발생)이면, Then 예외가 caller로 전파되지 않고 ERROR 로그만 남겨야 한다.
3. Given 알람 emitter가 `publish_alarm()`을 호출할 때, When 발행 중 임의 예외가 발생하면, Then 예외가 caller로 전파되지 않고 ERROR 로그만 남겨야 한다.
4. Given `kill_switch_fired`·`ledger_delivery_failed`·`fill_price_unresolvable` 등 알람 발행 지점이 슬롯 전이·매매 집행·원장 기록 경로 중간에 위치할 때, When 알람 발행이 예외로 실패해도, Then 해당 경로(슬롯 전이·집행·원장 기록)는 정상 완료되거나 기존 에러 처리 흐름을 따라야 한다(알람 발행 실패가 core 본업 실패를 유발하지 않음).
5. Given trex-core 코드베이스 전체를 탐색할 때, When 텔레그램 봇 토큰·chat_id·TELEGRAM_* 환경변수를 참조하는 코드를 찾으면, Then 해당 참조가 0건이어야 한다.
6. Given 알람 emitter 구현이 완료됐을 때, When `AlarmEmitter`(또는 동등 컴포넌트)의 단위 테스트를 실행하면, Then 8개 code 각각에 대해 (a) 정상 발행 시 올바른 envelope 필드 검증, (b) transport 예외 발생 시 re-raise 없음 + 로그 기록 두 케이스가 모두 통과해야 한다.
7. Given `AlarmEmitter`가 `MessagingTransport` 포트를 통해 구현됐을 때, When application 레이어 코드를 확인하면, Then pika 라이브러리를 직접 import하는 코드가 없어야 한다(의존성 역전 준수).
8. Given domain 레이어(`trex_core/domain/`) 코드를 확인할 때, When `AlarmEmitter` 또는 messaging import를 탐색하면, Then domain 레이어에 messaging/transport/infra 관련 import가 없어야 한다(domain 순수성 불변식).
## Impacted Area
- Docs: `docs/interface-contract.md` — 알람 채널 섹션에 core emitter source/routing key 명시(REQ-098 섹션 내용)
- Modules:
- `trex_core/application/` — `AlarmEmitter` (또는 동등 thin class/함수) 신규 추가
- `trex_core/application/execution.py` — `fill_price_unresolvable`, `sell_skipped_no_balance` 발행 배선
- `trex_core/application/reconciliation.py` — `core_holding_broker_flat` 발행 배선
- `trex_core/application/ledger_outbox.py` — `ledger_delivery_failed` 발행 배선
- `trex_core/application/killswitch.py` — `kill_switch_fired` 발행 배선
- `trex_core/adapters/broker/rabbitmq.py` — `publish_unavailable_after_reconnect` / `dlq_publish_failed` 발행 배선
- `trex_core/adapters/portfolio/` 또는 `trex_core/application/snapshot_scheduler.py` — `portfolio_hub_unavailable` 발행 배선
- `trex_core/runtime/reset.py` — `schema_verification_failed` / `migration_failed` 발행 배선
- `trex_core/runtime/daemon.py` — `AlarmEmitter` 인스턴스 생성·주입
- Tests:
- `tests/application/` — `AlarmEmitter` 단위 테스트 (8 code × 2 케이스 최소 16 테스트)
- 기존 execution/reconciliation/killswitch/ledger_outbox 테스트 회귀 확인
## Notes
- `AlarmEmitter`는 application 레이어에 두고 `MessagingTransport` 포트만 주입받는다. 테스트에서는 `InMemoryTransport` 또는 mock transport를 주입해 결정적 단위 테스트를 작성한다.
- `publish_unavailable_after_reconnect` / `dlq_publish_failed`는 rabbitmq.py 어댑터에서 발생하지만, 알람 emitter를 어댑터 내부에 두면 의존성이 역전되므로 처리 방식을 batch design에서 결정한다(예: 어댑터가 콜백을 호출하거나, 이벤트를 상위 레이어로 전달하는 방식).
- `portfolio_hub_unavailable`는 `portfolio.py:156`과 `snapshot_scheduler.py:90,98` 두 곳에서 발생 가능 — 중복 발행 억제는 notifier의 rate-limit 책임이므로 core는 각 발생 지점에서 독립 발행한다.
- `reset.py`는 daemon이 아닌 CLI 진입점(`trex-core-reset`)에서 실행된다. `MessagingTransport` 미연결 가능성 있음 — transport가 None이면 발행 skip + 로그(batch design에서 확정).
- dcy-033 성공기준 S-1(CRITICAL 조건 발생 시 텔레그램 도달) 전제 조건: core emitter가 올바른 envelope을 `trex.alerts`에 발행.
- dcy-033 NFR-3(core에 외부 시크릿 유입 없음) 대응 — AC-5가 직접 검증 기준.
- INFO·WARN code 배선은 이 REQ 범위 밖. CRITICAL 우선(dcy-033 범위 경계).
## Change Log
### CHG-20260621-01
- Date: 2026-06-21
- Author: trex-core-dev
#### Change Summary
- REQ-100 신규 생성(Proposed). dcy-033 FR-2(core 부분) 대응: ALARM-INVENTORY §A core CRITICAL 8종 emitter 배선.
#### Intent
- trex-core가 소유한 CRITICAL alarm code 발생 지점에서 표준 envelope을 `trex.alerts` exchange로 발행하는 알람 emitter를 구현한다.
#### Acceptance Criteria Delta
- Added: AC-1~8 (신규 생성)
- Updated: (없음)
- Removed: (없음)
#### Impacted Area Delta
- Docs:
- `docs/interface-contract.md` 알람 섹션 core emitter 내용 추가
- Modules:
- application 레이어 AlarmEmitter 신규 + 8개 발생 지점 배선
- Tests:
- AlarmEmitter 단위 테스트 신규 추가
#### Delivery Trace Delta
- Batches: (미정, M2 이후 큐잉)
- Releases: (미정)
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: REQ-098/099/101과 단일 batch 편성 예정
#### Validation Plan
- AlarmEmitter 단위 테스트 8 code × 2 케이스 통과
- 기존 execution/reconciliation/killswitch 테스트 회귀 통과
- domain 레이어 messaging import 0건 확인(grep)
- TELEGRAM_* 환경변수 참조 0건 확인(grep)
@@ -0,0 +1,85 @@
# req-098: 알람 envelope wire 계약 정의
- Status: Approved
- Type: Interface
- Priority: High
- Owner: trex-core-dev
## Intent
- 제품군 모든 모듈(core/orb/tsl/kdh)이 알람을 발신할 때 사용할 표준 메시지 envelope 스키마를 trex-core가 wire 계약으로 정의하고 `docs/interface-contract.md`에 명시한다.
- 이 계약은 trex-notifier가 알람을 수집·분류할 때의 파싱 기준이 되며, 다른 모듈 repo(orb/tsl/kdh)와 trex-notifier가 이 문서를 단일 출처(source of truth)로 참조한다.
- 기존 wire 계약(slots RPC/trex.events/trex.control.req)은 일절 변경하지 않는다.
## Requirement
- trex-core는 알람 envelope의 표준 JSON 스키마를 아래 필드로 정의하고, `docs/interface-contract.md`에 신규 섹션 `Interface: trex.alerts (알람 채널, dcy-033)`으로 버전드 명시해야 한다.
- `source` :: str — 알람 발신 모듈 식별자 (예: `"trex-core"`, `"trex-orb-v1"`, `"trex-tsl-v1"`, `"kis-data-hub"`)
- `severity` :: `"critical"` | `"warn"` | `"info"` — 등급 (소문자 리터럴)
- `code` :: str — 알람 식별 코드 (예: `"kill_switch_fired"`, `"fill_price_unresolvable"`)
- `title` :: str — 사람이 읽는 1행 요약
- `fields` :: dict[str, any] — 선택 구조화 컨텍스트 (예: `{"slot_id": "...", "symbol": "..."}`)
- `occurred_at` :: ISO-8601 datetime string (UTC 또는 KST+09:00 offset 포함)
- `schema_version` :: str — 현재 값 `"1"` (semver 주 버전 문자열; 소비자는 미인식 버전 무시·경고)
- 계약 섹션에 routing key 규약(`alarm.{severity}.{source}`)과 exchange 이름(`trex.alerts`)을 함께 기술한다.
- 이 스키마를 변경(필드 추가 포함)하면 `schema_version`을 올리거나 additive 변경 여부를 명시하도록 Update Trigger를 문서에 포함한다.
## Acceptance Criteria
1. Given `docs/interface-contract.md`에 `Interface: trex.alerts (알람 채널, dcy-033)` 섹션이 존재하지 않을 때, When REQ-098 구현을 완료하면, Then 해당 섹션이 위 7개 필드(source/severity/code/title/fields/occurred_at/schema_version)의 JSON 스키마 명세를 포함해야 한다.
2. Given 섹션이 추가됐을 때, When 명세를 검토하면, Then `severity` 값이 `"critical"` | `"warn"` | `"info"` 세 리터럴로 제한되고, `schema_version="1"`이 현재 버전으로 명시되어야 한다.
3. Given 명세가 추가됐을 때, When 기존 interface-contract.md의 다른 섹션(slots RPC/trex.events/trex.control.req/strategy_state)을 검토하면, Then 기존 섹션의 내용이 변경되지 않아야 한다(가산·격리 불변 — diff는 신규 섹션 추가뿐).
4. Given envelope 스키마 명세가 완성됐을 때, When routing key 규약을 확인하면, Then `alarm.{severity}.{source}` 형식이 명시되어야 한다.
5. Given 명세가 추가됐을 때, When Update Trigger를 확인하면, Then 알람 envelope 스키마 변경(breaking/additive 모두) 시 이 문서를 갱신해야 한다는 트리거가 포함되어야 한다.
6. Given 다른 모듈 repo(orb/tsl/kdh) 개발자가 알람 emitter를 구현할 때, When `docs/interface-contract.md`의 알람 계약 섹션을 읽으면, Then 자체 `trex_core` import 없이 envelope 구성에 필요한 모든 정보(필드·타입·필수 여부·schema_version·routing key)를 얻을 수 있어야 한다.
## Impacted Area
- Docs: `docs/interface-contract.md` — 알람 채널 신규 섹션 추가(기존 섹션 불변)
- Modules: 코드 변경 없음(이 REQ는 문서 계약만; emitter 구현은 REQ-100)
- Tests: `docs/interface-contract.md` diff가 기존 섹션 변경 0임을 확인하는 검증(batch verification 단계에서 수동 검토 또는 diff assertion)
## Notes
- 이 REQ는 **계약 문서화**만 담당한다. envelope을 실제로 직렬화·발행하는 Python 코드(알람 emitter)는 REQ-100이 담당한다.
- `fields` 값은 opaque dict — core/notifier 모두 내용을 강제 파싱하지 않는다. 각 code별 권장 fields는 ALARM-INVENTORY.md를 참조한다.
- `occurred_at` 형식: 권장은 KST+09:00 offset-aware ISO-8601(기존 코어 로깅 관례와 통일). UTC도 수용.
- 다른 모듈(orb/tsl/kdh)은 `trex_core`를 import하지 않는다. 이 계약은 **문서 규약**이며 런타임 공유 코드가 아니다(Discovery 제약 A-1).
- trex-notifier가 `schema_version`이 미인식인 envelope을 수신하면 경고 로그 후 무시한다(소비자 책임 — notifier REQ 범위).
- dcy-033 성공기준 S-1(CRITICAL 알람 텔레그램 도달)·S-3(wire 계약 무회귀)의 선행 계약.
## Change Log
### CHG-20260621-01
- Date: 2026-06-21
- Author: trex-core-dev
#### Change Summary
- REQ-098 신규 생성(Proposed). dcy-033 FR-1 대응: 알람 envelope wire 계약 문서화.
#### Intent
- dcy-033(제품군 알람 전달 체계) confirmed Discovery에서 요구하는 표준 알람 envelope 스키마를 trex-core wire 계약으로 정식화한다.
#### Acceptance Criteria Delta
- Added: AC-1~6 (신규 생성)
- Updated: (없음)
- Removed: (없음)
#### Impacted Area Delta
- Docs:
- `docs/interface-contract.md` 신규 섹션 추가
- Modules:
- 코드 변경 없음
- Tests:
- interface-contract diff 검증(기존 섹션 불변 확인)
#### Delivery Trace Delta
- Batches: (미정, M2 이후 큐잉)
- Releases: (미정)
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 편성 시 REQ-099/100/101과 묶어 단일 batch로 처리 예정
#### Validation Plan
- `docs/interface-contract.md`에 알람 채널 섹션이 추가됐는지 수동 확인
- 기존 섹션 diff=0 확인(grep 또는 수동 비교)
- 7개 필드·severity 리터럴·schema_version·routing key 규약이 모두 명시됐는지 체크리스트 검토
+7 -1
View File
@@ -74,7 +74,7 @@
## Next Requirement ID
- Current: `REQ-098`
- Current: `REQ-102`
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
## Requirement Register
@@ -178,9 +178,15 @@
| REQ-095 | 체결가 0/None 지정가 백필 가드 (슬롯 평단 + 원장 price) | Exception | Implemented | High | trex-core-dev | [req-095](./Exception/req-095_fill-price-zero-backfill-guard.md) |
| REQ-096 | 체결가 백필·0가격 통과 WARN 로깅 | Exception | Implemented | High | trex-core-dev | [req-096](./Exception/req-096_fill-price-backfill-warn-logging.md) |
| REQ-097 | 알람(alert) 능력 검토 스파이크 산출물 | Documentation | Implemented | Medium | trex-core-dev | [req-097](./Documentation/req-097_alert-capability-spike.md) |
| REQ-098 | 알람 envelope wire 계약 정의 | Interface | Approved | High | trex-core-dev | [req-098](./Interface/req-098_alarm-envelope-wire-contract.md) |
| REQ-099 | trex.alerts AMQP exchange 토폴로지 + 가산·격리 불변식 | Integration | Approved | High | trex-core-dev | [req-099](./Integration/req-099_alarm-exchange-topology.md) |
| REQ-100 | trex-core 알람 emitter — CRITICAL code 발신 배선 | Integration | Approved | High | trex-core-dev | [req-100](./Integration/req-100_core-alarm-emitter.md) |
| REQ-101 | 알람 발신 흐름 baseline 문서 정합 | Documentation | Approved | Medium | trex-core-dev | [req-101](./Documentation/req-101_alarm-baseline-alignment.md) |
## Recent Change Log Summary
- 2026-06-21 :: REQ-098·099·100·101 **Proposed → Approved** (confirm-req, 팀장): dcy-033 확정 설계(trex.alerts AMQP·envelope 7필드·core CRITICAL 8종) 충실 반영, AC 측정가능, 가산·격리 불변식(098 AC-3/099 AC-2)·best-effort 비차단(099 AC-6/100 AC-2~4)·시크릿 격리(100 AC-5)·의존성역전/domain 순수성(100 AC-7/8) 강제 확인. emitter-in-adapter 경계·reset.py transport는 batch design 확정. 다음=suggest-batch-reqs→draft-batch. **delivery는 첫 tsl REL(M2) 이후 큐잉.**
- 2026-06-21 :: REQ-098·099·100·101 신규 생성(Proposed, draft-req dcy-033): 제품군 알람 전달 체계 — trex-core 소유분. REQ-098 알람 envelope wire 계약 정의(Interface, FR-1, dcy-033 S-1/S-3), REQ-099 trex.alerts AMQP exchange 토폴로지+가산·격리 불변식(Integration, NFR-1/S-3), REQ-100 core CRITICAL emitter 8종 배선(Integration, FR-2 core 부분, NFR-2/3, S-1), REQ-101 알람 발신 흐름 baseline 정합(Documentation, FR-3). orb/tsl/kdh emitter·trex-notifier는 각 repo REQ로 별도 진행. delivery는 M2 이후 큐잉.
- 2026-06-21 :: **BAT-048 confirm-batch-verification**(팀장): REQ-095·096 **Approved → Implemented**. 구현=`execution.py` `_resolve_fill_price` 단일화(application) + `_record_ledger` resolved_price + skip+강WARN. 검증=신규 21 단위 테스트(매수 16+매도 5), **전체 575 passed/47 skipped/0 fail**(팀장 독립 재실행) + ruff clean. 독립 opus QC=PASS-WITH-NOTES → NOTE-1(ruff 2건)·NOTE-2(매도 경로 테스트) 정정 완료. skip 경로 부분변이 없음·price=0 원장 미생성 QC 확인. wire/LedgerTrade 스키마 불변(AC-5). **통합 3건 미실행 결정**(영속/전송 어댑터 diff 0이라 타 서비스 DB migration/truncate 침습 회피, KIS avg=None은 sandbox 결정적 재현 불가, 실가동 시 REQ-096 WARN으로 탐지). REQ-097=스파이크 Implemented(알람 impl 후속). 다음=커밋(git-ops)→M2.
- 2026-06-21 :: **BAT-048 design review**(팀장+사장님): D-1 백필 지점=application 단일화·D-2 폴백=skip+강WARN **팀장 승인**(REQ-095 AC-6 구체화). **D-3 사장님 결정: 우선 백필+WARN 로깅(REQ-095/096)만 구현, 알람은 BAT-048 완료 후 별도 진행+M2 포함 여부 그때 결정**. REQ-097 스파이크 산출물(design.md) 제출로 **Implemented**(후보 A 권고/B=wire변경 제외/C=M3). BAT-048 구현 범위=REQ-095+096.
- 2026-06-21 :: REQ-095·096·097 **Proposed → Approved** (confirm-req, 팀장): 세 REQ가 dcy-032 FR-1~4·NFR-1~3을 충실히 반영, AC 측정 가능, design Deferred 항목(백필 지점·지정가 부재 폴백·알람 채널) 명시 확인. REQ-095 AC-6(지정가 부재 폴백)은 design 확정 후 구체화(사장님 합의 deferral). 다음=suggest-batch-reqs → draft-batch.