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
@@ -0,0 +1,40 @@
# BAT-029 Design
- Status: confirmed (D1=slot_id 계약 / D2=3자 확장 — 2026-06-10, 사용자 결정)
- Batch: BAT-029
> **D1 재정의(2026-06-10)**: 계약 키 = **slot_id**(buy·sell 공통, FR-7 재정의 / REQ-061 CHG-20260610-03). 매도는 이미 slot_id(불변); 매수가 portfolio_id→slot_id 이동(레거시 portfolio_id **dual-accept**, 폐기 BAT-δ). 아래 REQ-061 절은 slot_id 기준으로 읽는다.
> **D2 확정**: REQ-062 = reconcile 3자 확장 + A=error/B·C=warning + 청산 브로커 정본.
## 설계 목표
- intent 를 자산 키로 통일하되 외부 전략 모듈 무중단(dual-accept), 수량 정본을 hub+미러·청산을 브로커로, 코어 holding↔broker/hub 비정상 불일치를 자동 청산 없이 표면화. 운영자 slots 단일 뷰.
## REQ-061 — sell 자산 키 (dual-accept)
- `SellIntent`: `slot_id: Optional[UUID]` 로 완화 + `portfolio_id: Optional[str]` 추가. 둘 중 하나 필수.
- messaging_handlers sell 파싱: `portfolio_id` 있으면 자산 키, 없으면 `slot_id`(레거시). 둘 다 없으면 reject.
- `_admit_sell`: portfolio_id 면 `find_slot_by_portfolio(instance_key, portfolio_id)` → holding 이면 `holding→pending_sell`, 아니면 `not_holding`. slot_id 면 기존 경로(fetch_slot). 멱등(intent_id) 유지.
- CLI `exit`: 인자를 portfolio_id 로 받되 UUID 형태면 slot_id 로 해석(호환).
- **`slot_id` 폐기는 BAT-δ**(전략 모듈 portfolio_id 전환 후).
## REQ-062 — 수량 정본 + 불일치 해소
- 청산 수량 = **브로커 잔고 정본**: safety_sell/execution 의 매도 수량 조회 경로가 broker.get_balance 기반인지 확인하고, 아니면 정합(미러 stale 무관 정확).
- `reconcile_positions`(REQ-031) 확장: 기존 hub↔broker(symbol 합산) 비교에 **코어 슬롯 holding** 추가.
- A. 코어 holding / broker 0 → 슬롯 `error`(reconcile_mismatch) + WARN. (운영자 resolve)
- B. 코어 flat / broker 보유 → WARN/alert (운영자 결정; 자동 편입·청산 금지).
- C. 둘 다 보유, 수량만 → 미러 갱신 + warning(기존 set_warning).
- D. hub 만 차이 → 기존 write-back(dcy-018) 경로.
- **자동 청산/자동 편입 금지(issue #15)** — 표면화(warning/error)·운영자 게이트만.
## REQ-063 — 운영자 slots 단일 뷰
- snapshot envelope slot dict 에 `universe_detached`(retired_at not null)·`symbol`(이미 있을 수 있음) 노출.
- `trex-core slots`: 자산 포지션(slot=자산) 단일 뷰 — status·holding·universe_detached 표시. (BAT-022 slots[] 확장; 자산 중심 의미.)
## 테스트 설계
- REQ-061: sell by portfolio_id(holding→pending_sell)/not_holding/ dual-accept(slot_id 레거시)/ legacy 회귀.
- REQ-062: reconcile A(error)/B(warning)/C(미러)/D 분기 단위; 청산 수량 브로커 정본.
- REQ-063: slots 뷰 universe_detached 표시.
- 회귀: 기존 sell(slot_id)·reconcile_positions·snapshot 무파손.
## 체크포인트 결정 필요
1. **D1 — sell 계약**: dual-accept(slot_id 레거시 + portfolio_id 신규, 폐기 BAT-δ)(권장) vs hard-switch(즉시 portfolio_id 전용).
2. **D2 — REQ-062 본 배치 범위**: (i) reconcile 3자 확장 + A=error/B·C=warning + 청산 브로커 정본(권장) vs (ii) 탐지+warning 만(error/operator 라우팅은 최소).
@@ -0,0 +1,16 @@
# BAT-029 Implementation
- Status: not-started
- Batch: BAT-029
## 구현 요약
- (run-batch-delivery 진입 시 작성) design.md 체크포인트(D1/D2) 확정 후 채운다.
## 변경 내역
- (대기)
## 테스트
- (대기)
## 검증 실행
- (대기)
@@ -0,0 +1,31 @@
# BAT-029: intent 자산 키 + 수량/불일치 해소 + 운영자 단일 뷰 (dcy-020 BAT-γ)
- Status: planning
- Profile: standard
- Source Discovery: dcy-020
- Included REQ: REQ-061 (intent 자산 키 통일), REQ-062 (수량 hub 정본+미러·불일치 해소), REQ-063 (운영자 slots 단일 뷰)
## Summary
- dcy-020 의 **3번째 구현 배치(BAT-γ)**. 매수/매도 intent 를 자산(portfolio_id) 키로 통일하고, 수량 정본을 hub+미러로·청산 수량을 브로커 정본으로 두며 코어 holding↔broker/hub 비정상 불일치를 표면화한다. 운영자 `slots` 를 자산 포지션 단일 뷰로.
- **외부 계약 영향(NFR-4)**: sell 이 현재 `slot_id` 필수 → 자산 키(`portfolio_id`)로 전환. 라이브 전략 모듈을 깨지 않기 위해 **dual-accept**(slot_id 레거시 + portfolio_id 신규)로 도입하고, `slot_id` 완전 폐기는 BAT-δ.
## Scope
- In Scope:
- REQ-061: sell intent 에 `portfolio_id` 수용(자산 중심 모드) + already_active/not_holding 규칙. slot_id 는 레거시 dual-accept(폐기 BAT-δ).
- REQ-062: `reconcile_positions` 확장 — 코어 슬롯 holding ↔ broker/hub 3자 비교, 불일치 A/B/C/D 표면화(warning/error). 청산 수량 = 브로커 잔고 정본.
- REQ-063: snapshot envelope slots[] 에 `universe_detached`/`retired`·symbol, `trex-core slots` 자산 포지션 단일 뷰.
- runtime-flows.md 부분 갱신.
- Out of Scope:
- sell `slot_id` 완전 폐기·literal inactive 전면화·컬럼 drop·자산 중심 default → BAT-δ
- 라이브 cutover (REQ-064 → BAT-δ)
- 자동 청산/자동 편입(불일치 해소는 표면화·운영자 게이트만, issue #15)
## 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). BAT-027/028 후속, BAT-δ 선행.
- **핵심 리스크**: REQ-061 은 외부 전략 모듈 계약 변경(NFR-4) → dual-accept 로 무중단 도입. REQ-062 불일치 해소는 자동 청산 금지(issue #15). design 체크포인트에서 계약 호환·divergence 범위 확정 후 구현.
@@ -0,0 +1,38 @@
# BAT-029 Planning
- Status: confirmed
- Batch: BAT-029
- Profile: standard
## Included REQ
- REQ-061 (Interface, Approved): intent 자산(portfolio_id) 키 통일 + sell-by-slot_id 폐기(점진) + already_active/not_holding.
- REQ-062 (Integration, Approved): 수량 hub 정본+미러·청산=브로커·holding↔broker/hub 불일치 해소.
- REQ-063 (Interface, Approved): 운영자 slots 자산 포지션 단일 뷰.
## Delivery Plan
1. REQ-061: `SellIntent.portfolio_id: Optional[str]` 추가(slot_id Optional 로 완화). messaging_handlers sell 파싱이 portfolio_id|slot_id dual-accept. `_admit_sell` 자산 중심 분기 — portfolio_id 면 `find_slot_by_portfolio` → holding 이면 pending_sell, 아니면 `not_holding`. legacy slot_id 경로 유지. CLI `exit` 가 portfolio_id 수용(+slot_id 호환).
2. REQ-062: `reconcile_positions` 확장 — 코어 슬롯 holding 집합을 broker/hub 와 3자 비교. A(코어 holding/broker 0)=슬롯 error+warning, B(코어 flat/broker 보유)=warning/alert(운영자), C(수량만)=미러 갱신+warning, D(hub만)=기존 write-back. 청산 경로(safety_sell/execution)가 매도 수량을 브로커 잔고에서 조회(이미 그렇다면 확인). 자동 청산/편입 금지.
3. REQ-063: snapshot envelope slot dict 에 `universe_detached`(retired_at not null)·symbol 추가, `trex-core slots` 단일 뷰(자산 포지션·tombstone 표시).
4. runtime-flows.md 부분(intent 자산 키·불일치 해소).
5. 단위(sell 자산 키/dual-accept/divergence/slots 뷰) + 회귀 + ruff (+ test DSN).
## Dependencies
- Internal: BAT-028(find_slot_by_portfolio·count_active·reconcile), BAT-027(상태머신). dcy-005 reconcile_positions(REQ-031)·snapshot envelope.
- External: 전략 모듈(외부 repo) — dual-accept 로 무중단, portfolio_id 전환은 전략 측 후속.
## Design Gate
- Design Required Before Implementation: yes
- Reason: (a) sell 계약 변경은 **외부 전략 모듈** 영향(NFR-4) — dual-accept vs hard-switch 를 설계에서 확정해 무중단 보장. (b) divergence 해소(A/B/C/D)는 기존 reconcile_positions·error 상태·safety-sell 과 교차하고 자동 청산 금지 경계를 못박아야 안전. (c) 청산 수량 브로커 정본 경로가 이미 그러한지/바꿔야 하는지 확인. → design.md 확정 후 구현 전 체크포인트.
## Milestones
1. M1 — REQ-061 sell 자산 키 dual-accept + admission 분기 + CLI
2. M2 — REQ-062 reconcile divergence 확장 + 청산 브로커 정본
3. M3 — REQ-063 slots 단일 뷰 + runtime-flows + 회귀/ruff
## Risks
- R-contract (High): sell 계약 변경으로 라이브 전략 매도 불능. 대응: dual-accept(slot_id 유지), 전환은 BAT-δ.
- R-divergence (Medium): 불일치 해소가 자동 청산/오격리. 대응: 표면화만(warning/error/operator), 자동 청산 금지(issue #15), 회귀 테스트.
## Notes
- 자산 중심 모드(max_active_slots 설정)에서 자산 키 admission 활성; legacy 모드는 기존 slot_id sell 유지.
- 본 배치 green 기준: 단위 전체 회귀(legacy sell 무파손) + 신규 자산 키 sell/divergence/slots 단위.
@@ -0,0 +1,13 @@
# BAT-029 Verification
- Status: not-started
- Batch: BAT-029
## Acceptance Mapping
- (draft-batch-verification / confirm-batch-verification 단계에서 작성)
## Evidence
- (대기)
## Result
- Ready for Release: false (구현 전)
+1
View File
@@ -36,3 +36,4 @@
| 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/) |
| BAT-028 | standard | planning | REQ-058, REQ-059, REQ-060 | dcy-020 (BAT-β) | [bat-028](./bat-028_20260610_universe-reconcile-and-active-admission/) |
| BAT-029 | standard | planning | REQ-061, REQ-062, REQ-063 | dcy-020 (BAT-γ) | [bat-029](./bat-029_20260610_asset-keyed-intent-and-divergence/) |