feat(slot): BAT-029 M2(a)/M3 — holding 불일치 reconcile + slots 단일 뷰 (dcy-020/REQ-062·063)

- REQ-062 divergence: list_holding_slots(protocol+in_memory+PG) +
  reconcile_core_holdings(A=코어 holding/broker0→error, C=수량 불일치→warning,
  B=broker 보유/코어 없음→warning; 자동 청산 없음 issue #15). snapshot_scheduler
  자산 중심 모드에서 best-effort 호출, daemon slot_service 배선.
- REQ-063: slot_to_dict 에 universe_detached(retired_at) 추가 — slots 단일 뷰 tombstone 표시.
- 신규 테스트: test_holding_divergence(4).

M2(b) 청산 수량 브로커 정본(execution dispatch)은 라이브 집행 변경 → BAT-δ carry-over.

검증: 단위 393 passed / 36 skipped, PG-gated 18 passed, ruff clean. legacy 무파손.
REQ-061/062/063 Approved 유지(γ 경로 도입 검증; default 전환·M2(b) 는 BAT-δ).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 12:18:56 +09:00
co-authored by Claude Opus 4.8
parent b80407098e
commit 78a743a2ef
10 changed files with 215 additions and 10 deletions
@@ -115,6 +115,12 @@ class InMemorySlotRepository:
if s.instance_key == instance_key and s.status in self._ACTIVE
)
def list_holding_slots(self, instance_key: str) -> list[Slot]:
return [
s for s in self._slots.values()
if s.instance_key == instance_key and s.status is SlotState.HOLDING
]
def reconcile_universe(
self, instance_key: str, trading_date: date, universe_portfolio_ids: set[str],
*, allow_retire: bool = True,
+5
View File
@@ -75,6 +75,11 @@ class SlotRepository(Protocol):
{holding, pending_buy, pending_sell} (dcy-020/REQ-059 M cap, D2)."""
...
def list_holding_slots(self, instance_key: str) -> list[Slot]:
"""Return holding slots for this instance (dcy-020/REQ-062 divergence
reconcile)."""
...
def reconcile_universe(
self, instance_key: str, trading_date: date, universe_portfolio_ids: set[str],
*, allow_retire: bool = True,