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:
@@ -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)
|
||||
Reference in New Issue
Block a user