docs(batch): draft-batch BAT-029 (dcy-020 BAT-γ) + FR-7 재정의(계약 키=slot_id)

사용자 결정으로 intent 계약 키를 portfolio_id → slot_id 재정의(코어 native·스냅샷
slots[] round-trip·portfolio-hub 식별 누수 방지). 매도는 이미 slot_id; 매수만 이동
(레거시 portfolio_id dual-accept, 폐기 BAT-δ).

- REQ-061 change-req(CHG-20260610-03): 계약 키 slot_id 로 body/AC 재작성
- dcy-020 FR-7 재정의 주석(2026-06-10)
- draft-batch BAT-029(REQ-061/062/063) + design 확정
  (D1=slot_id 계약, D2=reconcile 3자 확장+A=error/B·C=warning)
- batches/index BAT-029 등록

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 12:07:10 +09:00
co-authored by Claude Opus 4.8
parent 8a377e6fb3
commit e10ac519c7
8 changed files with 182 additions and 7 deletions
@@ -103,7 +103,8 @@
- FR-4: 보유 **수량(asset_quantity)의 정본은 portfolio-hub**이며, 슬롯은 수량을 정본 소유하지 않고 **로컬 best-effort 미러**(option D write-back `*_after`·추정 오버레이, BAT-023)로만 둔다. 실제 청산 시 매도 수량은 **브로커 잔고(`BrokerPort.get_balance().hldg_qty`)를 정본**으로 조회한다 → 로컬 미러가 stale 해도 청산 정확. 브로커 잔고는 포지션 존재·수량 불일치의 **reconcile ground truth** 이기도 하다(OQ-5). 즉 "포지션 열림(boolean)=코어 로컬, 수량=hub 정본+로컬 미러"로 분리. (issue #1 연계) 활성 슬롯의 보유 **수량 변경(포지션 사이징)** 은 본 주기에서 **분리(OQ-4 Out of Scope)** — 단 수량 가변 전제는 모델에 남긴다(토대 가드). 슬롯이 `qty`/`avg_price` 를 정본 소유하던 풀 모델(reconcile_positions/REQ-031 warning, option D write-back 의 drift 원인)은 본 분리로 정리한다. (사용자 결정 2026-06-10)
- FR-5: `docs/data-model.md`·`docs/runtime-flows.md`·`docs/project-structure.md` 를 새 모델로 갱신하고, dcy-001/002/014/019 의 폐기/재해석 범위를 명시한다. **dcy-019 산출물 구체 처분**: `find_allocatable_slot` selector·`closed→pending_buy` 전이·slot `entry_count` 필드는 FCFS·영구 슬롯 전환으로 **제거**(슬롯 회전 분석이 필요하면 `slot_transition_log` 로 도출, 슬롯 컬럼 아님). `daily_symbol_entries`(symbol-scoped 재진입 카운터)도 자산 중심 모델에서 무의미 → 폐기(별도 트랙과 합류).
- FR-6: **슬롯 상태머신을 자산 영구 슬롯에 맞게 재정의**한다. 기존 `idle`(매수 전)·`closed`(매도 후)는 영구 슬롯에서 **단일 `inactive`(포지션 없음)** 로 합쳐지고(dcy-001 `closed→idle` 복구 전이 제거), 상태 집합 = `inactive → pending_buy → holding → pending_sell → inactive`(+ 모든 비-error → `error`, `error → {inactive, holding}` 운영자 resolve). `retired_at`/`universe_detached`(OQ-2)·`instance_key`(OQ-7)는 status 와 **직교한 속성**으로 둔다. 활성=진입, 종료=전량 청산(HOLDING→HOLDING·부분 매도 미포함, OQ-4).
- FR-7: **intent 계약을 자산(portfolio_id) 키로 통일**한다. 매수·매도 신호 모두 `portfolio_id`(자산)로 식별하고 **`sell-by-slot_id` 는 폐기**(슬롯이 자산 1:1 영구라 slot_id 노출 불필요). 매수 = 해당 자산 슬롯 활성화(`inactive→pending_buy`), 매도 = 비활성화(`holding→pending_sell`). **중복/무효 신호 규칙**: 이미 활성(`pending_buy`/`holding`/`pending_sell`)인 자산에 또 매수 신호 → 멱등 거부(`already_active`); 보유 아닌 자산에 매도 신호 → 거부(`not_holding`). intent_id 멱등(intent_log) 보존.
- FR-7: **intent 계약을 슬롯 native 키(`slot_id`)로 통일**한다(2026-06-10 재정의 — 아래 주). 매수·매도 모두 `slot_id` 로 식별하고 스냅샷 `slots[]` 가 계약 1차 단위; portfolio_id 는 표시/자산 정체성으로 잔존(계약 키 아님). 매수 = 그 슬롯 활성화(`inactive→pending_buy`, symbol 은 슬롯 portfolio_id 로 resolve), 매도 = 비활성화(`holding→pending_sell`). **중복/무효 규칙**: 이미 활성(`pending_buy`/`holding`/`pending_sell`)인 슬롯에 매수 → `already_active`; 보유(holding) 아닌 슬롯에 매도 → `not_holding`. intent_id 멱등 보존.
- **[재정의 2026-06-10, 사용자 결정 / REQ-061 CHG-20260610-03]** 최초 FR-7 은 `portfolio_id` 키 통일(+sell-by-slot_id 폐기)였으나, trex-core 가 슬롯 기준 관리이고 스냅샷이 `slots[]` 를 단위로 주므로 **코어 native 키 `slot_id` 로 통일**하는 것이 일관적이고 portfolio-hub 식별 체계의 계약 누수를 막는다. 자산 영구 슬롯이라 매수도 slot_id 로 지정 가능(슬롯 선존재). 매도는 이미 slot_id(불변); 매수만 portfolio_id→slot_id 이동(레거시 portfolio_id dual-accept, 폐기 BAT-δ). slot_id 는 계약 키이자 내부 PK 로 잔존(제거 아님).
- FR-8: **운영자 표면 단일화** — `trex-core slots` 가 자산 포지션(슬롯=자산, status·holding·수량 미러·`universe_detached`/`retired`)을 **단일 뷰**로 보여준다. universe↔slots 이중 표면(BAT-022 혼동 원인) 해소. universe(자산 리스트)와 slots(자산 포지션)가 같은 자산 집합 위 view 가 된다.
## 비기능 요구사항