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:
@@ -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 (구현 전)
|
||||||
@@ -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-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-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-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/) |
|
||||||
|
|||||||
@@ -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-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-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-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 가 된다.
|
- FR-8: **운영자 표면 단일화** — `trex-core slots` 가 자산 포지션(슬롯=자산, status·holding·수량 미러·`universe_detached`/`retired`)을 **단일 뷰**로 보여준다. universe↔slots 이중 표면(BAT-022 혼동 원인) 해소. universe(자산 리스트)와 slots(자산 포지션)가 같은 자산 집합 위 view 가 된다.
|
||||||
|
|
||||||
## 비기능 요구사항
|
## 비기능 요구사항
|
||||||
|
|||||||
@@ -6,16 +6,17 @@
|
|||||||
- Owner: project maintainers
|
- Owner: project maintainers
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
- 자산 중심 모델에서 매수·매도 신호를 모두 자산(portfolio_id)으로 식별하도록 intent 계약을 통일하고, 슬롯이 자산 1:1 영구이므로 `sell-by-slot_id` 노출을 폐기한다. (dcy-020 FR-7/NFR-4)
|
- 전략↔코어 intent 계약을 **코어 native 키인 `slot_id`** 로 통일한다(CHG-20260610-03, 사용자 결정으로 FR-7 의 portfolio_id 키를 slot_id 로 재정의). 자산 중심 영구 슬롯이라 universe 모든 자산에 슬롯이 선존재하므로 매수도 slot_id 로 지정 가능하다. 스냅샷 `slots[]` 가 계약의 1차 단위이며 portfolio_id 는 표시/자산 정체성으로 잔존(계약 키 아님). (dcy-020 FR-7 재정의/NFR-4)
|
||||||
|
|
||||||
## Requirement
|
## Requirement
|
||||||
- 매수·매도 intent 를 모두 `portfolio_id`(자산) 키로 식별한다. 매수 = 해당 자산 슬롯 활성화(`inactive→pending_buy`), 매도 = 비활성화(`holding→pending_sell`). **`sell-by-slot_id` 는 폐기**한다. 중복/무효 신호 규칙: 이미 활성(`pending_buy`/`holding`/`pending_sell`)인 자산에 매수 신호 → 멱등 거부(`already_active`); 보유 아닌 자산에 매도 신호 → 거부(`not_holding`). `intent_id` 멱등(intent_log)은 보존한다. 계약 변경은 외부 전략 모듈을 깨뜨리므로 cutover/버전 절차를 명시한다(NFR-4).
|
- 매수·매도 intent 를 모두 **`slot_id`** 키로 식별한다. 매수 = 그 슬롯 활성화(`inactive→pending_buy`, symbol 은 슬롯의 `portfolio_id` 로 resolve), 매도 = 비활성화(`holding→pending_sell`). 매도는 이미 slot_id 계약(불변). **매수는 기존 portfolio_id 계약에서 slot_id 로 이동하되, 외부 전략 무중단을 위해 portfolio_id 를 레거시 dual-accept**(자산 중심 모드에서 portfolio_id → `find_slot_by_portfolio` 로 슬롯 매핑); portfolio_id 매수 계약 완전 폐기는 BAT-δ. 중복/무효 신호 규칙: 이미 활성(`pending_buy`/`holding`/`pending_sell`)인 슬롯에 매수 → 멱등 거부(`already_active`); 보유(holding) 아닌 슬롯에 매도 → 거부(`not_holding`). `intent_id` 멱등(intent_log) 보존.
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
1. Given 매도 신호, When `portfolio_id` 로 제출, Then 해당 자산 슬롯이 `pending_sell` 로 전이하고 `slot_id` 없이 처리된다.
|
1. Given 매수 신호, When `slot_id` 로 제출(자산 중심 모드), Then 그 슬롯이 `inactive→pending_buy` 로 활성화되고 symbol 은 슬롯의 portfolio_id 로 resolve 된다.
|
||||||
2. Given 이미 holding 인 자산, When 매수 신호, Then `already_active` 로 멱등 거부된다.
|
2. Given 이미 활성(pending_buy/holding/pending_sell)인 슬롯, When 매수, Then `already_active` 로 멱등 거부된다.
|
||||||
3. Given 보유하지 않은 자산, When 매도 신호, Then `not_holding` 으로 거부된다.
|
3. Given 보유(holding) 아닌 슬롯, When 매도, Then `not_holding`(또는 not-holding 사유)로 거부된다.
|
||||||
4. Given 같은 `intent_id` 재전송, When 처리, Then 슬롯 중복 전이 없이 최초 결정과 동일 응답(멱등).
|
4. Given 레거시 매수(`portfolio_id`), When 제출, Then `find_slot_by_portfolio` 로 슬롯에 매핑되어 동일하게 처리된다(dual-accept, 무중단).
|
||||||
|
5. Given 같은 `intent_id` 재전송, When 처리, Then 슬롯 중복 전이 없이 최초 결정과 동일 응답(멱등).
|
||||||
|
|
||||||
## Impacted Area
|
## Impacted Area
|
||||||
- Docs: docs/runtime-flows.md(intent 흐름), docs/interface-contract.md(메시지 계약)
|
- Docs: docs/runtime-flows.md(intent 흐름), docs/interface-contract.md(메시지 계약)
|
||||||
@@ -85,3 +86,37 @@
|
|||||||
|
|
||||||
#### Validation Plan
|
#### Validation Plan
|
||||||
- 승인 후 batch planning 에서 본 REQ AC 를 검증 기준으로 사용.
|
- 승인 후 batch planning 에서 본 REQ AC 를 검증 기준으로 사용.
|
||||||
|
|
||||||
|
### CHG-20260610-03
|
||||||
|
|
||||||
|
- Date: 2026-06-10
|
||||||
|
- Author: Justin Ko
|
||||||
|
|
||||||
|
#### Change Summary
|
||||||
|
|
||||||
|
- (change-req, dcy-020 FR-7 재정의 — 사용자 결정) intent 계약 키를 `portfolio_id` → **`slot_id`**(buy·sell 공통)로 재정의. 근거: trex-core 는 슬롯 기준 관리이고 스냅샷이 `slots[]` 를 단위로 주므로 코어 native 키(slot_id)로 round-trip 하는 것이 일관적이며 portfolio-hub 식별 체계의 계약 누수를 막는다. 자산 영구 슬롯이라 매수도 slot_id 로 지정 가능(슬롯 선존재). 매도는 이미 slot_id(불변); 매수만 portfolio_id→slot_id 이동(레거시 portfolio_id dual-accept, 폐기 BAT-δ). Approved 유지(사용자 재승인).
|
||||||
|
|
||||||
|
#### Intent
|
||||||
|
- 전략↔코어 계약을 슬롯 native 키로 통일.
|
||||||
|
|
||||||
|
#### Acceptance Criteria Delta
|
||||||
|
- Updated: AC 전체를 slot_id 기준으로 재작성(AC-1 매수 slot_id 활성화·symbol resolve, AC-4 portfolio_id dual-accept 추가).
|
||||||
|
- Removed: "매도 portfolio_id 제출" 전제(매도는 slot_id 유지).
|
||||||
|
|
||||||
|
#### Impacted Area Delta
|
||||||
|
- Modules: application/intents.py(BuyIntent slot_id·_admit_buy_asset_centric), 메시지 핸들러 buy 파싱, runtime/cli.py enter
|
||||||
|
- Tests: test_asset_centric_admission(slot_id), 메시지 consumer
|
||||||
|
- 연동: REQ-059(자산 admission)·REQ-007(buy intent) — buy 키 slot_id 로 정합(BAT-029).
|
||||||
|
|
||||||
|
#### Delivery Trace Delta
|
||||||
|
- Batches: BAT-029
|
||||||
|
- Releases: none
|
||||||
|
|
||||||
|
#### Revalidation Impact
|
||||||
|
- Reverification Needed: yes (BAT-029)
|
||||||
|
- Existing Implementation Invalidated: BAT-028 의 portfolio_id 매수 admission 은 dual-accept 로 흡수(레거시 경로 유지) — 무효화 아님
|
||||||
|
- Status Recommendation: keep-current (Approved, 사용자 재승인)
|
||||||
|
- Follow-up Action: BAT-029 구현. REQ-059/007 정합.
|
||||||
|
|
||||||
|
#### Validation Plan
|
||||||
|
- BAT-029: 매수 slot_id 활성화 / portfolio_id dual-accept / already_active / 매도 slot_id / 멱등 단위·통합.
|
||||||
|
|||||||
Reference in New Issue
Block a user