--- id: "req-0011" status: approved discovery: "dcy-0011" criteria: create: - "ac-0038" - "ac-0039" update: [] retire: [] satisfy: [] --- # SELL 체결 이벤트 owner 보존과 unroutable 차단 ## Objective SELL 주문 admission에서 확정된 전략 owner를 체결 event routing까지 보존하여 해당 전략 queue로만 전달한다. owner를 결정할 수 없는 SELL 체결은 정상 전략 event로 위장해 `_` routing key로 발행하지 않고 명시적 전달 실패로 관측하되, 이미 성공한 broker 체결, slot 전이, ledger outbox 처리는 되돌리지 않는다. ## Scope - `HOLDING → PENDING_SELL` admission에서 non-empty `SellIntent.sell_strategy_id`, slot에 보존된 non-empty `sell_strategy_id`, non-empty `strategy_id` 순서로 SELL event owner를 확정하고 기존 durable `sell_strategy_id`에 저장한다. Whitespace-only 값은 owner로 인정하지 않는다. - 확정된 owner는 기존 durable slot field를 통해 dispatch 전후와 daemon 재시작 후에도 보존하고, full fill 및 partial fill 후 external cancel의 `slot_filled_sell` routing hint로 사용한다. - zero-fill cancel 또는 reject는 fill event를 만들지 않고 기존 rollback 계약을 유지하며 확정 owner를 `HOLDING`에 보존한다. 이후 명시적 새 owner가 없는 SELL admission은 이 owner를 다시 사용하고, 명시적 새 owner가 있으면 그것으로 교체한다. - owner 미확정 SELL 체결은 `trex.events` 정상 전략 topic 또는 DLQ에 synthetic owner message를 publish하지 않는다. stable reason code `sell_event_owner_missing`, tick-level counter, ERROR log, best-effort alarm으로 구분해 관측한다. - 정상 owner가 있는 event의 publish 실패도 금융 처리를 rollback하지 않는다. 기존 payload의 `slot_id`, `event_type`, `sequence_at`을 변경하지 않으며, 재시작·반복 polling에서 같은 terminal execution의 event 재발행을 새로 보장하지 않는다. - `docs/interface-contract.md`와 `docs/runtime-flows.md`의 RabbitMQ event routing 및 slot event 발행 구간에 owner 선택, pre-publish skip, 전달 실패 의미를 구현과 함께 동기화한다. ## Constraints - `sell_strategy_id`와 `strategy_id`의 의미를 합치거나 legacy mapping을 추가하지 않는다. - 새 persistence column이나 strategy-event outbox를 추가하지 않는다. restart 보장은 terminal 처리 전에 durable `PENDING_SELL` row를 다시 읽는 범위이며, terminal commit 뒤 실패한 event의 재생 저장소를 이번 Line에서 도입하지 않는다. - publisher confirm과 1회 reconnect/retry, RabbitMQ mandatory publish 및 기존 transport DLQ 계약은 유지한다. - alarm payload와 log는 secret, credential, connection string 또는 raw exception text를 포함하지 않는다. - production broker 주문, production lifecycle, production daemon 재기동은 검증에서 실행하지 않는다. ## Non-Goals - 이미 발생한 과거 DLQ message의 복구 또는 재발행 - 전략 consumer 구현 변경 및 전역 exactly-once 보장 - RabbitMQ topology, queue TTL/max-length, exchange 이름 변경 - BUY event owner 계약 변경 - durable strategy-event outbox 또는 운영 replay command 추가