docs(batch): draft-batch BAT-027 (dcy-020 BAT-α 자산 영구 슬롯 토대) + design 확정(안 B)
REQ-056(영구 슬롯 스키마)+REQ-057(상태머신 재정의+인스턴스 lock) 토대 배치. - planning: delivery plan/Design Gate(R-cascade)/milestones/risks - design: R-cascade 실측(idle·closed/entry_count/전역 lock/trading_date 소비처) + 접근안 A/B/C → 안 B(점진/additive-first) 확정: * additive 스키마(instance_key·retired_at, entry_count/trading_date 유지→BAT-δ drop) * inactive 표준 + idle/closed 별칭(closed→idle 제거) * advisory lock 인스턴스 스코프화 * 부분 UNIQUE(instance_key, portfolio_id) WHERE portfolio_id NOT NULL * instance_key NOT NULL DEFAULT 'default' - batches/index BAT-027(standard, planning) 등록 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
|||||||
|
# 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-δ).
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# BAT-027 Implementation
|
||||||
|
|
||||||
|
- Status: not-started
|
||||||
|
- Batch: BAT-027
|
||||||
|
|
||||||
|
## 구현 요약
|
||||||
|
- (run-batch-delivery 진입 시 작성) design.md 접근안 확정 후 채운다.
|
||||||
|
|
||||||
|
## 변경 내역
|
||||||
|
- (대기)
|
||||||
|
|
||||||
|
## 테스트
|
||||||
|
- (대기)
|
||||||
|
|
||||||
|
## 검증 실행
|
||||||
|
- (대기)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# BAT-027: 자산 영구 슬롯 토대 — 스키마 + 상태머신 재정의 (dcy-020 BAT-α)
|
||||||
|
|
||||||
|
- Status: planning
|
||||||
|
- Profile: standard
|
||||||
|
- Source Discovery: dcy-020
|
||||||
|
- Included REQ: REQ-056 (영구 슬롯 스키마+마이그레이션), REQ-057 (상태머신 재정의 + 인스턴스 스코프 advisory lock)
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
- dcy-020(자산 중심 영구 슬롯 모델)의 **첫 구현 배치(BAT-α)**. 익명 풀 슬롯 도메인을 자산 1:1 영구 슬롯의 **데이터 모델 + 상태머신 토대**로 재작성한다. 외부 전략 계약 변경(BAT-γ)·라이브 cutover(BAT-δ)는 포함하지 않는다.
|
||||||
|
- 핵심: `Slot` 모델/`EXPECTED_SLOT_FIELDS` 재정의(portfolio_id 영구키·`instance_key`·`retired_at`, `entry_count`/슬롯 `trading_date` 제거), 멱등 마이그레이션 009(`UNIQUE(instance_key, portfolio_id)`), 상태머신 `inactive↔pending_buy↔holding↔pending_sell`(+error) 으로 재정의(`closed→idle` 복구 제거), advisory lock 인스턴스 스코프화.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
- In Scope:
|
||||||
|
- `Slot` 모델·`EXPECTED_SLOT_FIELDS` 영구 슬롯 재정의 (REQ-056)
|
||||||
|
- 마이그레이션 009 (instance_key·retired_at·UNIQUE 복합, entry_count/trading_date 제거) + persistence flat 매핑 (REQ-056)
|
||||||
|
- 상태머신 재정의(inactive 합침·closed→idle 제거) + holding 로컬 정본 + 인스턴스 스코프 advisory lock (REQ-057)
|
||||||
|
- `data-model.md` 슬롯 스키마/상태 부분 갱신 (REQ-065 부분)
|
||||||
|
- 상태/계약 단위테스트 재작성
|
||||||
|
- Out of Scope:
|
||||||
|
- universe reconcile lifecycle (REQ-058 → BAT-β)
|
||||||
|
- admission/capacity·intent 계약 (REQ-059/061 → BAT-β/γ)
|
||||||
|
- 수량/불일치 해소 (REQ-062 → BAT-γ)
|
||||||
|
- 라이브 cutover (REQ-064 → BAT-δ)
|
||||||
|
- 운영자 slots 단일 뷰 (REQ-063 → 후속)
|
||||||
|
|
||||||
|
## Documents
|
||||||
|
- Planning: [planning.md](./planning.md)
|
||||||
|
- Design: [design.md](./design.md)
|
||||||
|
- Implementation: [implementation.md](./implementation.md)
|
||||||
|
- Verification: [verification.md](./verification.md)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Source discovery 단일(dcy-020). 본 배치는 dcy-020 의 토대 배치로, 후속 BAT-β(reconcile·admission)/BAT-γ(intent·수량)/BAT-δ(cutover)의 선행.
|
||||||
|
- **핵심 리스크(R-cascade)**: `Slot` 모델·상태머신 변경은 intents.py·safety_sell·snapshot_handlers·execution 등 다수 소비처로 연쇄된다. design.md 에서 cascade 격리 전략(소비처 동시 수정 범위)을 확정한 뒤 구현한다 — **design 체크포인트 필수**.
|
||||||
|
- 라이브 데이터는 본 배치에서 건드리지 않는다(스키마는 신규 마이그레이션으로 정의만; 기존 행 전환은 REQ-064/BAT-δ).
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# BAT-027 Planning
|
||||||
|
|
||||||
|
- Status: confirmed
|
||||||
|
- Batch: BAT-027
|
||||||
|
- Profile: standard
|
||||||
|
|
||||||
|
## Included REQ
|
||||||
|
- REQ-056 (Data, Approved): 영구 슬롯 스키마 — portfolio_id 영구키·instance_key·retired_at·`UNIQUE(instance_key, portfolio_id)`, entry_count/슬롯 trading_date 제거 + 마이그레이션 009.
|
||||||
|
- REQ-057 (Interface, Approved): 상태머신 재정의(inactive↔pending_buy↔holding↔pending_sell, closed→idle 제거) + holding 로컬 정본 + 인스턴스 스코프 advisory lock.
|
||||||
|
|
||||||
|
## Delivery Plan
|
||||||
|
1. REQ-056 도메인 모델: `models.py` `Slot` 재정의 — `instance_key`(str) 추가, `retired_at: Optional[datetime]` 추가, `entry_count` 제거, `trading_date` 의미 재정의(슬롯 키 아님; 호환 위해 last_activity 성격으로 둘지 design 에서 확정). `EXPECTED_SLOT_FIELDS` 정합.
|
||||||
|
2. REQ-056 persistence: 마이그레이션 009(`ALTER`/신규 — instance_key NOT NULL·retired_at·UNIQUE(instance_key, portfolio_id), entry_count drop), `postgresql.py` `_SLOT_COLUMNS`/`_row_to_slot`/`update_slot`/insert 매핑 정합. 기존 행 전환은 본 배치 범위 밖(REQ-064).
|
||||||
|
3. REQ-057 상태머신: `state.py` 상태 집합 `{inactive, pending_buy, holding, pending_sell, error}` + 허용 전이 재정의(`closed`/`idle` → `inactive`, `closed→idle` 제거). `SLOT_ADVISORY_LOCK_ID` → 인스턴스 스코프 키(2-int advisory lock) 헬퍼.
|
||||||
|
4. REQ-057 서비스: `service.py` holding 로컬 set/clear, transit 인스턴스 스코프 lock 사용. 점유 필드 리셋 의미 재정의(종료=inactive 복귀).
|
||||||
|
5. 소비처 연쇄 정합(R-cascade): intents.py·safety_sell·snapshot_handlers·execution·reconciliation 등 `idle/closed`·`entry_count`·전역 lock 참조를 새 모델로 정합(컴파일·테스트 green 유지 범위).
|
||||||
|
6. 테스트: test_state(전이 집합)·test_contract(필드셋)·test_service 재작성 + 인스턴스 락 단위.
|
||||||
|
7. `data-model.md` 슬롯 스키마/상태 부분 갱신(REQ-065 부분).
|
||||||
|
8. 단위 회귀 + ruff (+ test DSN 마이그레이션/드리프트).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- Internal: dcy-001/BAT-001 슬롯 도메인(재작성 대상), dcy-002/BAT-002 persistence(재작성 대상), dcy-019/BAT-025 entry_count(제거 대상).
|
||||||
|
- External: 없음(본 배치는 내부 도메인/스키마). 외부 전략 계약은 BAT-γ.
|
||||||
|
|
||||||
|
## Design Gate
|
||||||
|
- Design Required Before Implementation: yes
|
||||||
|
- Reason: (a) `Slot` 모델·상태머신 변경이 intents/safety_sell/snapshot/execution/reconciliation 으로 **연쇄(R-cascade)** — 한 배치에서 어디까지 동시 수정해 green 을 유지할지 경계를 설계에서 확정해야 한다. (b) `idle`+`closed`→`inactive` 합침과 `closed→idle` 제거가 기존 lifecycle(재진입·점유 리셋·daily_symbol_entries)과 교차 — 전이/리셋 의미를 못박아야 안전. (c) advisory lock 전역→인스턴스 스코프 전환은 단일 writer 불변식(REQ-002 NFR-1) 재정의라 락 키 설계 필요. (d) `trading_date` 컬럼 drop vs 재의미·entry_count drop 의 스키마/계약 가드 영향. → design.md 를 채우고 **구현 전 사용자 체크포인트**.
|
||||||
|
|
||||||
|
## Milestones
|
||||||
|
1. M1 — REQ-056 모델+마이그레이션+persistence 매핑, 계약/드리프트 green
|
||||||
|
2. M2 — REQ-057 상태머신+인스턴스 락, 전이/락 단위 green
|
||||||
|
3. M3 — 소비처 연쇄 정합(전체 단위 green 회복)
|
||||||
|
4. M4 — data-model.md 부분 갱신 + 회귀/ruff
|
||||||
|
|
||||||
|
## Risks
|
||||||
|
- R-cascade (High): 모델/상태머신 변경의 소비처 연쇄로 광범위 회귀. 대응: design 에서 동시 수정 범위 확정, 단계별 단위 green 게이트, 기존 테스트 대량 갱신 인지.
|
||||||
|
- R-trading-date (Medium): trading_date 슬롯 키 제거가 일일 회계·kill-switch day-scope·transition_log 와 얽힘. 대응: design 에서 drop vs 재의미 결정, 회계 경로는 transition_log/ledger 로 이전.
|
||||||
|
- R-lock (Medium): 인스턴스 스코프 락 전환이 동시성 회귀. 대응: 2-int advisory lock + 경합 회귀 테스트.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- 라이브 데이터 불변(본 배치는 목표 스키마/모델 정의 + 신규 마이그레이션). 기존 행 cutover 는 BAT-δ(REQ-064).
|
||||||
|
- 본 배치 green 기준: 단위 전체 회귀(소비처 연쇄 정합 무누락). PG 마이그레이션/드리프트는 test DSN.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# BAT-027 Verification
|
||||||
|
|
||||||
|
- Status: not-started
|
||||||
|
- Batch: BAT-027
|
||||||
|
|
||||||
|
## Acceptance Mapping
|
||||||
|
- (draft-batch-verification / confirm-batch-verification 단계에서 작성)
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
- (대기)
|
||||||
|
|
||||||
|
## Result
|
||||||
|
- Ready for Release: false (구현 전)
|
||||||
@@ -34,3 +34,4 @@
|
|||||||
| BAT-024 | batch-lite | release-candidate | REQ-032 (CHG-20260609-01) | dcy-016 (issue #17) | [bat-024](./bat-024_20260609_operator-enter-forced-close-at/) |
|
| BAT-024 | batch-lite | release-candidate | REQ-032 (CHG-20260609-01) | dcy-016 (issue #17) | [bat-024](./bat-024_20260609_operator-enter-forced-close-at/) |
|
||||||
| BAT-025 | standard | release-candidate | REQ-002 (CHG-20260609-01), REQ-003 (CHG-20260609-01), REQ-006 (CHG-20260609-01), REQ-007 (CHG-20260609-01), REQ-009 (CHG-20260609-01) | dcy-019 (issue #18) | [bat-025](./bat-025_20260609_slot-entry-count-and-closed-reallocation/) |
|
| BAT-025 | standard | release-candidate | REQ-002 (CHG-20260609-01), REQ-003 (CHG-20260609-01), REQ-006 (CHG-20260609-01), REQ-007 (CHG-20260609-01), REQ-009 (CHG-20260609-01) | dcy-019 (issue #18) | [bat-025](./bat-025_20260609_slot-entry-count-and-closed-reallocation/) |
|
||||||
| BAT-026 | batch-lite | release-candidate | REQ-048 (CHG-20260609-01), REQ-010 (CHG-20260609-01) | 없음 (trex-orb-v1 정합) | [bat-026](./bat-026_20260609_config-dir-env-and-config-path/) |
|
| BAT-026 | batch-lite | release-candidate | REQ-048 (CHG-20260609-01), REQ-010 (CHG-20260609-01) | 없음 (trex-orb-v1 정합) | [bat-026](./bat-026_20260609_config-dir-env-and-config-path/) |
|
||||||
|
| BAT-027 | standard | planning | REQ-056, REQ-057 | dcy-020 (BAT-α) | [bat-027](./bat-027_20260610_asset-permanent-slot-foundation/) |
|
||||||
|
|||||||
Reference in New Issue
Block a user