feat(slot): BAT-028 M2/M3 — universe reconcile + 자산 중심 admission (dcy-020/REQ-058·059)
config 게이트 이중 경로(max_active_slots 설정 시 자산 중심, 미설정 시 legacy pool). 운영 데몬은 cutover(BAT-δ) 전까지 pool 무중단. - repository: find_slot_by_portfolio·count_active(holding+pending_buy+pending_sell)· reconcile_universe(생성 멱등/soft-retire/tombstone, allow_retire 가드) — protocol+in_memory+PG (PG: ON CONFLICT (instance_key, portfolio_id) WHERE portfolio_id IS NOT NULL) - snapshot_scheduler: run_once swap 직후 _reconcile_slots(빈 universe skip, ≥50%↓ retire 보류) - intents: _admit_buy 모드 분기 + _admit_buy_asset_centric(활성화/already_active/ capacity_full/unknown_portfolio/over-cap, 인스턴스 lock) - daemon: instance_key·max_active_slots·reconcile_enabled·trading_date_fn 배선 - 신규 테스트: test_universe_reconcile(6), test_asset_centric_admission(6), PG reconcile(1) 검증: 단위 387 passed / 35 skipped, PG-gated 17 passed, ruff clean. legacy 무파손. REQ-058/059/060 은 Approved 유지(자산 중심 경로 도입 검증; default 전환·cutover 는 BAT-δ). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+16
-5
@@ -1,16 +1,27 @@
|
||||
# BAT-028 Implementation
|
||||
|
||||
- Status: not-started
|
||||
- Status: completed
|
||||
- Batch: BAT-028
|
||||
|
||||
## 구현 요약
|
||||
- (run-batch-delivery 진입 시 작성) design.md 체크포인트(D1/D2/D3) 확정 후 채운다.
|
||||
- config 게이트 이중 경로(D1). `max_active_slots` 설정 시 자산 중심(reconcile + FCFS/M admission), 미설정 시 legacy pool. 운영 데몬은 cutover 전까지 pool 무중단.
|
||||
|
||||
## 변경 내역
|
||||
- (대기)
|
||||
### M1 — REQ-060 config (max_active_slots)
|
||||
- `runtime/config.py`: `DaemonInstanceConfig.max_active_slots`/`DaemonConfig.max_active_slots`(Optional[int]) + from_toml(>0 검증) + from_config 전파.
|
||||
|
||||
### M2 — REQ-058 universe reconcile
|
||||
- `domain/slot/repository.py`(protocol)·`in_memory_repository.py`·`adapters/persistence/postgresql.py`: `find_slot_by_portfolio(instance_key, portfolio_id)`, `count_active(instance_key)`(holding+pending_buy+pending_sell, D2), `reconcile_universe(instance_key, trading_date, universe, *, allow_retire=True)` — 신규 자산 생성(멱등; PG ON CONFLICT 부분 unique), 비활성 soft-retire, 보유/pending tombstone. PG insert status='idle'(별칭).
|
||||
- `application/snapshot_scheduler.py`: `run_once` swap 직후 `_reconcile_slots`(reconcile_enabled 시) — 빈 universe skip, 급감(≥50%↓) 시 allow_retire=False+경고(D3). instance_key/trading_date_fn 주입.
|
||||
|
||||
### M3 — REQ-059 자산 중심 admission
|
||||
- `application/intents.py`: `IntentService(instance_key, max_active_slots)` + `_admit_buy` 모드 분기 + `_admit_buy_asset_centric`(자산 슬롯 활성화, already_active/capacity_full/unknown_portfolio/over-cap, 인스턴스 lock 재시도).
|
||||
- `runtime/daemon.py`: IntentService/SnapshotScheduler 에 instance_key·max_active_slots·reconcile_enabled·trading_date_fn 배선. `datetime` import 추가.
|
||||
|
||||
## 테스트
|
||||
- (대기)
|
||||
- 신규: tests/domain/slot/test_universe_reconcile.py(6), tests/application/test_asset_centric_admission.py(6), test_postgresql_repository.py::test_universe_reconcile_pg(PG).
|
||||
- legacy 회귀: max_active_slots 미설정 시 기존 pool 경로 무파손.
|
||||
|
||||
## 검증 실행
|
||||
- (대기)
|
||||
- 단위 전체: 387 passed / 35 skipped / 1 deselected. ruff clean.
|
||||
- PG-gated(trex_core_test): persistence 17 passed(reconcile ON CONFLICT 부분 인덱스 + find_by_portfolio/count_active + soft-retire 포함).
|
||||
|
||||
+35
-4
@@ -1,13 +1,44 @@
|
||||
# BAT-028 Verification
|
||||
|
||||
- Status: not-started
|
||||
- Status: verified (배치 범위)
|
||||
- Batch: BAT-028
|
||||
|
||||
## Acceptance Mapping
|
||||
- (draft-batch-verification / confirm-batch-verification 단계에서 작성)
|
||||
|
||||
### REQ-060 (max_active_slots config)
|
||||
| AC | 상태 | Evidence |
|
||||
|----|------|----------|
|
||||
| TOML 로드 + admission M 사용 | ✅ | config from_toml + test_asset_centric_admission(M 캡) |
|
||||
| 미설정 → legacy pool | ✅ | test_legacy_pool_mode_unaffected |
|
||||
| >0 검증 | ✅ | from_toml raise (단위) |
|
||||
|
||||
### REQ-058 (universe reconcile)
|
||||
| AC | 상태 | Evidence |
|
||||
|----|------|----------|
|
||||
| 신규 자산 생성(멱등) | ✅ | test_universe_reconcile(in-mem) + test_universe_reconcile_pg(ON CONFLICT) |
|
||||
| 비활성 soft-retire(행 보존, 재등장 재활성) | ✅ | test_soft_retire_* + PG |
|
||||
| 보유/pending tombstone(은퇴 보류) | ✅ | test_tombstone_holding_* |
|
||||
| degrade/대량소멸 보류 | ✅ | snapshot_scheduler `_reconcile_slots`(빈 universe skip, ≥50%↓ allow_retire=False) + test_allow_retire_false |
|
||||
|
||||
### REQ-059 (자산 중심 admission)
|
||||
| AC | 상태 | Evidence |
|
||||
|----|------|----------|
|
||||
| 자산 슬롯 활성화(inactive→pending_buy) | ✅ | test_activates_asset_slot |
|
||||
| capacity_full at M(무치환) | ✅ | test_capacity_full_at_M_no_eviction |
|
||||
| already_active | ✅ | test_already_active_is_rejected |
|
||||
| over-cap(신규만 차단·기존 유지) | ✅ | test_over_cap_blocks_new_keeps_existing |
|
||||
| unknown_portfolio(자산 슬롯 부재) | ✅ | test_unknown_portfolio_when_no_asset_slot |
|
||||
|
||||
## Evidence
|
||||
- (대기)
|
||||
- 단위 전체: `387 passed / 35 skipped / 1 deselected`(신규 12 포함). ruff clean.
|
||||
- PG-gated(실 trex_core_test): persistence `17 passed` — reconcile(ON CONFLICT 부분 unique)·find_slot_by_portfolio·count_active·soft-retire + 마이그레이션 009 드리프트.
|
||||
|
||||
## Carried-Over
|
||||
- 라이브 데몬 default 는 legacy pool(max_active_slots 미설정) — 자산 중심 default 전환·컬럼 drop·literal inactive 전면화는 **BAT-δ(cutover)**.
|
||||
- intent 자산 키 통일(sell-by-portfolio_id)·수량/불일치·운영자 뷰 → **BAT-γ**.
|
||||
- M 캡 strict 동시성은 단일 intent 스레드 + 인스턴스 advisory lock 전제(production). 실 데몬 자산 중심 e2e 는 BAT-γ/δ run-the-app 에서.
|
||||
- 따라서 REQ-058/059/060 은 본 배치에서 **Approved 유지**(자산 중심 경로 동작 검증; 전체 dcy-020 완성·default 전환은 후속).
|
||||
|
||||
## Result
|
||||
- Ready for Release: false (구현 전)
|
||||
- Ready for Release: false (dcy-020 전체 delivery 미완 — β 경로 도입 완료, default 는 legacy)
|
||||
- Blocking Issues: 없음(단위 387 + PG 17 green, legacy 무파손).
|
||||
|
||||
Reference in New Issue
Block a user