배경
기존 #28은 missing pricing과 budget notification 연결을 한 이슈에 묶었다. 그러나 notification은 Week 3 atomic reservation/reconciliation의 domain event를 소비해야 하며, Week 1 legacy evaluator에 먼저 연결하면 lifecycle 구현 후 다시 작성하게 된다.
이 이슈는 pricing resolution과 분리하여 실제 budget/accounting 상태 전이에 notification을 연결한다.
목표
atomic accounting transition
→ threshold decision
→ notification event
→ deduplicated best-effort handler
notification 실패는 회계 결과와 provider 응답을 바꾸지 않는다.
구현 범위
- #27의 typed
BudgetKey와 BudgetWindow를 그대로 사용한다.
- #37의 commit/block/reconciliation 결과에서 threshold decision을 만든다.
- 같은 policy/target/window/threshold event를 중복 발행하지 않는다.
- threshold가 HALF → WARNING → EXCEEDED로 상승하면 각 단계는 한 번씩 발행한다.
- 새 window에서는 같은 threshold를 다시 발행할 수 있다.
BudgetThreshold.NONE은 발행하지 않는다.
- 동일 idempotency key의 duplicate commit은 알림을 다시 만들지 않는다.
- handler마다 실패를 격리하고 다음 handler를 계속 실행한다.
- handler 실패는 accounting 상태를 rollback하거나 provider 응답을 실패시키지 않는다.
- bounded/sanitized error hook을 제공한다.
전달 보장
- MVP delivery는 best-effort다.
- 회계 상태와 notification 전송을 하나의 durable transaction으로 묶지 않는다.
- durable outbox가 없으므로 exactly-once delivery를 주장하지 않는다.
- 재시작 뒤 재전송, Slack/email adapter는 후속 범위다.
필수 테스트
Acceptance criteria
제외 범위
- durable outbox
- Slack/email/webhook 실제 전송 adapter
- 재시작 후 자동 replay
- 사용자별 임의 notification rule language
의존관계
Source
배경
기존 #28은 missing pricing과 budget notification 연결을 한 이슈에 묶었다. 그러나 notification은 Week 3 atomic reservation/reconciliation의 domain event를 소비해야 하며, Week 1 legacy evaluator에 먼저 연결하면 lifecycle 구현 후 다시 작성하게 된다.
이 이슈는 pricing resolution과 분리하여 실제 budget/accounting 상태 전이에 notification을 연결한다.
목표
notification 실패는 회계 결과와 provider 응답을 바꾸지 않는다.
구현 범위
BudgetKey와BudgetWindow를 그대로 사용한다.BudgetThreshold.NONE은 발행하지 않는다.전달 보장
필수 테스트
Acceptance criteria
evaluate()호출이 아니라 atomic accounting 결과를 소비한다.제외 범위
의존관계
Source