feat(slot): slot entry_count + closed-slot reallocation (dcy-019, BAT-025) + REL-005

dcy-019/issue #18: slot 자원을 진입 횟수 기준으로 고르게 재사용하고 idle 소진 시
closed 슬롯을 직접 재할당한다. 거버넌스는 전부 change-req(신규 REQ 없음).

- 도메인(REQ-002): Slot.entry_count(기본 0) + EXPECTED_SLOT_FIELDS, 상태 머신
  closed→pending_buy 신규 전이(base 8→9, ALLOWED_TRANSITIONS 14→15),
  _apply_transition_fields closed→pending_buy clean-slate + →pending_buy 시 +1
- 영속화(REQ-006): 멱등 마이그레이션 008(entry_count), flat 매핑,
  find_allocatable_slot(idle∪closed, entry_count→updated_at→slot_id) + in-memory 동등
- admission(REQ-007): _admit_buy find_idle_slot→find_allocatable_slot + from_state=slot.status
- 테스트(REQ-003): 전이 카운트/계약 가드 정합 + test_slot_entry_count.py / test_buy_admission_reallocation.py
- 문서(REQ-009): data-model.md / runtime-flows.md 정합
- 기존 daily_symbol_entries(symbol-scoped) 분리·미변경

BAT-025(release-candidate) delivery + change-req REQ-002/003/006/007/009(CHG-20260609-01).
REL-005(confirmed, app-service): BAT-024+BAT-025 묶음. operator-facing run-the-app
스모크로 enter --forced-close-at(BAT-024)·closed 재할당(BAT-025) 실 데몬 검증
(실 PG 마이그레이션 008 round-trip 포함).

