Files
trex-core/docs/batches/bat-027_20260610_asset-permanent-slot-foundation/implementation.md
T
genichinandClaude Opus 4.8 017b8185e0 feat(slot): BAT-027 M2/M3 — 상태머신 inactive 재정의 + 인스턴스 스코프 advisory lock (dcy-020/REQ-057)
안 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>
2026-06-10 10:53:39 +09:00

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_slot SET. 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_id export. 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 tests All 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.