Files
trex-core/docs/batches/bat-049_20260621_alarm-system-dcy-033/implementation.md
T
genichinandClaude Opus 4.8 d4c5fcbba1 feat(alarm): trex-core 알람 emitter + trex.alerts exchange (dcy-033 BAT-049, Implemented)
AlarmEmitter(application, best-effort 비차단)+trex.alerts topic exchange 가산·격리+8 CRITICAL code 배선. 608 passed, opus QC PASS, 팀장 e2e(텔레그램 HTTP200) 통과. REQ-098~101 Implemented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
2026-06-21 19:25:55 +09:00

6.8 KiB
Raw Blame History

BAT-049 Implementation

  • Status: completed
  • Batch: BAT-049

Plan Summary

REQ-098(문서) → REQ-099(topology+transport) → REQ-100(AlarmEmitter+배선) → REQ-101(runtime-flows 정합) 순서로 구현. design.md의 4개 Key Decision을 그대로 따름.

Changed Files

파일 변경 유형 내용
docs/interface-contract.md 섹션 추가 + 헤더 갱신 Interface: trex.alerts (알람 채널, dcy-033) 신규 섹션 (7필드 스키마, routing key, topology, CRITICAL code 목록, Update Trigger). Change Log + Last Updated 갱신. 기존 섹션 불변.
trex_core/adapters/messaging/topology.py 추가 ALERTS_EXCHANGE = "trex.alerts" + alarm_routing_key() 함수
trex_core/adapters/messaging/rabbitmq.py 확장 declare_topology() trex.alerts 선언 추가. publish_alarm_event() 신규. set_alarm_callback() + _fire_alarm_callback() 신규. 콜백 호출 배선 (publish_unavailable_after_reconnect, dlq_publish_failed). Callable import 추가.
trex_core/adapters/messaging/in_memory.py 확장 _alarm_events 리스트 추가. publish_alarm_event() stub + alarm_events() 조회.
trex_core/application/messaging.py 확장 MessagingTransport Protocol에 publish_alarm_event() 추가.
trex_core/application/alarm_emitter.py 신규 AlarmEmitter 클래스. envelope 구성, KST occurred_at, 예외 삼킴+ERROR 로그, None transport skip.
trex_core/application/execution.py 배선 AlarmEmitter import + alarm_emitter 파라미터 + sell_skipped_no_balance + fill_price_unresolvable 발행 배선.
trex_core/application/reconciliation.py 배선 AlarmEmitter import + alarm_emitter 파라미터 + core_holding_broker_flat 발행 배선.
trex_core/application/ledger_outbox.py 배선 AlarmEmitter import + alarm_emitter 파라미터 + ledger_delivery_failed 3지점(conflict_blocked, permanent_failure, dead_lettered) 배선.
trex_core/application/killswitch.py 배선 AlarmEmitter import + alarm_emitter 파라미터 + kill_switch_fired 배선.
trex_core/application/snapshot_scheduler.py 배선 AlarmEmitter import + alarm_emitter 파라미터 + portfolio_hub_unavailable 2지점(whoami_failed, get_portfolios_failed) 배선. reconcile_core_holdings 호출에 alarm_emitter 전달.
trex_core/runtime/reset.py 배선 _emit_reset_alarm() 헬퍼 추가. run_reset() alarm_emitter 파라미터 추가. migration 예외 + schema_ok=False 지점 배선.
trex_core/runtime/daemon.py 조립 AlarmEmitter import. alarm_emitter 생성 (event_publisher 이후). set_alarm_callback() 주입. LedgerWriter, KillSwitchService, SnapshotScheduler, ExecutionService 생성 시 alarm_emitter 주입. 서비스 생성 순서 재배치(alarm_emitter 생성 후 주입 보장).
tests/application/test_alarm_emitter.py 신규 AlarmEmitter 단위 테스트 29개 (8 code × envelope 검증, 8 code × TransportUnavailableError 삼킴, 8 code × 임의 예외 삼킴, None transport skip, fields default, routing key, pika 미import, TELEGRAM 미참조).
tests/adapters/messaging/test_topology.py 확장 test_alerts_exchange_constant, test_alarm_routing_key_format 추가.
tests/adapters/messaging/test_in_memory_transport.py 확장 test_publish_alarm_event_records_event, test_publish_alarm_event_raises_when_unavailable 추가.
docs/runtime-flows.md 섹션 추가 + 헤더 갱신 Flow: 알람 발신 (trex.alerts, dcy-033) 신규 섹션 (발행 흐름, best-effort 정책, 8종 code 목록, trex.events 차이표). 기존 섹션 불변.

