# BAT-029 Implementation - Status: completed (M2(b) 청산 브로커 정본은 BAT-δ carry-over) - Batch: BAT-029 ## 구현 요약 - M1(REQ-061 slot_id 계약)·M2(a)(REQ-062 divergence reconcile)·M3(REQ-063 slots 단일 뷰) 구현. M2(b)(execution dispatch 매도 수량 브로커 정본)는 라이브 집행 변경이라 BAT-δ 로 이월. ## 변경 내역 ### M1 — REQ-061 intent 계약 slot_id (커밋 별도) - BuyIntent slot_id 추가(portfolio_id Optional), `_admit_buy` 자산 중심 우선 분기, `_admit_buy_asset_centric` slot_id(canonical)/portfolio_id(legacy dual-accept) 슬롯 해소 + symbol resolve, messaging buy 파싱 slot_id 수용. ### M2(a) — REQ-062 divergence reconcile - `domain/slot/repository.py`·in_memory·postgresql: `list_holding_slots(instance_key)`. - `application/reconciliation.py`: `reconcile_core_holdings(slot_repo, slot_service, broker, instance_key)` — A(코어 holding/broker 0)=슬롯 error+WARN, C(수량 불일치)=WARN(상태 불변), B(broker 보유/코어 없음)=WARN. 자동 청산 없음(issue #15). - `application/snapshot_scheduler.py`: run_once 에서 자산 중심 모드 + slot_service 시 reconcile_core_holdings 호출(best-effort). daemon: slot_service 배선. ### M3 — REQ-063 slots 단일 뷰 - `application/portfolio.py` `slot_to_dict`: `universe_detached`(retired_at not null) 추가 → `trex-core slots`/envelope 자산 포지션 단일 뷰에 tombstone/soft-retire 표시. ### M2(b) — carry-over (BAT-δ) - execution dispatch 매도 수량 `int(slot.qty)` → 브로커 잔고 정본: **라이브 집행 루프 동작 변경**이라 cutover(BAT-δ, 자산 중심 default 전환)와 같은 게이트에서 처리. ## 테스트 - 신규: test_holding_divergence(4: A/C/ok/B), test_asset_centric_admission(slot_id 매수·unknown_slot 포함 10). - 회귀: 기존 sell(slot_id)·reconcile_positions·snapshot 무파손. ## 검증 실행 - 단위 전체: 393 passed / 36 skipped / 1 deselected. ruff clean. - PG-gated(trex_core_test): persistence 18 passed(list_holding_slots/reconcile 포함).