안 B 점진의 2/3단계. runtime 무변경(idle/closed 별칭 흡수)으로 cascade 통제. - state.py: SlotState.INACTIVE 추가 + idle/closed 별칭 normalize_state(); 전이 집합 inactive 기준 재정의(inactive↔pending_buy↔holding↔pending_sell), closed→idle 제거; is_allowed_transition 양쪽 정규화; slot_advisory_lock_id(instance_key) 헬퍼 - service.py: SlotService(instance_key) + advisory lock 인스턴스 스코프(self._lock_id) - __init__: normalize_state/slot_advisory_lock_id export; daemon: instance_key 전달 - 테스트 갱신: test_state(전이/멤버7/락/size12), test_service(closed→idle 제거→reallocation), test_slot_entry_count, test_postgresql_repository(closed→idle 거부+reallocation) 검증: 단위 375 passed / 35 skipped, PG-gated(trex_core_test) 8 passed (마이그레이션 009 드리프트 + lifecycle/reallocation), ruff clean. REQ-056/057 은 토대만 충족 → Approved 유지(entry_count/trading_date drop·literal inactive 전면 전환은 BAT-δ; confirm-req-implemented 는 dcy-020 후속 배치 완료 후). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3.2 KiB
3.2 KiB
BAT-027 Implementation
- Status: completed (M1·M2·M3 완료)
- Batch: BAT-027
구현 요약
- 안 B(점진/additive-first). M1(additive 모델+마이그레이션+persistence)·M2(상태머신 inactive 별칭+인스턴스 스코프 lock)·M3(소비처 cascade — runtime 무변경, idle/closed 별칭으로 흡수) 완료. 전체 375 passed + PG-gated 8 passed, ruff clean.
변경 내역
M1 — REQ-056 additive (완료)
trex_core/domain/slot/models.py:Slot에instance_key: str = "default"·retired_at: Optional[datetime] = None추가,EXPECTED_SLOT_FIELDS+instance_key/+retired_at. (entry_count/trading_date 는 안 B로 유지 — drop 은 BAT-δ)trex_core/adapters/persistence/sql/009_add_slot_instance_key_and_retired_at.sql(신규): instance_key NOT NULL DEFAULT 'default', retired_at, 부분 UNIQUE(instance_key, portfolio_id) WHERE portfolio_id IS NOT NULL. additive·멱등.trex_core/adapters/persistence/postgresql.py:_SLOT_COLUMNS+instance_key/retired_at(row[19]/[20]),_row_to_slot매핑,update_slotSET. pre_allocate INSERT 는 DEFAULT 의존(변경 없음).
M2 — REQ-057 상태머신 + 인스턴스 lock (완료)
trex_core/domain/slot/state.py:SlotState.INACTIVE추가 +idle/closed를 별칭으로normalize_state()매핑._BASE_TRANSITIONS를 inactive 기준으로 재정의(inactive↔pending_buy↔holding↔pending_sell),closed→idle복구 제거(inactive self-loop).is_allowed_transition가 양쪽 정규화.slot_advisory_lock_id(instance_key)헬퍼(전역 base ⊕ crc32(instance_key)).trex_core/domain/slot/service.py:SlotService(repository, instance_key="default")+ advisory lock 을self._lock_id(인스턴스 스코프)로 3개소 치환. holding 은 status 로컬 정본(불변).trex_core/domain/slot/__init__.py:normalize_state/slot_advisory_lock_idexport.trex_core/runtime/daemon.py:SlotService(..., instance_key=config.instance_key).
M3 — 소비처 cascade (완료, runtime 무변경)
- idle/closed 가 inactive 별칭으로 흡수되어 execution/cli/ops_checks/postgresql 의 idle·closed 사용은 그대로 유효(별칭 정규화). closed→idle 은 runtime 미사용이라 무파손. → 별도 소비처 코드 변경 없음. (실제 inactive 전면 전환·컬럼 drop 은 BAT-δ.)
- 테스트 갱신: test_state(새 전이/멤버/락), test_service(closed→idle 제거→reallocation), test_slot_entry_count, test_postgresql_repository(PG).
테스트
- 단위 전체: 375 passed / 35 skipped / 1 deselected.
- PG-gated(실 trex_core_test):
test_migration_and_drift(마이그레이션 009 +EXPECTED_SLOT_FIELDS↔컬럼 드리프트, instance_key/retired_at 포함) +test_postgresql_repository(lifecycle + closed→idle 거부 + closed→pending_buy reallocation) → 8 passed. ruff check trex_core testsAll passed.
검증 실행
TREX_BROKER=paper pytest -q --deselect …test_sandbox…→ 375 passed / 35 skipped / 1 deselected.TREX_POSTGRESQL_TEST_DSN=…(trex_core_test) pytest tests/adapters/persistence/test_migration_and_drift.py test_postgresql_repository.py→ 8 passed.ruff check trex_core tests→ All checks passed.