# req-061: intent 계약 자산(portfolio_id) 키 통일 + 중복/무효 규칙 - Status: Implemented - Type: Interface - Priority: High - Owner: project maintainers ## Intent - 전략↔코어 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 - 매수·매도 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 1. Given 매수 신호, When `slot_id` 로 제출(자산 중심 모드), Then 그 슬롯이 `inactive→pending_buy` 로 활성화되고 symbol 은 슬롯의 portfolio_id 로 resolve 된다. 2. Given 이미 활성(pending_buy/holding/pending_sell)인 슬롯, When 매수, Then `already_active` 로 멱등 거부된다. 3. Given 보유(holding) 아닌 슬롯, When 매도, Then `not_holding`(또는 not-holding 사유)로 거부된다. 4. Given 레거시 매수(`portfolio_id`), When 제출, Then `find_slot_by_portfolio` 로 슬롯에 매핑되어 동일하게 처리된다(dual-accept, 무중단). 5. Given 같은 `intent_id` 재전송, When 처리, Then 슬롯 중복 전이 없이 최초 결정과 동일 응답(멱등). 6. Given 보유(holding) 슬롯, When **증액 매수 intent**(qty+price)를 제출, Then `holding→pending_buy` 로 추가 매수가 집행된다(dcy-021, 이미 active 라도 증액은 허용 — `already_active` 예외). 7. Given 보유 슬롯, When **부분 매도 intent**(qty+price)를 제출, Then 명시 수량으로 `holding→pending_sell` 집행된다(집행 수량 정책 REQ-062). 사이징 intent 는 명령형(전략이 qty+price 계산·전송), core 는 target/의도 수량을 저장·추적하지 않는다. ## Impacted Area - Docs: docs/runtime-flows.md(intent 흐름), docs/interface-contract.md(메시지 계약) - Modules: trex_core/application/intents.py, 메시지 핸들러/스키마 - Tests: tests/application/test_messaging_intent_consumer.py, tests/application/test_*intent*.py ## Notes - 기존 REQ-007(intent API) 재정의 change-req 동반(sell-by-slot_id 폐기·자산 키 통일). NFR-4(전략 계약 호환): 외부 전략 모듈 동시 전환 또는 한시적 하위호환 — REQ/배치 cutover 에서 명시. - 출처: dcy-020 FR-7, NFR-4. ## Change Log ### CHG-20260610-01 - Date: 2026-06-10 - Author: Justin Ko #### Change Summary - dcy-020 FR-7/NFR-4 로부터 최초 생성(Proposed). intent 자산(portfolio_id) 키 통일 + sell-by-slot_id 폐기 + already_active/not_holding. #### Intent - 매수·매도 신호를 자산 키로 통일하는 계약 변경을 backlog 정규화. #### Acceptance Criteria Delta - Added: 매도 자산키/already_active/not_holding/intent_id 멱등 4개 기준 - Updated: 없음 - Removed: 없음 #### Impacted Area Delta - Docs: docs/runtime-flows.md, docs/interface-contract.md - Modules: application/intents.py, 메시지 핸들러/스키마 - Tests: test_messaging_intent_consumer.py, intent 테스트 #### Delivery Trace Delta - Batches: none - Releases: none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no (REQ-007 재정의 change-req 로 기존 폐기) - Status Recommendation: keep-current - Follow-up Action: confirm-req. NFR-4 외부 전략 계약 cutover 명시. #### Validation Plan - 자산 키 매도/거부 규칙/멱등 단위·통합 테스트 green. ### CHG-20260610-02 - Date: 2026-06-10 - Author: Justin Ko #### Change Summary - Status Proposed → Approved (confirm-req). 필수 필드·구체 AC 충족, source dcy-020(confirmed, handoff 승인)과 모순 없음. #### Intent - delivery input 으로 승격. #### Acceptance Criteria Delta - Added: 없음 / Updated: 없음 / Removed: 없음 #### Impacted Area Delta - Docs: docs/srs/index.md (Register Approved) / Modules: 없음 / Tests: 없음 #### Delivery Trace Delta - Batches: none / Releases: none #### Revalidation Impact - Reverification Needed: no / Existing Implementation Invalidated: no / Status Recommendation: keep-current / Follow-up Action: suggest-batch-reqs / draft-batch. #### Validation Plan - 승인 후 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 / 멱등 단위·통합. ### CHG-20260610-IMPL - Date: 2026-06-10 - Author: Justin Ko - Type: status - Summary: Approved → Implemented (confirm-req-implemented). 근거: BAT-029(+BAT-030 trading_date 제거·e2e slot_id 매수) — 모든 AC 가 release-candidate 배치 verification 의 evidence 와 연결됨(단위 367/PG 29/MQ 21 passed + run-the-app 자산 중심 e2e). Blocking Issues 없음. ### CHG-20260610-04 - Date: 2026-06-10 - Author: Justin Ko #### Change Summary - dcy-021(포지션 사이징) 반영: intent 계약에 **사이징 intent**(보유 슬롯 증액 매수 / 부분 매도)를 추가. 명령형 — 전략이 스냅샷 cash+quantity 로 **정수 주식 수(qty)+가격(price)** 을 계산해 보내고, core 는 실행만 한다(target/의도 수량 미저장·미추적). 증액은 이미 active 슬롯에도 허용(전량 매수 활성화의 `already_active` 와 구분). #### Intent - 보유 자산의 수량 조정을 기존 명령형 buy/sell 계약의 최소 additive 확장으로 표현(dcy-021 OQ-1/OQ-2 확정). #### Acceptance Criteria Delta - Added: AC-6(증액 매수 intent → holding→pending_buy), AC-7(부분 매도 intent qty → holding→pending_sell, 명령형·core 미저장) - Updated: 없음(기존 활성/청산 계약 보존) - Removed: 없음 #### Impacted Area Delta - Docs: docs/interface-contract.md(사이징 intent payload), docs/runtime-flows.md - Modules: trex_core/application/intents.py(증액/부분 매도 admission), messaging_handlers.py(payload) - Tests: tests/application/test_*intent*.py, 사이징 admission 신규 #### Delivery Trace Delta - Batches: dcy-021 신규 배치(예정) - Releases: none #### Revalidation Impact - Reverification Needed: yes (신규 사이징 계약 경로) - Existing Implementation Invalidated: no (기존 slot_id 매수·전량 매도 계약 보존, additive) - Status Recommendation: revert-to-approved (사이징 계약 미구현 — dcy-021 배치에서 구현·검증) - Follow-up Action: dcy-021 배치 구현 후 Implemented 복귀. #### Validation Plan - 증액 매수·부분 매도 intent admission 단위 + end-to-end(REQ-066). 명령형·멱등·core 미저장 검증. ### CHG-20260610-IMPL - Date: 2026-06-10 - Author: Justin Ko - Type: status - Summary: Approved → Implemented (confirm-req-implemented). 근거: BAT-031(release-candidate) verification — 단위 378 / 통합 422 passed + run-the-app 사이징 e2e(증액 가중평균·부분 매도 잔량·전량 매도) green. Blocking Issues 없음. ### CHG-20260610-05 - Date: 2026-06-10 - Author: Justin Ko #### Change Summary - (change-req, dcy-022 실행 정책) buy/sell intent 계약에 **실행 정책 필드**(optional `timeout_sec`·`slippage_pct`) 추가. `timeout_sec`: 주문 유효시간(N초 미체결 시 core 가 cancel_order 송신·롤백). `slippage_pct`: 체결 보호밴드(entry_price 기준 ±pct, entry_price 없으면 무시). 두 필드 모두 optional — 미지정 시 기존 경로 하위호환. `timeout_sec` 미지정 시 운영자 per-instance config(`default_order_timeout_sec`) 적용(미설정=OFF=무기한). Implemented → Approved 되돌림(신규 계약 필드 미구현, BAT-032 에서 재구현·confirm-req-implemented 복귀). #### Intent - 전략이 intent 제출 시 실행 정책을 함께 실어 미체결 고착을 원천 차단한다(dcy-022 FR-1). #### Acceptance Criteria Delta - Added: - AC-8: buy/sell intent 에 optional `timeout_sec` 포함 시 core 가 파싱해 슬롯에 기록하고 집행 루프가 timeout 초과 시 cancel_order 를 송신한다. - AC-9: buy/sell intent 에 optional `slippage_pct` 포함 시 `entry_price` 기준 실효 지정가를 산출해 주문을 제출한다(`entry_price` 없으면 무시). - AC-10: `timeout_sec`/`slippage_pct` 미지정 시 기존 매수/매도 경로와 동일하게 처리된다(하위호환). - Updated: 없음 - Removed: 없음 #### Impacted Area Delta - Docs: docs/interface-contract.md(payload 확장) - Modules: BuyIntent/SellIntent(+timeout_sec/slippage_pct), messaging_handlers.py(파싱), intents.py(transit 전달), slot models(+timeout_sec/slippage_pct 컬럼), execution.py(dispatch slippage·poll timeout) - Tests: timeout/slippage 파싱·집행 단위, 하위호환 회귀 #### Delivery Trace Delta - Batches: BAT-032 - Releases: none #### Revalidation Impact - Reverification Needed: yes (신규 계약 필드 경로) - Existing Implementation Invalidated: no (기존 slot_id 매수·매도 계약 보존, additive) - Status Recommendation: revert-to-approved (신규 필드 미구현 — BAT-032 에서 구현·검증) - Follow-up Action: BAT-032 구현 후 Implemented 복귀. #### Validation Plan - timeout_sec/slippage_pct 파싱·슬롯 기록·집행 단위. 미지정 회귀. end-to-end timeout 취소 시나리오. ### CHG-20260611-IMPL - Date: 2026-06-11 - Author: Justin Ko - Type: status - Summary: Approved → Implemented (confirm-req-implemented). 근거: BAT-032(release-candidate) verification — 실행 정책(timeout_sec·slippage_pct) 계약·집행·운영자 cancel 백스톱 전 AC acceptance mapping 존재(신규 71 tests), 단위 425 passed 회귀 없음, baseline 2문서 수동 확인(M5). Blocking Issues 없음.