# 기존 runtime → 신규 messaging platform cutover (MSG-015) ## 왜 기계적 매핑이 안 되는가 두 outbox 모델의 enum 이름이 겹치는데 의미가 반대다. | 모델 | retryable | terminal | |---|---|---| | 기존 `OutboxEventStatus` | `FAILED` (`next_attempt_at` 보유) | `DEAD` | | 신규 `OutboxStatus` | `AMBIGUOUS` | `FAILED`, `EXHAUSTED` | 이름으로 매핑하면 **확정 거절이 무한 재시도**가 되고 **불확정이 park**된다. 그래서 application은 자기 어휘(`OutboxPublishOutcome`)만 쓰고, 변환은 bridge adapter가 한다. ## 지금 반영된 것 - `OutboxPublishOutcome` — `CONFIRMED` / `AMBIGUOUS` / `REJECTED_BEFORE_SEND` / `REJECTED_AFTER_BROKER`. application이 소유하는 canonical 결과 타입이며, "리턴 or throw"만 가능한 기존 어댑터를 위해 `OutboxMessagePublishPort.publishForOutcome`의 default가 `CONFIRMED`를 돌려준다. - `APPLICATION_DOES_NOT_DEPEND_ON_THE_MESSAGING_PLATFORM` ArchUnit 규칙 — application-core가 `dev.caskeleton.messaging..`를 import하면 빌드가 깨진다. - 반대 방향(신규 `PublishResult` → application outcome) 매핑 규칙을 테스트로 고정. ## 남은 것 - `messaging-platform-bridge` outbound leaf: validated application event → platform envelope, `PublishResult` → `OutboxPublishOutcome`. registry에 leaf를 추가하는 변경이라 별도 커밋. - golden contract 테스트: event/message ID, type, schema revision, partition/order/correlation/ causation/tenant/trace, payload digest, wire version이 bytes 단위로 보존되는지. - 단일 publication authority: 기존 `OutboxPublicationAuthority` fence를 재사용해 writer/relay가 동시에 ACTIVE가 되지 않도록. **dual write/publish는 금지** — 한 business fact가 두 durable store와 두 relay로 나가는 상태가 cutover에서 가장 위험하다. - 첫 cutover 범위는 **transport만** 교체(저장소는 기존 유지). storage migration은 shadow read → authority switch → old backlog drain 순서로 별도 release.