Design Decisions Implemented

KD-1 (rabbitmq.py 어댑터 alarm 콜백 패턴):

  • RabbitMQTransport.__init__에 alarm_callback: Optional[Callable] 파라미터 추가.
  • set_alarm_callback() setter 추가 (늦은 주입 — daemon.py에서 AlarmEmitter 생성 후 바인딩).
  • _fire_alarm_callback() 내부 헬퍼: callback None guard + 예외 삼킴.
  • publish_event() retry 실패 시 publish_unavailable_after_reconnect 콜백 호출.
  • _publish_dlq() DLQ 실패 시 dlq_publish_failed 콜백 호출.

KD-2 (reset.py transport None 안전):

  • _emit_reset_alarm(alarm_emitter, code, title, fields) 헬퍼: None guard + 예외 삼킴.
  • run_reset() 시그니처에 alarm_emitter: Optional[object] = None 추가.
  • migration 예외(raise 전)와 schema_ok=False 지점에서 헬퍼 호출.

KD-3 (publish_alarm_event 별도 메서드):

  • trex.alerts exchange, mandatory=False, delivery_mode=1 (non-persistent).
  • MessagingTransport Protocol + RabbitMQTransport + InMemoryTransport 모두 구현.

KD-4 (AlarmEmitter 인터페이스):

  • AlarmEmitter(transport: Optional[MessagingTransport]).
  • publish_alarm(code, title, *, fields=None, severity="critical").
  • source="trex-core", schema_version="1" 고정.
  • occurred_at = KST ISO-8601.

Execution Log

  1. docs/interface-contract.md — trex.alerts 섹션 추가 (REQ-098 완료)
  2. topology.py — ALERTS_EXCHANGE + alarm_routing_key() (REQ-099 일부)
  3. messaging.py — publish_alarm_event Protocol 추가 (REQ-099 일부)
  4. rabbitmq.py — declare_topology + publish_alarm_event + 콜백 패턴 (REQ-099 완료 + REQ-100 KD-1)
  5. in_memory.py — publish_alarm_event stub (REQ-099 완료)
  6. alarm_emitter.py — AlarmEmitter 신규 (REQ-100 핵심)
  7. execution.py — fill_price_unresolvable + sell_skipped_no_balance (REQ-100 배선)
  8. reconciliation.py — core_holding_broker_flat (REQ-100 배선)
  9. ledger_outbox.py — ledger_delivery_failed (REQ-100 배선)
  10. killswitch.py — kill_switch_fired (REQ-100 배선)
  11. snapshot_scheduler.py — portfolio_hub_unavailable (REQ-100 배선)
  12. reset.py — schema_verification_failed + migration_failed (REQ-100 배선)
  13. daemon.py — AlarmEmitter 생성·주입 + 서비스 생성 순서 재배치 (REQ-100 조립)
  14. runtime-flows.md — 알람 발신 흐름 섹션 추가 (REQ-101 완료)
  15. 테스트 작성 (test_alarm_emitter.py 29개, topology/in_memory 추가)

Validation

pytest tests/application/test_alarm_emitter.py -v
→ 29 passed, 0 failed, 0 skipped

pytest --tb=short -q (전체)
→ 608 passed, 47 skipped, 0 failed

ruff check trex_core/ tests/
→ All checks passed!

Remaining Risks

  • R-1(Low): trex.alerts exchange 선언은 실 RabbitMQ에서만 검증 가능 (단위에서 mock). 팀장이 실인프라 통합 테스트 수행 예정.
  • R-2(Low): daemon.py에서 event_publisher가 InMemoryTransport인 경우 set_alarm_callback()이 없어 hasattr guard 처리됨 (InMemoryTransport는 콜백 불필요).
  • R-3(Low): reset.py의 alarm_emitter 파라미터가 main()(CLI 진입점)에서 None으로 실행됨 — 의도된 동작이나 실인프라 CLI 시나리오에서 알람 미발행.