Files
trex-core/docs/batches/bat-028_20260610_universe-reconcile-and-active-admission/design.md
T
genichinandClaude Opus 4.8 8bd9495c4b docs(batch): draft-batch BAT-028 (dcy-020 BAT-β reconcile+admission) + design 확정
REQ-058(universe reconcile)+REQ-059(자산 admission FCFS/M)+REQ-060(max_active_slots).
design 확정(사용자 결정):
- D1: config 게이트 이중경로 (max_active_slots 설정 시 자산중심, 미설정 시 legacy pool;
  운영 데몬은 cutover 전까지 pool 무중단)
- D2: "활성"(M 캡) = holding+pending_buy+pending_sell
- D3: 대량소멸 가드 = N→0 + 급감(50%↓) 경고
batches/index BAT-028(standard, planning) 등록.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 11:04:57 +09:00

3.8 KiB

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 보류 + 경고(생성은 진행).