# BAT-025 Implementation - Status: completed - Batch: BAT-025 ## 구현 요약 - dcy-019(slot-scoped entry_count + closed 슬롯 직접 재할당, issue #18)을 전부 change-req 로 구현(신규 REQ 없음). 기존 `daily_symbol_entries`(symbol-scoped) 미변경. ## 변경 내역 ### REQ-002 — 슬롯 도메인 필드 + 상태 머신 + 전이 로직 - `trex_core/domain/slot/models.py`: `Slot.entry_count: int = 0`(take_profit_price 뒤) + `EXPECTED_SLOT_FIELDS` 에 `"entry_count"` 추가. - `trex_core/domain/slot/state.py`: `_BASE_TRANSITIONS` 에 `(CLOSED, PENDING_BUY)` 추가(#9, base tuple 7→8, ALLOWED_TRANSITIONS 14→15). docstring "9 base allowed transitions" 갱신. - `trex_core/domain/slot/service.py` `_apply_transition_fields`: closed→pending_buy clean-slate 점유 초기화 블록(limit_price lifecycle 앞) + `new_entry_count = slot.entry_count + (1 if to_state is PENDING_BUY else 0)` + generic `replace(..., entry_count=new_entry_count)`. ### REQ-006 — 마이그레이션 + persistence + selector - `trex_core/adapters/persistence/sql/008_add_slot_entry_count.sql`(신규): `ALTER TABLE daily_slot ADD COLUMN IF NOT EXISTS entry_count INTEGER NOT NULL DEFAULT 0`(additive·멱등). - `trex_core/adapters/persistence/postgresql.py`: `_SLOT_COLUMNS` +`entry_count`(인덱스 18); `update_slot` SET `entry_count=%s` + params; `_row_to_slot` `entry_count=int(row[18]) if row[18] is not None else 0`; 신규 `find_allocatable_slot`(idle∪closed, `ORDER BY entry_count ASC, updated_at ASC, slot_id ASC LIMIT 1`, FOR UPDATE 미사용). - `trex_core/domain/slot/repository.py`: `find_allocatable_slot` Protocol 추가. - `trex_core/domain/slot/in_memory_repository.py`: `find_allocatable_slot`(idle/closed 후보 `min(key=(entry_count, updated_at or min, str(slot_id)))`). ### REQ-007 — buy admission 일반화 - `trex_core/application/intents.py` `_admit_buy`: `find_idle_slot` → `find_allocatable_slot`, transit from_state `SlotState.IDLE` → `slot.status`(closed 후보 직접 재할당). 후보 소진 `no_idle_slot` 거부·`SlotStateMismatchError` 재시도 루프·멱등 불변. ### REQ-003 — 테스트 계약 정합 - `tests/domain/slot/test_state.py`: `closed→pending_buy` allowed 이동, disallowed 에서 제거, `ALLOWED_TRANSITIONS` 크기 14→15. - 신규 `tests/domain/slot/test_slot_entry_count.py`: entry_count 증분(idle→/non-buy 미증가/closed→idle 보존), closed→pending_buy clean-slate+ +1, `find_allocatable_slot` 정렬(none/idle-only/closed 포함/entry_count 오름차순). - 신규 `tests/application/test_buy_admission_reallocation.py`: closed 재할당 admit / idle 우선 / 양쪽 소진 reject. - `tests/domain/slot/test_contract.py`·`tests/adapters/persistence/test_migration_and_drift.py`: `EXPECTED_SLOT_FIELDS` 갱신으로 entry_count 자동 검증. ### REQ-009 — baseline 문서 - `docs/data-model.md`: daily_slot `entry_count` 필드(두 카운터 구분), Lifecycle `closed→pending_buy`, State Rules(전이 패밀리 8 base 포함 + closed→pending_buy clean-slate). - `docs/runtime-flows.md`: buy admission `find_allocatable_slot`(idle∪closed, entry_count 정렬)·closed 직접 재할당, transit_slot 흐름 `→pending_buy 시 entry_count +1` 단계 추가. ## 테스트 - 신규 단위: `test_slot_entry_count.py`(12건), `test_buy_admission_reallocation.py`(3건). - 수정: `test_state.py`(전이 카운트/허용집합). ## 검증 실행 - 단위: `TREX_BROKER=paper .venv/bin/python -m pytest -q --deselect tests/adapters/broker/test_kis.py::test_sandbox_token_and_submit` → `366 passed / 35 skipped / 1 deselected`. (deselect 한 KIS sandbox 테스트는 LIVE `.env` 가 운영 KIS URL 을 가리켜 사전 환경 실패 — 본 batch 무관.) - `ruff check trex_core tests` → All checks passed. - PG 게이트(마이그레이션 008 round-trip + EXPECTED_SLOT_FIELDS 드리프트): 라이브 runtime DSN 미사용(`pg_clean` truncate 파괴적 회피). test DSN(`TREX_POSTGRESQL_TEST_DSN`) 환경에서 자동 검증 — 마이그레이션은 004 의 검증된 additive·멱등 패턴 동일, 드리프트 가드는 `EXPECTED_SLOT_FIELDS`(entry_count 포함)↔information_schema 로 자동 강제.