# BAT-028 Design - Status: confirmed (D1/D2/D3 확정 — 2026-06-10, 사용자 결정) - Batch: BAT-028 ## 설계 목표 - 슬롯 lifecycle 을 universe reconcile 로, admission 을 자산 중심 FCFS+M 캡으로 전환하되, **라이브 데몬 무중단**을 위해 config 게이트로 도입하고 legacy pool 경로를 보존한다(안 B). ## 핵심 결정 — config 게이트 이중 경로 (권장) - `DaemonInstanceConfig.max_active_slots: Optional[int]`. - **설정됨 → 자산 중심 모드**: 슬롯 생성=universe reconcile, admission=자산 슬롯 활성화+M 캡. - **미설정 → legacy pool 모드**: 기존 preallocation + find_allocatable_slot (현행 그대로). - 운영 데몬은 cutover(BAT-δ) 전까지 `max_active_slots` 미설정 → **legacy 유지, 무중단**. 자산 중심은 테스트/단계 전환에서 활성. - 대안(전면 switch)은 라이브 admission 을 한 번에 바꿔 위험 → 안 B에서 비채택. ## REQ-058 reconcile - repo 계약 추가: `reconcile_universe(instance_key, trading_date, universe_portfolio_ids: set[str]) -> ReconcileResult`. - universe 에 있고 슬롯 없음(instance_key, portfolio_id) → 자산 슬롯 생성(inactive, portfolio_id NOT NULL, 멱등). - 슬롯 있고 universe 에서 빠짐 + status==inactive(idle/closed 별칭) + retired_at IS NULL → `retired_at=now` (soft-retire). - 빠짐 + holding/pending → **tombstone**(retired_at 미설정, 유지; 신규 활성 후보 제외는 admission/조회에서 retired_at·universe 기준). - 호출: `SnapshotScheduler.run_once()` 의 `store.swap(items)` 직후, **자산 중심 모드일 때만**. degrade(get_portfolios 실패) → reconcile 건너뜀. **대량소멸 가드**: `len(universe) == 0` 또는 급감(예: 직전 대비 50%↓) 시 retire 보류 + 경고(생성은 진행). - 단일 writer: reconcile 쓰기는 인스턴스 스코프 advisory lock 안에서. ## REQ-059 admission (자산 중심 분기) - `IntentService._admit_buy`: 모드 분기. - 자산 중심: `repo.find_slot_by_portfolio(instance_key, trading_date, portfolio_id)` 로 그 자산의 슬롯을 잡는다. - 슬롯 없음(universe 밖) → reject `unknown_portfolio`. - 슬롯이 이미 활성(pending_buy/holding/pending_sell) → reject `already_active`(멱등). - 활성 수 계산 `count_active(instance_key, trading_date)` ≥ M → reject `capacity_full`(무치환). - else inactive→pending_buy 활성화(인스턴스 lock + 재시도). over-cap(M 축소)도 같은 경로로 신규만 차단. - legacy: 기존 find_allocatable_slot 경로. - **"활성(active)" 정의(M 캡 대상)**: `holding + pending_buy + pending_sell`(= inactive/error 아닌 포지션 점유 상태). pending_sell 은 청산 진행 중이나 아직 포지션 보유 → 캡에 포함. (체크포인트 D2) ## REQ-060 config - `max_active_slots` per-instance TOML 로드(REQ-048 계열). 자산 중심 모드에서 admission 이 M 으로 사용. legacy 모드는 무시. ## 테스트 설계 - reconcile: 생성 멱등 / 비활성 soft-retire / 보유·pending tombstone / degrade·대량소멸 보류 (in-memory + PG). - admission(자산 중심): 활성화 / capacity_full / already_active / over-cap / 동시성 경합 (인스턴스 lock). - config: max_active_slots 로드 / 모드 게이트. - legacy 회귀: max_active_slots 미설정 시 기존 pool admission/preallocation 무파손(기존 테스트 green). ## 체크포인트 결정 (2026-06-10, 사용자 확정) 1. **D1 = config 게이트 이중 경로.** `max_active_slots` 설정 시 자산 중심, 미설정 시 legacy pool. 운영 무중단(cutover 전 pool 유지). 2. **D2 = "활성"(M 캡) = `holding + pending_buy + pending_sell`** (inactive/error 아닌 포지션 점유 상태 전부). 3. **D3 = N→0 + 급감 경고.** universe 가 0이면 retire 보류; 직전 대비 급감(50%↓)도 retire 보류 + 경고(생성은 진행).