# BAT-027 Design - Status: confirmed (안 B 확정 — 2026-06-10, 사용자 결정) - Batch: BAT-027 ## 설계 목표 - 익명 풀 슬롯 도메인을 자산 1:1 영구 슬롯의 데이터 모델 + 상태머신 토대로 재작성하되, **소비처 연쇄(R-cascade)를 통제**해 각 단계에서 단위 green 을 유지한다. 라이브 데이터는 건드리지 않는다. ## R-cascade — 실측 영향 범위 (근거) 저장소 grep 기준, 본 배치가 바꾸는 3개 축의 소비처: - **`idle`/`closed` 상태 참조**: `application/execution.py`, `adapters/persistence/ops_checks.py`, `runtime/cli.py`, `adapters/persistence/postgresql.py`, `sql/001` - **`entry_count`**: `application/intents.py`, `domain/slot/*`, `adapters/persistence/postgresql.py`, `sql/001`, `sql/008` - **`SLOT_ADVISORY_LOCK_ID`(전역 락)**: `application/execution.py`, `application/intents.py`, `domain/slot/*`, `adapters/persistence/postgresql.py` - **슬롯 `trading_date`**: `preallocation_scheduler`, `safety_sell`, `messaging_handlers`, `intents`, `killswitch`, `daemon`, `portfolio`, `config`, `cli` (광범위 — 일일 회계·키 양쪽) → "스키마+상태머신만" 깨끗이 격리되지 않는다. 특히 `idle/closed`→`inactive` 합침과 전역→인스턴스 락은 execution/intents 를 동시 수정해야 컴파일·테스트가 선다. ## 접근안 (택1 — 체크포인트 결정 필요) ### 안 A — Big-bang 토대 (모델+상태+persistence+소비처 동시) - BAT-027 에서 모델/상태머신/persistence + 모든 idle·closed·entry_count·락 소비처를 한 번에 새 모델로 전환. - 장점: 깨끗한 최종 상태, 임시 이중 모델 없음. 단점: 단일 배치가 매우 큼, 회귀 위험 집중, intents/execution(=admission·집행)까지 본 배치로 끌려와 BAT-β 경계 흐려짐. ### 안 B — 점진(additive-first) + 단계 green ★권장 - **BAT-027(본 배치)**: (1) 스키마 **additive**(instance_key·retired_at 추가, `UNIQUE(instance_key, portfolio_id)` 도입) — `entry_count`·`trading_date` 컬럼은 **이번엔 유지**(drop 은 cutover BAT-δ로 미뤄 cascade 축소). (2) 상태머신: `inactive` 를 **신규 표준 상태로 추가**하고 `idle`/`closed` 를 **`inactive` 의 별칭/매핑**으로 흡수(전이표는 inactive 기준 재정의, `closed→idle` 제거). (3) advisory lock 을 인스턴스 스코프 2-int 로 전환(전역 락 호출부 일괄 치환). (4) holding 로컬 정본 헬퍼. (5) 소비처는 **상태 별칭·락 시그니처**만 정합(idle/closed 리터럴 → inactive 매핑 유지로 execution/cli/ops_checks 최소 수정). - **후속 배치**에서 `entry_count`/`trading_date` 컬럼 drop·idle/closed 별칭 완전 제거를 cutover(BAT-δ)와 함께 마무리. - 장점: 각 단계 단위 green 유지, cascade 분할, BAT-β/γ 경계 보존. 단점: 한시적 별칭/잔존 컬럼(임시 복잡도) — 단, drop 시점(BAT-δ)이 문서로 고정됨. ### 안 C — 최소(스키마 additive만) - instance_key·retired_at 만 additive 추가, 상태머신은 손대지 않음. 단점: REQ-057(상태머신) 미충족이라 BAT-α 의도 미달 → 부적합. ## 권장 = 안 B 점진(additive-first). 이유: R-cascade·R-trading-date 를 분할해 단위 green 을 유지하고, 라이브 영향(컬럼 drop)을 cutover(BAT-δ)로 모아 안전. REQ-056/057 의 "목표 스키마·상태머신"은 충족(별칭·잔존 컬럼은 후속 drop 으로 수렴). ## 상태머신 (안 B 기준) - 표준 상태: `inactive, pending_buy, holding, pending_sell, error`. `idle`/`closed` 는 입력 호환 별칭 → `inactive` 로 매핑(전이 검증·저장은 inactive). - 허용 전이: `inactive→pending_buy, pending_buy→holding, pending_buy→inactive, holding→pending_sell, pending_sell→inactive, pending_sell→holding, *→error, error→{inactive,holding}`. (`closed→idle` 제거.) - holding = 로컬 정본: pending_buy→holding 에서 set, pending_sell→inactive(매도 체결)에서 clear. ## advisory lock (REQ-057 / NFR-1) - `SLOT_ADVISORY_LOCK_ID` 전역 상수 → 인스턴스 스코프 키: `pg_advisory_xact_lock(SLOT_LOCK_ID, hash32(instance_key))`. in-memory repo 는 (lock_id, instance_key) 별 락. 호출부(execution/intents/service/postgresql) 시그니처에 instance_key 전달. ## 스키마 (안 B / 마이그레이션 009) - `ALTER TABLE daily_slot ADD COLUMN instance_key TEXT NOT NULL DEFAULT 'default'` (cutover 전 단일 인스턴스 호환), `ADD COLUMN retired_at TIMESTAMPTZ`. - `UNIQUE(instance_key, portfolio_id)` — 단, portfolio_id 가 nullable·풀 모델 잔존 행과 충돌 가능 → 본 배치에선 **부분 UNIQUE(WHERE portfolio_id IS NOT NULL)** 또는 cutover 후 전체 UNIQUE. (구현 시 확정.) - `entry_count`/`trading_date` 컬럼 유지(후속 drop). `EXPECTED_SLOT_FIELDS` 는 +instance_key·+retired_at(−entry_count 는 후속). ## 테스트 설계 - test_state: 전이 집합(inactive 기준, closed→idle 부재), idle/closed 별칭 매핑. - test_contract: 필드셋(+instance_key·retired_at). - test_service: holding set/clear, 인스턴스 락 직렬화/비교차. - PG 게이트(test DSN): 마이그레이션 009 round-trip + 드리프트. - 회귀: 소비처 연쇄 정합 후 전체 단위 green. ## 체크포인트 결정 (2026-06-10, 사용자 확정) 1. **접근안 = 안 B(점진/additive-first).** 2. `idle/closed` 별칭 유지 → **BAT-δ(cutover)에서 완전 제거**. (수용) 3. `UNIQUE` 는 **부분 인덱스 `UNIQUE(instance_key, portfolio_id) WHERE portfolio_id IS NOT NULL`** — 풀 모델 잔존 행(portfolio_id NULL)과 충돌 회피. cutover 후 전체 UNIQUE 승격. 4. `instance_key` 컬럼 **`NOT NULL DEFAULT 'default'`** — 단일 인스턴스/기존 행 호환. cutover 에서 실제 instance_key 태깅(BAT-δ).