검증: 단위 366 passed / 35 skipped(KIS sandbox 1건 deselect — live .env 환경 의존), ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 14:49:22 +09:00
co-authored by Claude Opus 4.8
parent 941d324899
commit 6e8e79660c
28 changed files with 912 additions and 26 deletions
@@ -15,7 +15,7 @@
1. Given 이식 완료, When `from trex_core.domain.slot import SlotState, Slot, SlotCapacity, SlotTransitionRecord, SlotRepository, SlotService, SlotDomainError, SlotNotFoundError, SlotStateMismatchError, SlotTransitionError, is_allowed_transition, SLOT_ADVISORY_LOCK_ID, ALLOWED_TRANSITIONS` 를 실행, Then 모두 import 에 성공한다.
2. Given 이식된 도메인 코드, When 정적 import 스윕, Then stdlib 와 `trex_core.domain.slot` 내부 참조 외 외부 의존(psycopg/네트워크/CLI/config) import 가 0이다. (NFR-1)
3. Given 공개 API, When `__init__.py` 의 `__all__` 을 확인, Then `PostgreSQLSlotRepository`·`check_slot_domain_schema_drift` 등 인프라 심볼이 노출되지 않는다.
4. Given `SlotService`, When 6 상태/8 전이/advisory-lock 직렬화/`closed→idle` 재진입 +1/lock 보유 중 외부 I/O 금지 동작을 확인, Then trex-orbit 과 동일하게 보존된다. (NFR-2 — 검증은 REQ-003)
4. Given `SlotService`, When 6 상태/9 base 전이(`closed→pending_buy` 포함, dcy-019)/advisory-lock 직렬화/`closed→idle` 재진입 +1/`→pending_buy` 시 slot `entry_count` +1(dcy-019)/`closed→pending_buy` clean-slate 점유 초기화/lock 보유 중 외부 I/O 금지 동작을 확인, Then 보존된다. (NFR-2 — 검증은 REQ-003)
## Impacted Area
- Docs: docs/interface-contract.md, docs/data-model.md (계약 기준 — 큰 변경 없을 수 있음)
@@ -191,3 +191,44 @@
#### Validation Plan
- BAT-016: 전이별 set/clear 단위 + 컬럼 계약 가드(REQ-003) 정합.
### CHG-20260609-01
- Date: 2026-06-09
- Author: Justin Ko
#### Change Summary
- (change-req, dcy-019/issue #18) 슬롯 도메인에 (1) slot-scoped `Slot.entry_count`(기본 0) 필드 + `EXPECTED_SLOT_FIELDS` 추가, (2) 상태 머신 허용 전이에 `closed→pending_buy`(closed 슬롯 직접 재할당) 추가(8→9 base 전이), (3) `_apply_transition_fields` 에 closed→pending_buy clean-slate 점유 초기화 + 모든 `→pending_buy`(idle→/closed→) 시 `entry_count` +1 로직 반영. 기존 `daily_symbol_entries`(symbol-scoped, AC-3)는 무관·미변경. BAT-025 재구현·재검증 완료 → Implemented 유지.
#### Intent
- 슬롯 자원을 고르게 재사용(최저 진입 우선)하고 idle 소진 시 closed 재할당을 허용하기 위해, 슬롯 단위 누적 진입 횟수를 도메인 정본에 영속하고 closed→pending_buy 직접 전이를 도메인 계약에 추가.
#### Acceptance Criteria Delta
- Updated:
- AC-4: "6 상태/8 전이" → "6 상태/9 base 전이(`closed→pending_buy` 포함)". 불변식 보존 대상에 `→pending_buy 시 entry_count +1`, `closed→pending_buy clean-slate 점유 초기화` 추가.
- Added:
- `Slot.entry_count`(int, 기본 0) 필드가 `EXPECTED_SLOT_FIELDS` 계약에 포함된다.
#### Impacted Area Delta
- Modules: `trex_core/domain/slot/models.py`(Slot.entry_count/EXPECTED_SLOT_FIELDS), `trex_core/domain/slot/state.py`(closed→pending_buy 전이), `trex_core/domain/slot/service.py`(clean-slate + entry_count +1)
- Tests: tests/domain/slot/test_state.py(전이 카운트 14→15), test_contract.py(필드셋), test_slot_entry_count.py(신규)
#### Delivery Trace Delta
- Batches: BAT-025
- Releases: none
#### Revalidation Impact
- Reverification Needed: yes (BAT-025 에서 완료)
- Existing Implementation Invalidated: no (additive — 기존 idle-only 동작은 closed 후보 부재 시 보존, 회귀 green)
- Status Recommendation: Implemented (change-req → BAT-025 재검증)
- Follow-up Action: confirm-req-implemented(BAT-025).
#### Validation Plan
- BAT-025: 전이 카운트/계약 가드 + entry_count 증분·보존 + closed→pending_buy clean-slate 단위 테스트 green; `daily_symbol_entries`(AC-3) 회귀 green.
@@ -12,7 +12,7 @@
- `POST /v1/intents/buy` 와 `POST /v1/intents/sell` 를 제공한다. 코어는 admission/검증 후 슬롯 전이(idle→pending_buy / holding→pending_sell)를 advisory lock 하 단일 writer 로 수행하고, 동기로 admit/reject 를 응답한다. 수락된 intent 는 같은 트랜잭션에서 `intent_log`(intent_id, payload, decision, ts) 에 durable 기록하며 `intent_id` 로 dedup 한다. `GET /v1/intents/{intent_id}` 로 결정을 재조회한다.
## Acceptance Criteria
1. Given idle 슬롯, When `POST /v1/intents/buy`, Then admission 통과 시 idle→pending_buy 로 전이하고 `{admitted:true, slot_id}` 를 동기 응답한다.
1. Given 할당 가능 슬롯(idle ∪ closed, dcy-019), When `POST /v1/intents/buy`, Then `find_allocatable_slot`(entry_count→updated_at→slot_id 정렬)로 최저 진입 후보를 잡아 `slot.status→pending_buy`(idle→ 또는 closed→ 직접 재할당) 전이하고 `{admitted:true, slot_id}` 를 동기 응답한다. idle·closed 모두 소진 시 `no_idle_slot` 거부.
2. Given holding 슬롯, When `POST /v1/intents/sell`, Then 검증 통과 시 holding→pending_sell 로 전이하고 admit 결과를 동기 응답한다(거부 시 사유 포함).
3. Given 이미 처리된 `intent_id`, When 동일 intent 재전송, Then 슬롯이 중복 전이되지 않고 최초 결정과 동일한 응답을 돌려준다(멱등). (NFR-3)
4. Given 동일 슬롯에 대한 동시 intent, When 병렬 처리, Then advisory lock 으로 직렬화되어 단일 writer 불변식이 유지된다(매수·매도 전략은 슬롯을 직접 쓰지 않음). (NFR-2)
@@ -273,3 +273,42 @@
#### Validation Plan
- 실 데몬 기동(paper + test PG/RabbitMQ) + `trex-core enter pf-005930 --yes` → admitted/체결/ledger completed e2e + test_daemon_resolve_symbol 회귀.
### CHG-20260609-01
- Date: 2026-06-09
- Author: Justin Ko
#### Change Summary
- (change-req, dcy-019/issue #18) buy admission(`_admit_buy`) 의 슬롯 선택을 `find_idle_slot` → `find_allocatable_slot`(idle ∪ closed, entry_count→updated_at→slot_id 정렬)로 일반화하고, 후보 status 를 따르도록 transit from_state 를 하드코딩 `IDLE` → `slot.status` 로 변경(closed 후보면 `closed→pending_buy` 직접 재할당). 마지막 슬롯 경합 재시도 루프·멱등(intent_log dedup)·단일 writer 불변식 보존. BAT-025 검증 완료 → Implemented 유지.
#### Intent
- idle 소진 시에도 closed 슬롯을 재할당해 슬롯 자원을 효율적으로 회전시키고, 슬롯 사용을 진입 횟수 기준으로 고르게 분산.
#### Acceptance Criteria Delta
- Updated:
- AC-1: 후보 = idle ∪ closed, `find_allocatable_slot` 선택 + `slot.status→pending_buy` 전이(closed 직접 재할당 포함), 양쪽 소진 시 `no_idle_slot` 거부로 일반화.
#### Impacted Area Delta
- Modules: `trex_core/application/intents.py`(`_admit_buy`: find_allocatable_slot + from_state=slot.status)
- Tests: tests/application/test_buy_admission_reallocation.py(신규: closed 재할당 admit/idle 우선/양쪽 소진 reject), tests/application/test_intent_concurrency.py(경합 회귀 보존)
#### Delivery Trace Delta
- Batches: BAT-025
- Releases: none
#### Revalidation Impact
- Reverification Needed: yes (BAT-025 에서 완료)
- Existing Implementation Invalidated: no (closed 후보 부재 시 기존 idle-only 동작과 동일 — 회귀 green)
- Status Recommendation: Implemented (change-req → BAT-025 재검증)
- Follow-up Action: confirm-req-implemented(BAT-025).
#### Validation Plan
- BAT-025: closed 재할당 admit / idle 우선 / 양쪽 소진 reject 단위 + 동시성 경합 회귀 green.