From e2a71e50e21e64b6f5c982084f0aba9b8bdf140e Mon Sep 17 00:00:00 2001 From: Justin Ko Date: Wed, 10 Jun 2026 10:28:38 +0900 Subject: [PATCH] =?UTF-8?q?docs(req):=20dcy-020=20confirm=20+=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=82=AC=EC=96=91(FR-6~8/NFR-4~5/OQ-8)=20+=20draft?= =?UTF-8?q?-req=20REQ-056~065?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dcy-020(자산 중심 영구 슬롯 모델) Requirements phase. - 추가 사양: FR-6(상태머신 재정의), FR-7(intent 자산 키), FR-8(운영자 단일뷰), NFR-4(전략 계약 호환), NFR-5(불일치 감지 주기), OQ-8(over-cap=신규 차단·기존 유지), dcy-019 산출물 제거 명시. review-discovery 정리 반영. - confirm-discovery: 상태 draft→confirmed, Handoff 승인, Ready=true, Confirmed By=Justin Ko, 2026-06-10. index 행 confirmed. - draft-req: 신규 REQ-056~065(전부 Proposed) 생성 - 056 영구 슬롯 스키마 / 057 상태머신+인스턴스 lock / 058 universe reconcile - 059 capacity·admission / 060 max_active_slots TOML / 061 intent 자산 키 - 062 수량 hub 정본+불일치 해소 / 063 slots 단일뷰 / 064 cutover / 065 baseline - srs/index Next ID→066, Register 10행, Recent Change Log 갱신 - dcy-020 생성된 REQ 참조 갱신 기존 REQ-002/003/005/006/007/048 폐기·재정의 change-req 는 후속(문서 단계). Co-Authored-By: Claude Opus 4.8 --- ...y-020_20260610_asset-centric-slot-model.md | 61 ++++++++++++------ docs/discovery/index.md | 2 +- .../req-060_max-active-slots-config.md | 61 ++++++++++++++++++ .../req-056_asset-permanent-slot-schema.md | 64 +++++++++++++++++++ ...eq-065_asset-centric-baseline-alignment.md | 61 ++++++++++++++++++ ...q-058_universe-slot-reconcile-lifecycle.md | 63 ++++++++++++++++++ ...2_quantity-hub-authority-and-divergence.md | 62 ++++++++++++++++++ .../req-057_asset-slot-state-machine.md | 64 +++++++++++++++++++ .../req-059_active-capacity-admission.md | 63 ++++++++++++++++++ .../req-061_asset-keyed-intent-contract.md | 62 ++++++++++++++++++ .../req-063_unified-operator-slot-view.md | 61 ++++++++++++++++++ .../req-064_pool-to-permanent-slot-cutover.md | 62 ++++++++++++++++++ docs/srs/index.md | 14 +++- 13 files changed, 676 insertions(+), 24 deletions(-) create mode 100644 docs/srs/Configuration/req-060_max-active-slots-config.md create mode 100644 docs/srs/Data/req-056_asset-permanent-slot-schema.md create mode 100644 docs/srs/Documentation/req-065_asset-centric-baseline-alignment.md create mode 100644 docs/srs/Integration/req-058_universe-slot-reconcile-lifecycle.md create mode 100644 docs/srs/Integration/req-062_quantity-hub-authority-and-divergence.md create mode 100644 docs/srs/Interface/req-057_asset-slot-state-machine.md create mode 100644 docs/srs/Interface/req-059_active-capacity-admission.md create mode 100644 docs/srs/Interface/req-061_asset-keyed-intent-contract.md create mode 100644 docs/srs/Interface/req-063_unified-operator-slot-view.md create mode 100644 docs/srs/Migration/req-064_pool-to-permanent-slot-cutover.md diff --git a/docs/discovery/dcy-020_20260610_asset-centric-slot-model.md b/docs/discovery/dcy-020_20260610_asset-centric-slot-model.md index e013bb4..e2991d1 100644 --- a/docs/discovery/dcy-020_20260610_asset-centric-slot-model.md +++ b/docs/discovery/dcy-020_20260610_asset-centric-slot-model.md @@ -1,14 +1,14 @@ # 0. 문서 상태 이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다. -- 상태: draft +- 상태: confirmed - 문서 ID: dcy-020_20260610_asset-centric-slot-model - 이슈명: 자산 중심 슬롯 모델 재설계 — portfolio-hub universe 와 1:1 슬롯 + 활성 슬롯 개수 제어 (일반 슬롯 풀 모델 대체) - 작성 시각(KST): 2026-06-10 01:04 KST -- 마지막 갱신 시각(KST): 2026-06-10 01:04 KST (OQ-1 Answered: FCFS+무치환; FR-3/FR-4 holding/수량 정본 분리 결정; OQ-5 추가: 불일치 해소 정책; OQ-3 Answered: 영구 슬롯 + trading_date 강등 + dcy-014 폐기; OQ-2 Answered: 생성 즉시·제거 soft-retire/tombstone·degrade 보류; OQ-4 Answered: 포지션 사이징 분리·토대만; OQ-5 Answered: 불일치 해소 ground truth=브로커·표면화·운영자 게이트; A-1 CONFIRMED: 자산 중심 대전제; OQ-6 추가·Answered: M=per-instance TOML; review 정리 반영(§3 시나리오·§4·§11 현행화); OQ-7 추가·Answered: 공유 DB + instance_key 스코프(UNIQUE(instance_key,portfolio_id)) + advisory lock 인스턴스 스코프화 — OQ 전부 종료, confirm-discovery 준비) +- 마지막 갱신 시각(KST): 2026-06-10 01:04 KST (OQ-1 Answered: FCFS+무치환; FR-3/FR-4 holding/수량 정본 분리 결정; OQ-5 추가: 불일치 해소 정책; OQ-3 Answered: 영구 슬롯 + trading_date 강등 + dcy-014 폐기; OQ-2 Answered: 생성 즉시·제거 soft-retire/tombstone·degrade 보류; OQ-4 Answered: 포지션 사이징 분리·토대만; OQ-5 Answered: 불일치 해소 ground truth=브로커·표면화·운영자 게이트; A-1 CONFIRMED: 자산 중심 대전제; OQ-6 추가·Answered: M=per-instance TOML; review 정리 반영(§3 시나리오·§4·§11 현행화); OQ-7 추가·Answered: 공유 DB + instance_key 스코프(UNIQUE(instance_key,portfolio_id)) + advisory lock 인스턴스 스코프화; FR-6/7/8 추가(상태머신·intent 자산키·운영자 단일뷰), NFR-4/5 추가(전략 계약 호환·불일치 감지 주기), dcy-019 산출물 제거 명시, OQ-8 추가·Answered(over-cap=신규 차단·기존 유지); confirm-discovery 승인(2026-06-10 10:00 KST, Justin Ko) → 상태 confirmed; draft-req: REQ-056~065 생성) - 대체됨: 없음 - 후속 Discovery 참조: 없음 -- 생성된 REQ 참조: 없음 +- 생성된 REQ 참조: REQ-056(영구 슬롯 스키마), REQ-057(상태머신/lock), REQ-058(universe reconcile), REQ-059(capacity/admission), REQ-060(max_active_slots TOML), REQ-061(intent 자산 키), REQ-062(수량/불일치), REQ-063(slots 단일 뷰), REQ-064(cutover), REQ-065(baseline). 기존 REQ-002/003/005/006/007/048 등은 폐기/재정의 change-req 동반(예정). # 1. 계획 상태 요약 현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다. @@ -68,7 +68,10 @@ - 슬롯 lifecycle 재설계: 일일 count preallocation(dcy-014) → **universe 기반 슬롯 생성/소멸**(스냅샷 refresh 와 결합). idle 풀·`find_allocatable_slot`·closed 재할당(dcy-019) 폐기/대체. - 슬롯 영속성 전환(OQ-3): 일일 리셋 → **영구 슬롯**(키=`portfolio_id`, `UNIQUE`, 날짜 없음). `daily_slot.trading_date` 컬럼 드롭/재의미, trading_date 는 이벤트/원장/정책의 시간 속성으로 강등. 일일 daily_slot 행 → 자산당 영구 1행 cutover + 보유 carry-over. - capacity 제어 전환: `available_slots`(유휴 수) → "**동시 활성 holding 최대 M개**" 정책. 활성 수 == M 이면 신규 신호 거부(FCFS·무치환, OQ-1). M 값은 per-instance TOML 설정에서 읽는다(OQ-6). -- 상태머신 재해석: 익명 슬롯의 IDLE/CLOSED 중심 → 자산 슬롯의 "비활성/활성" 중심(활성=진입, 종료=전량 청산). **HOLDING→HOLDING 수량 변경·부분 매도는 미포함**(포지션 사이징 issue #1 은 OQ-4 로 분리, 토대만). 수량 가변 전제는 모델에 남겨 후속 사이징이 깨끗이 얹히게 한다. +- 상태머신 재해석(FR-6): 익명 슬롯의 IDLE/CLOSED → 영구 슬롯의 **단일 `inactive`** 로 합침(`closed→idle` 복구 전이 제거), 집합 = `inactive→pending_buy→holding→pending_sell→inactive`(+`error`). 활성=진입, 종료=전량 청산. **HOLDING→HOLDING 수량 변경·부분 매도 미포함**(포지션 사이징 OQ-4 분리). 수량 가변 전제는 모델에 남김. +- intent 계약 통일(FR-7): 매수·매도 모두 portfolio_id(자산) 키, `sell-by-slot_id` 폐기, 중복/무효 신호 멱등 거부(`already_active`/`not_holding`). +- 운영자 표면 단일화(FR-8): `trex-core slots` = 자산 포지션 단일 뷰(universe↔slots 이중 표면 해소, BAT-022). +- dcy-019 산출물 제거(FR-5): `find_allocatable_slot`·`closed→pending_buy` 전이·slot `entry_count` 제거(FCFS·영구 슬롯이 대체). `daily_symbol_entries` 폐기. - baseline 문서(`docs/project-structure.md`/`docs/runtime-flows.md`/`docs/data-model.md`) 전면 갱신 + 기존 dcy-001/002/014/019 와의 대체/재해석 관계 명시. ## 영향 범위 @@ -83,7 +86,10 @@ - `trex_core/domain/slot/state.py`·`service.py` — advisory lock 인스턴스 스코프화(`(SLOT_LOCK_ID, hash(instance_key))`, NFR-1 재정의, OQ-7) - `trex_core/runtime/config.py`(`DaemonInstanceConfig`) + `config/*.toml` — 동시 활성 최대치 M 설정 키(예: `max_active_slots`) 추가(OQ-6, REQ-048 per-instance TOML 계열) - 문서 영향: - - `docs/data-model.md`(daily_slot 재설계), `docs/runtime-flows.md`(admission→활성화 흐름), `docs/project-structure.md`(슬롯 모델) + - `docs/data-model.md`(daily_slot 재설계: instance_key·retired_at·entry_count 제거·trading_date 강등), `docs/runtime-flows.md`(admission→활성화 흐름·reconcile lifecycle·불일치 해소), `docs/project-structure.md`(슬롯 모델) + - `trex_core/domain/slot/state.py`·`models.py` — 상태 집합 재정의(FR-6: idle/closed→inactive), `entry_count` 제거 + - `trex_core/application/intents.py`·메시지 계약 — intent 자산 키 통일·sell-by-slot_id 폐기(FR-7); `trex_core/runtime/cli.py` slots 단일 뷰(FR-8) + - (제거) `daily_symbol_entries` 테이블·`find_allocatable_slot`·`closed→pending_buy` 전이(dcy-019 산출물) - 운영 영향: - 슬롯 수가 universe 크기에 연동, capacity 운영이 "활성 M개" 정책으로 전환. 마이그레이션·이행 절차 필요. 기존 풀 모델 운영 가정 변경. @@ -95,7 +101,10 @@ - FR-2: capacity 는 "동시 활성(holding) 슬롯 최대 M개"로 제어한다. 활성화는 **FCFS(신호 도착 순)**이며 활성 수 == M 이면 새 신호를 **거부(`capacity_full`)**한다 — 코어는 자리를 만들려고 기존 holding 을 자율 청산(치환)하지 않는다(OQ-1). 도착 순서는 단일 writer 직렬화로 결정적이고, 동시 후보 tie-break 은 `updated_at` asc → `portfolio_id`(alpha 비사용). capacity 를 최고 기회에 배분하는 능동 선택이 필요하면 전략의 선언형 target(OQ-4/issue #1)으로 처리한다. **M 값은 per-instance TOML 설정(`config/*.toml`, 예: `max_active_slots`)에서 읽는다**(REQ-048 per-instance TOML 계열, OQ-6) — 인스턴스별 독립 운영. - FR-3: 매수/매도 신호는 익명 슬롯 배정이 아니라 **해당 자산 슬롯의 활성화/비활성화**로 처리한다. 동시성·단일 writer·불변식(advisory lock)을 보존한다. **holding(포지션 열림 여부)은 코어 슬롯의 로컬 정본**으로, 체결 시 동기 set·매도 체결 시 동기 clear 한다 — portfolio-hub `asset_quantity` 로 판단하지 않는다. 근거: (1) 자율 안전 매도(forced_close/stop_loss/take_profit/market_close/watchdog/kill-switch)는 portfolio-hub 불가 시에도 독립 동작해야 하므로(REQ-024) 보유 사실을 코어 durable 상태에서 알아야 한다; (2) universe 에서 자산이 빠지면 `get_portfolios` 에서 사라져 `asset_quantity` 신호가 증발하지만 로컬 holding 은 생존(OQ-2 고아 포지션 방지); (3) hub 수량은 ledger outbox 비동기 전파(dcy-015)라 지연되므로 안전 판단의 primary 신호로 부적합; (4) capacity(M개, FR-2) 카운트의 원자성은 단일 writer 슬롯 상태라야 보장. (사용자 결정 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 의 폐기/재해석 범위를 명시한다. +- 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-8: **운영자 표면 단일화** — `trex-core slots` 가 자산 포지션(슬롯=자산, status·holding·수량 미러·`universe_detached`/`retired`)을 **단일 뷰**로 보여준다. universe↔slots 이중 표면(BAT-022 혼동 원인) 해소. universe(자산 리스트)와 slots(자산 포지션)가 같은 자산 집합 위 view 가 된다. ## 비기능 요구사항 성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다. @@ -103,6 +112,8 @@ - NFR-1: 동시성·일관성 — 슬롯 활성화/수량 변경은 advisory lock/단일 트랜잭션으로 경합 없이 처리(REQ-002 NFR-1 계승). **공유 DB 다중 인스턴스에서 단일 writer 불변식은 인스턴스 단위로 재정의**한다: advisory lock 을 인스턴스 스코프화(예: `pg_advisory_xact_lock(SLOT_LOCK_ID, hash(instance_key))`)해 인스턴스 간 교차 경합(한 인스턴스 전이가 타 인스턴스를 막음)을 제거한다(OQ-7). - NFR-2: 이행 안전 — 기존 풀 모델 데이터/운영에서 새 모델로의 마이그레이션이 검증된 매수/매도 경로(REL-001~004)를 회귀시키지 않거나, 명확한 cutover 절차를 갖는다. - NFR-3: universe 결합 견고성 — portfolio-hub 불가/degraded 시 슬롯 생성·은퇴를 **전부 보류**하고 마지막 집합을 유지한다(`SnapshotStore` degraded 신호 재사용). fetch 성공이라도 universe 가 N→0/급격 대량 감소면 글리치로 의심해 **mass-retire 보류 + 경고**(정상 단건 제거와 구분). 보유 중 자산은 universe 누락에도 tombstone 으로 보호되어 포지션을 잃지 않는다(OQ-2). 코어 holding↔브로커/hub 의 **비정상 불일치**는 브로커를 ground truth 로 감지(REQ-031 reconcile 확장)하되 조용한 자동 교정 없이 warning/`error`+운영자로 표면화하고, 실주문 발생은 운영자 게이트로 둔다(OQ-5). +- NFR-4: 전략 계약 호환 — intent 계약 변경(FR-7: 자산 키 통일, `sell-by-slot_id` 폐기)·snapshot envelope 의미 변경(FR-8)이 외부 전략 모듈을 깨뜨리므로, **계약 버전/cutover 절차**를 REQ/배치에서 명시한다(전략 모듈 동시 전환 또는 한시적 하위호환). 슬롯 선택은 여전히 core 책임(전략은 자산 신호만, A-3 계승). +- NFR-5: 불일치 감지 주기 — 코어 holding↔브로커 비정상 불일치(OQ-5 Case B, 미보호 고아 포지션)는 안전 민감이므로, 감지 reconcile 은 일일 universe refresh 와 **분리해 더 자주** 수행 가능해야 한다(브로커 잔고+슬롯 상태만 필요, 전체 universe fetch 불필요). 구체 주기는 설계 tuning. ## 범위 경계 이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다. @@ -191,6 +202,11 @@ - 결정: **공유 DB + `instance_key` 스코프.** (1) `daily_slot` 유일성 = **`UNIQUE(instance_key, portfolio_id)`** (+ `instance_key` 컬럼), slot-scoped 테이블 전반(slot_transition_log/intent_log/ledger_outbox/snapshot_pause/killswitch_exclusion 등) instance 스코프화 + 쿼리 필터. (2) advisory lock 을 **인스턴스 스코프화**(`(SLOT_LOCK_ID, hash(instance_key))`)해 인스턴스 간 교차 경합 제거(NFR-1 재정의). (3) `daemon_id` 는 **universe 파티션(get_portfolios) + 감사용**으로만(선택 병행 저장), **스코프 키 아님**; reconcile 시 instance_key↔daemon_id **1:1 정합 가드**(불일치 표면화, 쓰기는 daemon_id 비의존). (4) cutover: 기존 단일 인스턴스 데이터에 instance_key 태깅(R-1). - 근거: 인스턴스별 DB 는 마이그레이션/백업/관측 N배로 비합리적. 메시징이 이미 "브로커 공유 + instance_key 큐 격리"(dcy-009)라 DB 도 동일 패턴이 대칭적. **핵심: 스코프 키를 `instance_key` 로 두어 영속 스토어·락이 portfolio-hub 가용성(whoami→daemon_id)에 의존하지 않음** → 자율 안전 매도 offline 보장(REQ-024)·부팅 즉시 가용·토큰 회전에 안정. daemon_id 의 권위적 소유 이점은 1:1 가드로 흡수. - 반영: FR-1, NFR-1, §영향 범위, R-1, §10 DECIDE. (OQ-3 의 "키=portfolio_id 단독"은 본 OQ-7 로 복합키 `(instance_key, portfolio_id)`로 정련.) +- OQ-8: 운영 중 M(`max_active_slots`)을 현재 보유 수보다 작게 변경하면(over-cap) 어떻게 처리하는가. + - 상태: Answered (2026-06-10, 사용자 결정) + - 결정: **초과분은 신규 활성화만 차단, 기존 보유는 자연 종료까지 유지.** M 을 낮춰 현재 holding 수 > M 이 되어도 코어는 기존 보유를 강제 청산하지 않고(자율청산 금지, issue #15), holding 수가 M 이하로 내려갈 때까지 **새 매수 활성화만 거부**(`capacity_full`)한다. 전략의 명시적 매도로 자연 수렴. + - 근거: 강제 청산은 자율청산 원칙·single-writer 안정성과 충돌. FCFS+무치환(OQ-1)과 일관 — capacity 는 "신규 진입 게이트"이지 "보유 강제 축소 트리거"가 아니다. + - 반영: FR-2(또는 인접), §10 DECIDE. # 7. 초기 성공 기준 이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다. @@ -198,6 +214,7 @@ - S-1: 슬롯 집합이 portfolio-hub universe 자산과 1:1 로 일치한다(universe N개 → 자산 슬롯 N개, portfolio_id 키 일치). - S-2: 동시 활성(holding) 슬롯 수가 항상 ≤ M 이며, 후보 초과 시 OQ-1 규칙대로 결정적으로 활성 자산이 선택된다. - S-3: 매수/매도 신호가 익명 배정 없이 해당 자산 슬롯의 활성화/비활성화로 처리되고, 단일 writer·불변식 회귀가 없다. universe↔slots 표면이 합쳐져 운영 관측이 단일화된다. +- S-4: 재정의 상태머신(FR-6: inactive↔pending_buy↔holding↔pending_sell, idle/closed 합침)·intent 자산 키 통일(FR-7: portfolio_id, 중복=`already_active`/무효=`not_holding` 거부)·`trex-core slots` 단일 뷰(FR-8)가 동작하고, over-cap 시 신규만 거부·기존 보유 유지(OQ-8)된다. ## 측정 방식 및 데이터 출처 각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다. @@ -205,20 +222,21 @@ - S-1 측정 방식: universe fetch 결과 vs 슬롯 집합 대조(통합 테스트) / 데이터 출처: portfolio-hub `get_portfolios`, daily_slot - S-2 측정 방식: 후보>M 시나리오에서 활성 수·선택 결과 단언 / 데이터 출처: 슬롯 활성 상태 + 선택 규칙 - S-3 측정 방식: 신호→활성화/비활성화 경로 단위·통합 + 동시성 경합 테스트 / 데이터 출처: 슬롯 상태 전이, 전이 로그 +- S-4 측정 방식: 상태 전이 단위테스트(허용/거부 집합) + intent 멱등/거부(`already_active`/`not_holding`) 단위 + `slots` 단일 뷰 출력 단언 + over-cap(M 축소) 시나리오 / 데이터 출처: 상태머신 전이표, intent_log, slots RPC 응답 # 8. REQ로 넘기기 전 확인 체크 REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다. -- [ ] 범위(In Scope/Out of Scope) 확정 - - 기준: 포지션 사이징(OQ-4) 포함 여부 + 풀 모델 폐기 범위 합의 -- [ ] 성공 지표의 측정 방식/데이터 출처 확정 - - 기준: S-1~3 측정 시나리오가 테스트 가능한 형태로 합의 -- [ ] High 영향 리스크 우선순위 및 해소 책임자 지정 - - 기준: R-1(풀 모델 폐기·이행)·R-2(universe 변동 충돌) 해소 방향·책임자 지정 -- [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인 - - 기준: OQ-1~7 가 Answered 또는 Deferred 처리됨 (현재 OQ-1~7 전부 Answered, A-1 Confirmed) -- [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정 - - 기준: 승인자 confirm-discovery 수행 +- [x] 범위(In Scope/Out of Scope) 확정 + - 기준: 포지션 사이징(OQ-4 분리) + 풀 모델 폐기 범위(dcy-001/002/014/019 산출물 명시) 합의 완료(§5 범위 경계). +- [x] 성공 지표의 측정 방식/데이터 출처 확정 + - 기준: S-1~4 측정 시나리오·데이터 출처가 테스트 가능한 형태로 합의(§7). +- [x] High 영향 리스크 우선순위 및 해소 책임자 지정 (비차단으로 이관) + - 기준: R-1(풀 모델 폐기·cutover)·R-2(완화됨) 해소 방향은 확정. **해소 책임자(owner) 지정은 사람 책임 배정이라 REQ/batch planning 단계 담당자 할당으로 이관** — Discovery confirm 비차단(confirm-discovery rule 3-4). +- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인 + - 기준: OQ-1~8 전부 Answered, A-1 Confirmed. +- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정 + - 기준: 승인자 Justin Ko, confirm-discovery 수행(2026-06-10). # 9. 파일 처리 결과 이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다. @@ -244,6 +262,7 @@ REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 - [Decided 2026-06-10] holding↔hub/broker 불일치 해소 (OQ-5): **ground truth=브로커, 코어 holding=즉시 안전 구동, hub 차이=warning.** A(코어 holding/브로커 0)=error+운영자, B(코어 flat/브로커 보유, 최위험)=alert+격리+운영자(자동청산·자동편입 금지), C(수량만)=미러 갱신+warning, D(hub 지연)=기존 write-back. 감지=REQ-031 reconcile 확장, 자동은 비파괴만·실주문은 운영자 게이트. universe-detached holding(정상)=tombstone(OQ-2)과 구분. - [Decided 2026-06-10] 동시 활성 최대치 M 설정 출처 (OQ-6): **per-instance TOML(`config/*.toml`, 예: `max_active_slots`, `DaemonInstanceConfig`/REQ-048 계열).** 인스턴스별 독립 운영, env·하드코딩 기본값 비채택. 기존 `TREX_TOTAL_SLOTS`(풀 크기)는 폐기. - [Decided 2026-06-10] 슬롯 영속 인스턴스 스코프 (OQ-7): **공유 DB + `instance_key` 스코프.** `UNIQUE(instance_key, portfolio_id)` + slot-scoped 테이블 전반 instance 스코프화, advisory lock 인스턴스 스코프화(NFR-1 재정의), `daemon_id` 는 universe 파티션·감사용(스코프 키 아님)·instance_key↔daemon_id 1:1 가드. 인스턴스별 별도 DB 비채택. 핵심: 스코프 키를 instance_key 로 두어 영속·락이 portfolio-hub 가용성 비의존(offline 안전). +- [Decided 2026-06-10] over-cap(M 축소) 처리 (OQ-8): **초과분은 신규 활성화만 차단, 기존 보유는 자연 종료까지 유지** — 강제 청산 없음(issue #15), holding ≤ M 될 때까지 새 매수 `capacity_full` 거부. ## CONFIRM - [Confirmed 2026-06-10] 제품 방향이 "universe 자산 중심 포지션 관리 + 동시 보유 수 제어"(익명 capacity 풀 아님)임을 확정 (A-1) — 모델 전환의 대전제. 사용자 확정. @@ -262,8 +281,8 @@ REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록 - 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약) ## Freeze 플래그 -- Handoff Decision: 보류 -- Handoff Rationale: OQ-1~6 전부 Answered + 대전제 A-1 Confirmed(2026-06-10 사용자 결정) + holding/수량 정본 분리(FR-3/FR-4) 확정. 잔여(confirm-discovery 소관): R-1(풀 모델 폐기·일일행→영구행 cutover)·R-2 해소 책임자 지정, CONFIRMED_BY 지정, §8 체크 마감. 내용상 REQ 초안 인계 준비 완료이며 confirm-discovery 게이트 대기 상태. -- Ready for REQ Drafting: false -- Confirmed By: TBD -- Confirmed At (KST): TBD +- Handoff Decision: 승인 +- Handoff Rationale: OQ-1~8 전부 Answered + 대전제 A-1 Confirmed + FR-1~8/NFR-1~5 확정 + holding/수량 정본 분리(FR-3/FR-4)·인스턴스 스코프(OQ-7)·over-cap(OQ-8) 결정. C-type placeholder 0개, Open OQ 0개, §10 DECIDE/CONFIRM 전부 해소. R-1(풀 모델 폐기·일일행→영구행 cutover)·R-2 해소 **책임자 지정은 REQ/batch planning 단계 담당자 할당으로 이관(비차단, rule 3-4)**. 승인자 Justin Ko 확정 → REQ Drafting 인계 가능. +- Ready for REQ Drafting: true +- Confirmed By: Justin Ko +- Confirmed At (KST): 2026-06-10 10:00 KST diff --git a/docs/discovery/index.md b/docs/discovery/index.md index 7190293..5def414 100644 --- a/docs/discovery/index.md +++ b/docs/discovery/index.md @@ -25,4 +25,4 @@ | dcy-017_20260608_watchdog-ownership-visibility | 20260608 | 전략 이탈로 슬롯이 내부 청산 루틴(워치독) 소유로 넘어간 상태를 가시화 — CLI/RPC vs Redis projection (issue #12) | [Discovery](./dcy-017_20260608_watchdog-ownership-visibility.md) | confirmed | | dcy-018_20260608_ledger-response-cash-position-writeback | 20260608 | ledger write 응답의 cash/quantity 를 portfolio 스냅샷에 write-back — option D 활성화 + 수량 source 통일 (운영 환류) | [Discovery](./dcy-018_20260608_ledger-response-cash-position-writeback.md) | confirmed | | dcy-019_20260609_slot-entry-count-and-closed-reallocation | 20260609 | slot entry_count 추가 및 closed 슬롯 재할당 정책 (issue #18) | [Discovery](./dcy-019_20260609_slot-entry-count-and-closed-reallocation.md) | confirmed | -| dcy-020_20260610_asset-centric-slot-model | 20260610 | 자산 중심 슬롯 모델 재설계 — portfolio-hub universe 와 1:1 슬롯 + 활성 슬롯 개수 제어 (일반 슬롯 풀 모델 대체) | [Discovery](./dcy-020_20260610_asset-centric-slot-model.md) | draft | \ No newline at end of file +| dcy-020_20260610_asset-centric-slot-model | 20260610 | 자산 중심 슬롯 모델 재설계 — portfolio-hub universe 와 1:1 슬롯 + 활성 슬롯 개수 제어 (일반 슬롯 풀 모델 대체) | [Discovery](./dcy-020_20260610_asset-centric-slot-model.md) | confirmed | \ No newline at end of file diff --git a/docs/srs/Configuration/req-060_max-active-slots-config.md b/docs/srs/Configuration/req-060_max-active-slots-config.md new file mode 100644 index 0000000..80bcbd2 --- /dev/null +++ b/docs/srs/Configuration/req-060_max-active-slots-config.md @@ -0,0 +1,61 @@ +# req-060: 동시 활성 최대치(max_active_slots) per-instance TOML 설정 + +- Status: Proposed +- Type: Configuration +- Priority: High +- Owner: project maintainers + +## Intent +- 동시 활성(holding) 슬롯 최대치 M 을 인스턴스별로 독립 제어하기 위해 per-instance TOML 설정 키로 노출한다. 영구 슬롯 전환으로 폐기되는 `TREX_TOTAL_SLOTS`(풀 크기)를 대체한다. (dcy-020 FR-2/OQ-6) + +## Requirement +- `DaemonInstanceConfig`(REQ-048 per-instance TOML)에 `max_active_slots`(정수, M) 키를 추가하고 `config/*.toml` 에서 읽는다. admission(REQ-059)이 이 값을 capacity 게이트로 사용한다. env(`TREX_*`)·하드코딩 기본값은 채택하지 않으며, 기존 `TREX_TOTAL_SLOTS` 는 폐기한다. 미설정 시 부팅 실패 또는 명시적 검증 오류로 처리한다(암묵 기본값 금지). + +## Acceptance Criteria +1. Given `config/inst.toml` 에 `max_active_slots = N`, When 데몬 기동, Then `DaemonInstanceConfig.max_active_slots == N` 이고 admission 이 N 을 M 으로 사용한다. +2. Given `max_active_slots` 미설정, When 기동, Then 명시적 오류로 거부된다(암묵 기본값 사용 안 함). +3. Given 두 인스턴스가 서로 다른 `max_active_slots`, When 각자 기동, Then 인스턴스별 독립 M 으로 동작한다. + +## Impacted Area +- Docs: docs/data-model.md/docs/runtime-flows.md(설정), config/instance.example.toml +- Modules: trex_core/runtime/config.py(DaemonInstanceConfig) +- Tests: tests/runtime/test_config*.py + +## Notes +- REQ-048(per-instance TOML 로더) 계열. `TREX_TOTAL_SLOTS` 폐기는 REQ-005/REQ-010 영향(change-req 검토). +- 출처: dcy-020 FR-2, OQ-6. + +## Change Log + +### CHG-20260610-01 +- Date: 2026-06-10 +- Author: Justin Ko + +#### Change Summary +- dcy-020 FR-2/OQ-6 로부터 최초 생성(Proposed). `max_active_slots` per-instance TOML + `TREX_TOTAL_SLOTS` 폐기. + +#### Intent +- 동시 활성 최대치 M 을 인스턴스별 TOML 로 제어하도록 backlog 정규화. + +#### Acceptance Criteria Delta +- Added: TOML 로딩/미설정 거부/인스턴스별 독립 3개 기준 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: config/instance.example.toml, baseline 문서 +- Modules: runtime/config.py(DaemonInstanceConfig) +- Tests: test_config*.py + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: confirm-req. REQ-048 계열, REQ-005/010(TREX_TOTAL_SLOTS) change-req 검토. + +#### Validation Plan +- TOML 키 로딩 + 미설정 거부 단위테스트 green. diff --git a/docs/srs/Data/req-056_asset-permanent-slot-schema.md b/docs/srs/Data/req-056_asset-permanent-slot-schema.md new file mode 100644 index 0000000..ca402bc --- /dev/null +++ b/docs/srs/Data/req-056_asset-permanent-slot-schema.md @@ -0,0 +1,64 @@ +# req-056: 자산 영구 슬롯 스키마 재설계 + 마이그레이션 + +- Status: Proposed +- Type: Data +- Priority: High +- Owner: project maintainers + +## Intent +- 익명 슬롯 풀(`daily_slot` 일일 익명 행)을 portfolio-hub universe 자산과 1:1 대응하는 **영구 슬롯**으로 전환하기 위해, `daily_slot` 스키마를 자산 키·인스턴스 스코프·영속 모델로 재설계한다. (dcy-020 FR-1/OQ-3/OQ-7) + +## Requirement +- `daily_slot` 을 영구 슬롯으로 재설계한다: `portfolio_id` 를 **NOT NULL 영구 키**로, `instance_key` 컬럼 추가, **`UNIQUE(instance_key, portfolio_id)`**, 은퇴 마커 `retired_at`(또는 `universe_detached`) 추가. `trading_date`(슬롯 정체성)·`entry_count`(dcy-019) 컬럼은 제거하고, trading_date 는 이벤트/원장/정책의 시간 속성으로만 둔다. 멱등 마이그레이션(009) + `EXPECTED_SLOT_FIELDS` 계약 + persistence flat 매핑 + `data-model.md` 를 같은 변경 묶음으로 정합한다. slot-scoped 테이블(slot_transition_log/intent_log/ledger_outbox/snapshot_pause/killswitch_exclusion)도 `instance_key` 스코프화한다. + +## Acceptance Criteria +1. Given 마이그레이션 적용, When `daily_slot` 스키마를 확인, Then `portfolio_id NOT NULL`, `instance_key` 존재, `UNIQUE(instance_key, portfolio_id)`, `retired_at` 존재, 슬롯 정체성 `trading_date`·`entry_count` 부재. +2. Given `EXPECTED_SLOT_FIELDS` 계약 가드, When 도메인 필드셋과 information_schema 를 비교, Then 영구 슬롯 필드셋과 일치한다. +3. Given 같은 `(instance_key, portfolio_id)` 행 중복 삽입, When insert, Then UNIQUE 제약으로 거부된다. +4. Given slot-scoped 테이블, When 스키마 확인, Then 각 테이블이 `instance_key` 로 스코프되어 인스턴스 간 행이 섞이지 않는다. + +## Impacted Area +- Docs: docs/data-model.md (daily_slot 재설계), dcy-020 +- Modules: trex_core/adapters/persistence/sql/009_*.sql(신규), trex_core/adapters/persistence/postgresql.py, trex_core/domain/slot/models.py +- Tests: tests/domain/slot/test_contract.py, tests/adapters/persistence/test_migration_and_drift.py + +## Notes +- 기존 REQ 영향(change-req 동반): REQ-002(Slot 필드/EXPECTED_SLOT_FIELDS 재정의), REQ-006(persistence 스키마), REQ-003(계약 가드). dcy-019 `entry_count`·dcy-014 `trading_date` 슬롯키 제거는 그 REQ들의 change-req 로 함께 처리. +- 라이브 데이터 일일행→영구행 전환은 REQ-064(cutover)에서 다룬다(본 REQ 는 목표 스키마 정의). +- 출처: dcy-020 FR-1, OQ-3, OQ-7. + +## Change Log + +### CHG-20260610-01 +- Date: 2026-06-10 +- Author: Justin Ko + +#### Change Summary +- dcy-020 FR-1/OQ-3/OQ-7 로부터 최초 생성(Proposed). 영구 슬롯 스키마(자산 키·instance_key·UNIQUE 복합·retired_at, trading_date/entry_count 제거). + +#### Intent +- 익명 풀 daily_slot → 자산 1:1 영구 슬롯 스키마 전환을 backlog 로 정규화. + +#### Acceptance Criteria Delta +- Added: + - 영구 슬롯 스키마/UNIQUE(instance_key, portfolio_id)/계약 가드/slot-scoped 테이블 instance 스코프 4개 기준 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: docs/data-model.md +- Modules: sql/009_*, postgresql.py, domain/slot/models.py +- Tests: test_contract.py, test_migration_and_drift.py + +#### Delivery Trace Delta +- Batches: none (batch 편성 전) +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no (목표 스키마 정의; 실데이터 전환은 REQ-064) +- Status Recommendation: keep-current +- Follow-up Action: confirm-req → suggest-batch-reqs/draft-batch. REQ-002/003/006 change-req 동반. + +#### Validation Plan +- 마이그레이션 적용 후 스키마/UNIQUE/계약 가드 + drift 테스트 green. diff --git a/docs/srs/Documentation/req-065_asset-centric-baseline-alignment.md b/docs/srs/Documentation/req-065_asset-centric-baseline-alignment.md new file mode 100644 index 0000000..edbab8c --- /dev/null +++ b/docs/srs/Documentation/req-065_asset-centric-baseline-alignment.md @@ -0,0 +1,61 @@ +# req-065: 자산 중심 모델 baseline 문서 정합 + 풀 모델 폐기 명시 + +- Status: Proposed +- Type: Documentation +- Priority: Medium +- Owner: project maintainers + +## Intent +- 자산 중심 영구 슬롯 모델 전환 후 baseline 문서를 실제 구조/흐름/데이터 모델과 일치시키고, 폐기되는 풀 모델(dcy-001/002/014/019) 산출물의 처분을 명시한다. (dcy-020 FR-5) + +## Requirement +- `docs/data-model.md`(영구 slot 스키마·instance_key·retired_at·trading_date 강등·entry_count 제거), `docs/runtime-flows.md`(universe reconcile lifecycle·신호 기반 admission·불일치 해소·일일 preallocation 폐기), `docs/project-structure.md`(자산 중심 슬롯 모델)를 새 모델로 갱신한다. dcy-001(6상태/closed→idle)·dcy-014(일일 preallocation)·dcy-019(entry_count/closed 재할당/find_allocatable_slot) 의 폐기/재해석 범위와 `daily_symbol_entries` 폐기를 명시한다. + +## Acceptance Criteria +1. Given 전환 구현 완료, When `docs/data-model.md` 확인, Then 영구 슬롯 스키마·상태·트레이딩데이트 강등이 코드와 일치한다. +2. Given 전환 구현 완료, When `docs/runtime-flows.md` 확인, Then reconcile lifecycle·admission·불일치 해소 흐름이 실제와 일치하고 일일 preallocation 서술이 제거된다. +3. Given 폐기 대상, When 문서 확인, Then dcy-001/014/019·daily_symbol_entries 폐기/재해석 범위가 명시된다. + +## Impacted Area +- Docs: docs/data-model.md, docs/runtime-flows.md, docs/project-structure.md +- Modules: 없음(문서 전용) +- Tests: 없음(코드-문서 diff 대조) + +## Notes +- 다른 dcy-020 REQ 구현 완료 후 수행해야 의미가 있다(순서 의존). baseline 갱신은 각 배치의 일부로 분산 가능. +- 출처: dcy-020 FR-5. + +## Change Log + +### CHG-20260610-01 +- Date: 2026-06-10 +- Author: Justin Ko + +#### Change Summary +- dcy-020 FR-5 로부터 최초 생성(Proposed). baseline 3문서 자산 중심 재설계 + dcy-001/014/019·daily_symbol_entries 폐기 명시. + +#### Intent +- 모델 전환 후 baseline 문서 정합과 폐기 범위 명시를 backlog 정규화. + +#### Acceptance Criteria Delta +- Added: data-model/runtime-flows/폐기 명시 3개 기준 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: docs/data-model.md, docs/runtime-flows.md, docs/project-structure.md +- Modules: 없음(문서 전용) +- Tests: 없음(코드-문서 diff) + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: confirm-req. 다른 dcy-020 REQ 구현 후 수행(순서 의존). + +#### Validation Plan +- 코드-문서 diff 대조로 정합 확인. diff --git a/docs/srs/Integration/req-058_universe-slot-reconcile-lifecycle.md b/docs/srs/Integration/req-058_universe-slot-reconcile-lifecycle.md new file mode 100644 index 0000000..221f6c9 --- /dev/null +++ b/docs/srs/Integration/req-058_universe-slot-reconcile-lifecycle.md @@ -0,0 +1,63 @@ +# req-058: universe 기반 슬롯 lifecycle reconcile (생성/soft-retire/tombstone) + +- Status: Proposed +- Type: Integration +- Priority: High +- Owner: project maintainers + +## Intent +- 슬롯 집합을 portfolio-hub universe 의 projection 으로 유지하기 위해, universe fetch 결과로 슬롯을 생성/은퇴하는 reconcile 경로를 정의한다. 일일 preallocation(dcy-014)을 폐기하고 universe 동기화로 대체한다. (dcy-020 FR-1/OQ-2/NFR-3) + +## Requirement +- 스냅샷 refresh(`get_portfolios` 성공 직후)에 슬롯 reconcile 을 수행한다: (1) universe 신규 자산 → 영구 슬롯 즉시 생성(비활성·멱등, `UNIQUE(instance_key, portfolio_id)`); (2) universe 제거 + 슬롯 비활성(holding/pending 아님) → **soft-retire**(`retired_at` 마킹, 행 보존, hard delete 금지); (3) universe 제거 + 보유 중/pending → **tombstone**(은퇴 보류, safety-sell/watchdog 유지, 신규 매수 후보 제외, 닫힌 뒤 은퇴). reconcile 쓰기는 인스턴스 스코프 advisory lock 안에서 단일 writer 로 수행한다. cadence 는 기존 부팅 + 일 1회 + 운영자 on-demand. portfolio-hub 불가/degraded 또는 universe N→0/급격 대량 감소면 생성·은퇴를 보류하고 경고한다. + +## Acceptance Criteria +1. Given universe 에 신규 자산, When reconcile, Then 그 자산의 영구 슬롯이 비활성으로 생성되고 재실행해도 중복 생성되지 않는다(멱등). +2. Given universe 에서 빠진 비활성 슬롯, When reconcile, Then soft-retire(`retired_at` set, 행 보존)되고, 같은 자산이 재등장하면 동일 슬롯이 재활성화된다. +3. Given universe 에서 빠졌으나 holding/pending 인 슬롯, When reconcile, Then 은퇴되지 않고 tombstone(신규 매수 제외·safety-sell 유지)되며, 매도 체결 후에야 은퇴된다. +4. Given portfolio-hub degraded 또는 universe 가 N→0, When reconcile, Then 슬롯 생성·은퇴가 보류되고 마지막 집합이 유지되며 경고가 남는다. + +## Impacted Area +- Docs: docs/runtime-flows.md(reconcile lifecycle) +- Modules: trex_core/application/snapshot_scheduler.py, trex_core/domain/slot/repository.py, trex_core/adapters/persistence/postgresql.py, trex_core/application/preallocation_scheduler.py(폐기) +- Tests: tests/application/test_snapshot_scheduler.py(reconcile), tests/application/test_preallocation_scheduler.py(폐기/대체) + +## Notes +- 기존 REQ-005/REQ-048(일일 preallocation·instance config) change-req 동반(preallocation 폐기·재해석). trading_date KST 계산(REQ-005)은 일일 회계 경계용으로 부분 존속. +- 출처: dcy-020 FR-1, OQ-2, NFR-3. + +## Change Log + +### CHG-20260610-01 +- Date: 2026-06-10 +- Author: Justin Ko + +#### Change Summary +- dcy-020 FR-1/OQ-2/NFR-3 로부터 최초 생성(Proposed). universe reconcile(생성/soft-retire/tombstone) + degrade·대량소멸 보류 + 일일 preallocation 폐기. + +#### Intent +- 슬롯 집합을 universe projection 으로 유지하는 lifecycle 을 backlog 로 정규화. + +#### Acceptance Criteria Delta +- Added: + - 생성 멱등/비활성 soft-retire 재활성/tombstone/degrade·대량소멸 보류 4개 기준 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: docs/runtime-flows.md +- Modules: snapshot_scheduler.py, repository.py, postgresql.py, preallocation_scheduler.py(폐기) +- Tests: test_snapshot_scheduler.py, test_preallocation_scheduler.py + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: confirm-req. REQ-005/048 change-req(preallocation 폐기) 동반. + +#### Validation Plan +- reconcile 생성/은퇴/tombstone/degrade 단위·통합 테스트 green. diff --git a/docs/srs/Integration/req-062_quantity-hub-authority-and-divergence.md b/docs/srs/Integration/req-062_quantity-hub-authority-and-divergence.md new file mode 100644 index 0000000..a3e8a9c --- /dev/null +++ b/docs/srs/Integration/req-062_quantity-hub-authority-and-divergence.md @@ -0,0 +1,62 @@ +# req-062: 수량 hub 정본+로컬 미러 + holding↔broker/hub 불일치 해소 + +- Status: Proposed +- Type: Integration +- Priority: High +- Owner: project maintainers + +## Intent +- 보유 수량의 정본을 portfolio-hub 로 두고 슬롯은 미러만 유지하며, 코어 로컬 holding 과 브로커/hub 가 비정상적으로 어긋날 때의 해소 정책을 정의한다. (dcy-020 FR-4/OQ-5/NFR-5) + +## Requirement +- 보유 **수량(asset_quantity)의 정본은 portfolio-hub** 이며, 슬롯은 수량을 정본 소유하지 않고 로컬 best-effort 미러(option D write-back/추정)로 둔다. 실제 청산 시 매도 수량은 **브로커 잔고(`get_balance().hldg_qty`)를 정본**으로 조회한다. 불일치 감지는 `reconcile_positions`(REQ-031)를 확장해 **브로커 잔고 + hub 수량 + 코어 슬롯 holding** 3자를 비교하며, 브로커를 ground truth 로 한다. 해소: A(코어 holding/브로커 0) → 슬롯 `error` + 운영자 resolve; B(코어 flat/브로커 보유, 최위험) → WARN/alert + 격리 + 운영자 결정(자동 청산·자동 편입 금지); C(수량만 불일치) → 미러 갱신 + warning, 상태 변경 없음; D(hub 만 지연) → 기존 write-back. **조용한 자동 교정 금지·표면화**, 실주문은 운영자 게이트(issue #15). 감지 reconcile 은 일일 refresh 와 분리해 더 자주 수행 가능(브로커 잔고+슬롯 상태만 필요). + +## Acceptance Criteria +1. Given 매도 청산, When 매도 수량 결정, Then 로컬 미러가 stale 해도 브로커 잔고 기준으로 정확히 청산된다. +2. Given 코어 holding 인데 브로커 0, When reconcile, Then 슬롯이 `error` 로 격리되고 safety-sell 대상에서 빠지며 운영자 resolve 가 요구된다. +3. Given 코어 flat 인데 브로커 보유, When reconcile, Then WARN/alert + 격리되고 자동 청산·자동 편입 없이 운영자 결정으로 넘어간다. +4. Given 둘 다 보유, 수량만 불일치, When reconcile, Then 미러만 갱신되고 상태 전이는 없으며 hub 지속 불일치는 warning 으로 노출된다. + +## Impacted Area +- Docs: docs/runtime-flows.md(불일치 해소·write-back) +- Modules: trex_core/application/reconciliation.py, trex_core/application/ledger_outbox.py, trex_core/domain/slot/models.py(qty 미러 의미) +- Tests: tests/application/test_reconciliation.py, tests/application/test_ledger_option_d.py + +## Notes +- 기존 REQ-017/019/029/030/031 와 연계(option D write-back 재사용). 슬롯 qty 정본 소유 제거는 REQ-002/006 change-req 와 정합. +- 출처: dcy-020 FR-4, OQ-5, NFR-5. + +## Change Log + +### CHG-20260610-01 +- Date: 2026-06-10 +- Author: Justin Ko + +#### Change Summary +- dcy-020 FR-4/OQ-5/NFR-5 로부터 최초 생성(Proposed). 수량 hub 정본+미러·청산=브로커·불일치 해소(A/B/C/D, 표면화·운영자 게이트). + +#### Intent +- 수량 정본 분리와 holding↔broker/hub 불일치 해소 정책을 backlog 정규화. + +#### Acceptance Criteria Delta +- Added: 청산=브로커/A error/B 격리·운영자/C 미러갱신 4개 기준 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: docs/runtime-flows.md +- Modules: application/reconciliation.py, ledger_outbox.py, domain/slot/models.py +- Tests: test_reconciliation.py, test_ledger_option_d.py + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no (REQ-031/017/019 연계·확장) +- Status Recommendation: keep-current +- Follow-up Action: confirm-req. REQ-002/006(qty 미러) change-req 정합. + +#### Validation Plan +- 청산 브로커 기준 + A/B/C 불일치 해소 단위·통합 테스트 green. diff --git a/docs/srs/Interface/req-057_asset-slot-state-machine.md b/docs/srs/Interface/req-057_asset-slot-state-machine.md new file mode 100644 index 0000000..66852ca --- /dev/null +++ b/docs/srs/Interface/req-057_asset-slot-state-machine.md @@ -0,0 +1,64 @@ +# req-057: 자산 영구 슬롯 상태머신 재정의 + 인스턴스 스코프 advisory lock + +- Status: Proposed +- Type: Interface +- Priority: High +- Owner: project maintainers + +## Intent +- 영구 자산 슬롯에 맞춰 슬롯 상태머신을 재정의하고, holding 을 코어 로컬 정본으로 두며, 공유 DB 다중 인스턴스에서 단일 writer 불변식을 인스턴스 단위로 보존한다. (dcy-020 FR-6/FR-3/NFR-1) + +## Requirement +- 상태 집합을 `inactive → pending_buy → holding → pending_sell → inactive` (+ 모든 비-error → `error`, `error → {inactive, holding}` 운영자 resolve)로 재정의한다. 기존 `idle`·`closed` 는 영구 슬롯에서 단일 `inactive` 로 합치고 `closed→idle` 복구 전이를 제거한다. `holding`(포지션 열림)은 코어 슬롯의 로컬 정본으로 체결 시 동기 set, 매도 체결 시 동기 clear 한다(portfolio-hub `asset_quantity` 로 판단하지 않음). advisory lock 을 **인스턴스 스코프**(`pg_advisory_xact_lock(SLOT_LOCK_ID, hash(instance_key))`)로 두어 인스턴스 간 교차 직렬화를 제거한다. + +## Acceptance Criteria +1. Given 상태머신, When 허용 전이 집합을 확인, Then `{inactive→pending_buy, pending_buy→holding, pending_buy→inactive, holding→pending_sell, pending_sell→inactive, pending_sell→holding, *→error, error→inactive, error→holding}` 와 정확히 일치한다. +2. Given 영구 슬롯, When 매도 체결로 종료, Then 슬롯은 `inactive` 로 돌아가고(별도 `closed`·`closed→idle` 없음) holding 로컬 플래그가 clear 된다. +3. Given 서로 다른 instance_key 의 두 슬롯, When 동시에 전이, Then 인스턴스별 락으로 서로를 차단하지 않는다(교차 경합 없음). +4. Given 같은 슬롯에 대한 동시 전이, When 병렬 처리, Then 인스턴스 스코프 락으로 직렬화되어 단일 writer 불변식이 유지된다. + +## Impacted Area +- Docs: docs/runtime-flows.md(전이 흐름), docs/data-model.md(상태) +- Modules: trex_core/domain/slot/state.py, trex_core/domain/slot/service.py, trex_core/domain/slot/models.py +- Tests: tests/domain/slot/test_state.py, tests/domain/slot/test_service.py + +## Notes +- 기존 REQ-002(상태머신·서비스) 재정의 change-req 동반. dcy-001 의 6상태/closed→idle 모델 폐기. +- holding 로컬 정본 근거(offline 안전·universe 누락 생존·hub 지연·capacity 원자성)는 dcy-020 FR-3. +- 출처: dcy-020 FR-6, FR-3, NFR-1, OQ-7. + +## Change Log + +### CHG-20260610-01 +- Date: 2026-06-10 +- Author: Justin Ko + +#### Change Summary +- dcy-020 FR-6/FR-3/NFR-1 로부터 최초 생성(Proposed). 상태머신 재정의(idle/closed→inactive, closed→idle 제거) + holding 로컬 정본 + 인스턴스 스코프 advisory lock. + +#### Intent +- 영구 자산 슬롯 상태머신·holding 로컬·인스턴스 단위 단일 writer 를 backlog 로 정규화. + +#### Acceptance Criteria Delta +- Added: + - 전이 집합/inactive 종료/인스턴스별 락 비교차/동일 슬롯 직렬화 4개 기준 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: docs/runtime-flows.md, docs/data-model.md +- Modules: domain/slot/state.py, service.py, models.py +- Tests: test_state.py, test_service.py + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no (신규 정의; REQ-002 재정의 change-req 로 기존 폐기) +- Status Recommendation: keep-current +- Follow-up Action: confirm-req. REQ-002 change-req 동반. + +#### Validation Plan +- 전이 허용/거부 집합 + 인스턴스 락 단위테스트 green. diff --git a/docs/srs/Interface/req-059_active-capacity-admission.md b/docs/srs/Interface/req-059_active-capacity-admission.md new file mode 100644 index 0000000..0cce3ff --- /dev/null +++ b/docs/srs/Interface/req-059_active-capacity-admission.md @@ -0,0 +1,63 @@ +# req-059: 활성 capacity 제어 + 신호 기반 admission (FCFS·M캡·무치환·over-cap) + +- Status: Proposed +- Type: Interface +- Priority: High +- Owner: project maintainers + +## Intent +- 익명 슬롯 배정 대신 "동시 활성(holding) 최대 M개" 정책으로 capacity 를 제어하고, 매수/매도 신호를 해당 자산 슬롯의 활성화/비활성화로 처리한다. (dcy-020 FR-2/FR-3/OQ-1/OQ-8) + +## Requirement +- 매수 신호는 해당 자산 슬롯을 활성화(`inactive→pending_buy`)한다. 활성(holding+pending_buy) 수 < M 이면 즉시 활성화, == M 이면 **거부(`capacity_full`)** 한다. 코어는 자리를 만들려고 기존 holding 을 **자율 청산(치환)하지 않는다**(FCFS·무치환). 동시 후보 tie-break 은 `updated_at` asc → `portfolio_id`(alpha 비사용). M 을 현재 보유 수보다 작게 바꿔 over-cap 이 되면 **신규 활성화만 차단**하고 기존 보유는 자연 종료까지 유지한다(강제 청산 없음). 마지막 슬롯 경합은 인스턴스 스코프 advisory lock + 재시도로 직렬화한다. + +## Acceptance Criteria +1. Given 활성 수 < M, When 자산 매수 신호, Then 그 자산 슬롯이 `pending_buy` 로 활성화된다. +2. Given 활성 수 == M, When 새 자산 매수 신호, Then `capacity_full` 로 거부되고 기존 holding 은 청산되지 않는다. +3. Given 이미 활성(pending_buy/holding/pending_sell)인 자산, When 또 매수 신호, Then 멱등 거부(`already_active`)된다. +4. Given M 을 현재 보유 수보다 작게 변경(over-cap), When 신규 매수 신호, Then 활성 수가 M 이하로 내려갈 때까지 신규만 거부되고 기존 보유는 유지된다. +5. Given 동시 매수 신호 경합, When 병렬 처리, Then 인스턴스 스코프 락으로 직렬화되어 M 초과 활성화가 발생하지 않는다. + +## Impacted Area +- Docs: docs/runtime-flows.md(admission→활성화 흐름) +- Modules: trex_core/application/intents.py, trex_core/application/portfolio.py(capacity 개념) +- Tests: tests/application/test_intent_concurrency.py, tests/application/test_*admission*.py + +## Notes +- M 값 출처는 REQ-060(per-instance TOML). 기존 REQ-007(admission) 재정의 change-req 동반(find_allocatable_slot/idle 배정 폐기). +- 출처: dcy-020 FR-2, FR-3, OQ-1, OQ-8. + +## Change Log + +### CHG-20260610-01 +- Date: 2026-06-10 +- Author: Justin Ko + +#### Change Summary +- dcy-020 FR-2/FR-3/OQ-1/OQ-8 로부터 최초 생성(Proposed). FCFS + M 캡 + capacity_full 거부 + 무치환 + over-cap(신규만 차단) + already_active. + +#### Intent +- 신호 기반 활성화/비활성화와 동시 활성 M 제어를 backlog 로 정규화. + +#### Acceptance Criteria Delta +- Added: 활성화/거부/already_active/over-cap/경합 직렬화 5개 기준 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: docs/runtime-flows.md +- Modules: application/intents.py, portfolio.py +- Tests: test_intent_concurrency.py, admission 테스트 + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: confirm-req. REQ-007 재정의 change-req·REQ-060(M) 의존. + +#### Validation Plan +- FCFS/거부/over-cap/경합 단위·통합 테스트 green. diff --git a/docs/srs/Interface/req-061_asset-keyed-intent-contract.md b/docs/srs/Interface/req-061_asset-keyed-intent-contract.md new file mode 100644 index 0000000..0dc574a --- /dev/null +++ b/docs/srs/Interface/req-061_asset-keyed-intent-contract.md @@ -0,0 +1,62 @@ +# req-061: intent 계약 자산(portfolio_id) 키 통일 + 중복/무효 규칙 + +- Status: Proposed +- Type: Interface +- Priority: High +- Owner: project maintainers + +## Intent +- 자산 중심 모델에서 매수·매도 신호를 모두 자산(portfolio_id)으로 식별하도록 intent 계약을 통일하고, 슬롯이 자산 1:1 영구이므로 `sell-by-slot_id` 노출을 폐기한다. (dcy-020 FR-7/NFR-4) + +## 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). + +## Acceptance Criteria +1. Given 매도 신호, When `portfolio_id` 로 제출, Then 해당 자산 슬롯이 `pending_sell` 로 전이하고 `slot_id` 없이 처리된다. +2. Given 이미 holding 인 자산, When 매수 신호, Then `already_active` 로 멱등 거부된다. +3. Given 보유하지 않은 자산, When 매도 신호, Then `not_holding` 으로 거부된다. +4. Given 같은 `intent_id` 재전송, When 처리, Then 슬롯 중복 전이 없이 최초 결정과 동일 응답(멱등). + +## 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. diff --git a/docs/srs/Interface/req-063_unified-operator-slot-view.md b/docs/srs/Interface/req-063_unified-operator-slot-view.md new file mode 100644 index 0000000..2bf2e33 --- /dev/null +++ b/docs/srs/Interface/req-063_unified-operator-slot-view.md @@ -0,0 +1,61 @@ +# req-063: 운영자 표면 단일화 — slots = 자산 포지션 단일 뷰 + +- Status: Proposed +- Type: Interface +- Priority: Medium +- Owner: project maintainers + +## Intent +- universe(자산 리스트)와 slots(슬롯 상태)의 이중 표면(BAT-022 혼동 원인)을 해소하기 위해, 슬롯=자산 1:1 모델에서 `trex-core slots` 가 자산 포지션을 단일 뷰로 제공한다. (dcy-020 FR-8) + +## Requirement +- `trex-core slots` 가 자산 슬롯(슬롯=자산)을 단일 뷰로 표시한다: `portfolio_id`/symbol/status(inactive/pending_buy/holding/pending_sell/error)/holding 수량 미러/`universe_detached`(tombstone)/`retired`. universe(자산 리스트)와 slots(자산 포지션)는 같은 자산 집합 위의 view 가 되며, 데몬 RPC(스냅샷)로 통일한다(직접 DB 쓰기 없음). 운영자는 자산별 포지션·활성 여부·tombstone 여부를 한 표면에서 본다. + +## Acceptance Criteria +1. Given universe N개 자산, When `trex-core slots`, Then N개 자산 슬롯이 status·holding·tombstone 표시와 함께 단일 뷰로 출력된다. +2. Given tombstone(universe-detached holding) 슬롯, When `slots`, Then `universe_detached` 로 구분 표시된다. +3. Given `--status` 필터, When 조회, Then 자산 슬롯 status 로 필터된다. + +## Impacted Area +- Docs: docs/runtime-flows.md, docs/interface-contract.md(snapshot envelope slots[]) +- Modules: trex_core/runtime/cli.py, snapshot envelope 빌더 +- Tests: tests/application/test_messaging_snapshot_consumer.py, CLI 테스트 + +## Notes +- 기존 REQ-032(운영자 CLI)·REQ-028/029(snapshot envelope) 와 연계 change-req. BAT-022 슬롯 가시화의 자산 중심 재정의. +- 출처: dcy-020 FR-8. + +## Change Log + +### CHG-20260610-01 +- Date: 2026-06-10 +- Author: Justin Ko + +#### Change Summary +- dcy-020 FR-8 로부터 최초 생성(Proposed). `trex-core slots` = 자산 포지션 단일 뷰(universe↔slots 이중 표면 해소). + +#### Intent +- 운영 관측 단일화(자산별 포지션·활성·tombstone)를 backlog 정규화. + +#### Acceptance Criteria Delta +- Added: 단일 뷰 출력/tombstone 표시/status 필터 3개 기준 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: docs/runtime-flows.md, docs/interface-contract.md +- Modules: runtime/cli.py, snapshot envelope 빌더 +- Tests: test_messaging_snapshot_consumer.py, CLI 테스트 + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no (REQ-032/028/029 연계 change-req) +- Status Recommendation: keep-current +- Follow-up Action: confirm-req. + +#### Validation Plan +- slots 단일 뷰 출력·tombstone·필터 단위테스트 green. diff --git a/docs/srs/Migration/req-064_pool-to-permanent-slot-cutover.md b/docs/srs/Migration/req-064_pool-to-permanent-slot-cutover.md new file mode 100644 index 0000000..75fa9f7 --- /dev/null +++ b/docs/srs/Migration/req-064_pool-to-permanent-slot-cutover.md @@ -0,0 +1,62 @@ +# req-064: 풀 모델 → 영구 자산 슬롯 cutover 마이그레이션 + +- Status: Proposed +- Type: Migration +- Priority: High +- Owner: project maintainers + +## Intent +- 출시·운영 중인 익명 풀 모델(일일 daily_slot 행) 데이터를 자산 영구 슬롯 모델로 안전하게 이행한다. 라이브 트레이딩 데이터(보유 포지션)를 잃지 않고 cutover 한다. (dcy-020 NFR-2/R-1) + +## Requirement +- 기존 일일 `daily_slot` 행을 **자산당 영구 1행**으로 전환하고, 현재 보유(holding) 포지션을 새 영구 슬롯으로 **carry-over** 한다. 기존 slot-scoped 행(slot_transition_log/intent_log/ledger_outbox 등)에 `instance_key` 를 태깅한다. cutover 는 거래 세션 외 시간에 수행하고, 롤백 계획(역마이그레이션 또는 스냅샷 복원)을 갖춘다. 단계적 전환(읽기 정합 → 모델 전환)을 우선 검토하고, cutover 전후 보유 포지션·safety-sell 보호가 끊기지 않음을 검증한다. + +## Acceptance Criteria +1. Given 기존 풀 모델 데이터(보유 N개 포함), When cutover 적용, Then 각 보유 자산이 영구 슬롯 1행(holding)으로 보존되고 수량/임계값 등 보호 컨텍스트가 유실되지 않는다. +2. Given cutover 후, When 데몬 기동, Then universe reconcile(REQ-058)·admission(REQ-059)·safety-sell 이 회귀 없이 동작한다. +3. Given cutover 실패, When 롤백, Then 직전 released 리비전/스키마로 복원 가능하다(롤백 계획 검증). +4. Given slot-scoped 기존 행, When cutover, Then `instance_key` 태깅이 적용되어 인스턴스 스코프 쿼리가 정확하다. + +## Impacted Area +- Docs: docs/runtime-flows.md(cutover 절차), 릴리즈 Rollback Plan +- Modules: trex_core/adapters/persistence/sql/(cutover 마이그레이션), 이행 스크립트 +- Tests: tests/adapters/persistence/(cutover round-trip), 회귀 스위트 + +## Notes +- 라이브 데이터 영향 — 배포 게이트·운영자 입회 권장. 실행은 별도 배치(BAT-δ)로 분리하고 confirm-release 전 run-the-app/cutover 리허설 evidence 필요. +- 출처: dcy-020 NFR-2, R-1. + +## Change Log + +### CHG-20260610-01 +- Date: 2026-06-10 +- Author: Justin Ko + +#### Change Summary +- dcy-020 NFR-2/R-1 로부터 최초 생성(Proposed). 일일행→영구행 cutover + 보유 carry-over + instance_key 태깅 + 롤백 계획. + +#### Intent +- 라이브 풀 모델 데이터를 무손실로 영구 슬롯 모델로 이행하는 cutover 를 backlog 정규화. + +#### Acceptance Criteria Delta +- Added: 보유 carry-over/기동 회귀/롤백/instance_key 태깅 4개 기준 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: docs/runtime-flows.md, 릴리즈 Rollback Plan +- Modules: persistence cutover 마이그레이션, 이행 스크립트 +- Tests: cutover round-trip, 회귀 스위트 + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: yes (cutover 리허설/회귀 필수) +- Existing Implementation Invalidated: no (이행 절차 신규) +- Status Recommendation: keep-current +- Follow-up Action: confirm-req. 별도 배치(BAT-δ)·운영자 입회·confirm-release 전 리허설 evidence. + +#### Validation Plan +- cutover 리허설(보유 보존·기동 회귀·롤백) + run-the-app evidence. diff --git a/docs/srs/index.md b/docs/srs/index.md index e0ff7c7..8440e71 100644 --- a/docs/srs/index.md +++ b/docs/srs/index.md @@ -74,7 +74,7 @@ ## Next Requirement ID -- Current: `REQ-056` +- Current: `REQ-066` - Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다. ## Requirement Register @@ -136,11 +136,21 @@ | REQ-053 | safety-sell 재시도 쿨다운 env 설정 2종 | Configuration | Implemented | Medium | project maintainers | [req-053](./Configuration/req-053_safety-sell-retry-cooldown-env.md) | | REQ-054 | safety-sell 쿨다운 runtime-flows 정합 | Documentation | Implemented | Low | project maintainers | [req-054](./Documentation/req-054_safety-sell-cooldown-baseline-alignment.md) | | REQ-055 | closed 슬롯 ↔ ledger_outbox 누락 운영 점검 쿼리 | Exception | Implemented | Medium | project maintainers | [req-055](./Exception/req-055_ledger-gap-ops-check.md) | +| REQ-056 | 자산 영구 슬롯 스키마 재설계 + 마이그레이션 | Data | Proposed | High | project maintainers | [req-056](./Data/req-056_asset-permanent-slot-schema.md) | +| REQ-057 | 자산 영구 슬롯 상태머신 재정의 + 인스턴스 스코프 advisory lock | Interface | Proposed | High | project maintainers | [req-057](./Interface/req-057_asset-slot-state-machine.md) | +| REQ-058 | universe 기반 슬롯 lifecycle reconcile (생성/soft-retire/tombstone) | Integration | Proposed | High | project maintainers | [req-058](./Integration/req-058_universe-slot-reconcile-lifecycle.md) | +| REQ-059 | 활성 capacity 제어 + 신호 기반 admission (FCFS·M캡·무치환·over-cap) | Interface | Proposed | High | project maintainers | [req-059](./Interface/req-059_active-capacity-admission.md) | +| REQ-060 | 동시 활성 최대치(max_active_slots) per-instance TOML 설정 | Configuration | Proposed | High | project maintainers | [req-060](./Configuration/req-060_max-active-slots-config.md) | +| REQ-061 | intent 계약 자산(portfolio_id) 키 통일 + 중복/무효 규칙 | Interface | Proposed | High | project maintainers | [req-061](./Interface/req-061_asset-keyed-intent-contract.md) | +| REQ-062 | 수량 hub 정본+로컬 미러 + holding↔broker/hub 불일치 해소 | Integration | Proposed | High | project maintainers | [req-062](./Integration/req-062_quantity-hub-authority-and-divergence.md) | +| REQ-063 | 운영자 표면 단일화 — slots = 자산 포지션 단일 뷰 | Interface | Proposed | Medium | project maintainers | [req-063](./Interface/req-063_unified-operator-slot-view.md) | +| REQ-064 | 풀 모델 → 영구 자산 슬롯 cutover 마이그레이션 | Migration | Proposed | High | project maintainers | [req-064](./Migration/req-064_pool-to-permanent-slot-cutover.md) | +| REQ-065 | 자산 중심 모델 baseline 문서 정합 + 풀 모델 폐기 명시 | Documentation | Proposed | Medium | project maintainers | [req-065](./Documentation/req-065_asset-centric-baseline-alignment.md) | ## Recent Change Log Summary +- 2026-06-10 :: REQ-056~065 신규 생성(Proposed) (draft-req, dcy-020 자산 중심 슬롯 모델 재설계): 익명 슬롯 풀 → portfolio-hub universe 자산과 1:1 **영구 슬롯** + 동시 활성(M) 제어로 substrate 전환. REQ-056(영구 슬롯 스키마·UNIQUE(instance_key,portfolio_id)·trading_date/entry_count 제거), REQ-057(상태머신 inactive↔pending_buy↔holding↔pending_sell·holding 로컬·인스턴스 스코프 lock), REQ-058(universe reconcile 생성/soft-retire/tombstone·degrade 보류·일일 preallocation 폐기), REQ-059(FCFS·M캡·무치환·over-cap), REQ-060(max_active_slots per-instance TOML), REQ-061(intent 자산 키 통일·sell-by-slot_id 폐기), REQ-062(수량 hub 정본+미러·청산 브로커·불일치 해소), REQ-063(slots 단일 뷰), REQ-064(풀→영구 cutover), REQ-065(baseline 정합·폐기 명시). 기존 REQ-002/003/005/006/007/048 등은 폐기/재정의 change-req 동반 예정. 단계적 배치(스키마·상태머신 → reconcile·admission → intent·수량·CLI → cutover) delivery. 전부 **Proposed**(confirm-req 대기). (draft-req) - 2026-06-09 :: REQ-048/010 change-req→Implemented (CHG-20260609-01/02, trex-orb-v1 설정 관리 정합, BAT-026): config 디렉터리 기반 설정 관리 도입 — `--config` 를 이름으로 해석 + `--config-path `(기본 `./config`) + `resolve_config_toml`(슬래시 포함 값은 하위호환 명시 경로)(REQ-048), `.env` 를 config-dir 결정적 해석(`--env-file` 우선, 없으면 `/.env`, CWD 무관)으로 변경 + `config/.env.example` + `.gitignore` `config/*` 허용목록(REQ-010). 운영자 CLI 도 `--config-path`/`--env-file` 동일 적용. `from_env()` legacy 루트 `.env` 폴백 유지로 완전 하위호환(`.env` 이동은 운영 배포 시 무중단). batch-lite(BAT-026) 단위 23 + 전체 375 passed, ruff clean, 라이브 스모크(name·path 동일 결과) → 둘 다 **Implemented**. (change-req/confirm-req-implemented) - 2026-06-09 :: REQ-002/003/006/007/009 change-req (CHG-20260609-01, dcy-019/issue #18, BAT-025): slot-scoped `entry_count` 추가 + closed 슬롯 직접 재할당. daily_slot/`Slot` 에 `entry_count`(기본 0) 필드 + `EXPECTED_SLOT_FIELDS` + 멱등 마이그레이션 008(REQ-002/006), 상태 머신 `closed→pending_buy` 신규 전이(8→9 base, ALLOWED_TRANSITIONS 14→15)(REQ-002), `_apply_transition_fields` closed→pending_buy clean-slate + 모든 `→pending_buy` 시 entry_count +1(REQ-002), `find_allocatable_slot`(idle∪closed, `entry_count→updated_at→slot_id`) selector + in-memory 동등(REQ-006), `_admit_buy` find_idle_slot→find_allocatable_slot + from_state=slot.status(REQ-007), 테스트 계약 정합(REQ-003), baseline 2문서(data-model/runtime-flows) 정합(REQ-009). 기존 `daily_symbol_entries`(symbol-scoped) 무관·미변경. 전부 change-req(신규 REQ 없음, OQ-1/2/3). 단위 366 passed(+KIS sandbox 1건은 live .env URL 로 사전 환경 실패·무관), ruff clean. PG 게이트 마이그레이션/드리프트는 라이브 runtime DSN 미사용(파괴적 truncate 회피)·test DSN 자동 검증. 전부 **Implemented** 유지. (change-req) - 2026-06-09 :: REQ-032 change-req→Implemented (CHG-20260609-01/02, issue #17, BAT-024): `trex-core enter` 에 `--forced-close-at ` (절대시간) 옵션 추가 — 운영자가 매수 진입 명령 하나로 시간 기반 자동청산(forced_close) 시각 지정. CLI 가 buy intent payload 의 기존 `forced_close_at` 필드에 매핑(계약 REQ-007/021·도메인 REQ-066·forced-close 루프 REQ-037 이미 구현·불변, CLI 노출 갭만 보완). 잘못된 형식 제출 전 거부, 미지정 시 미포함(하위호환). 상대시간(`--hold-minutes`) 미채택. AC-5/6 신규. batch-lite(BAT-024) 단위 검증 → **Implemented**. 단위 353 passed, ruff clean. (change-req/confirm-req-implemented) - 2026-06-08 :: REQ-017/019/029 change-req + REQ-030 Approved→Implemented (dcy-018, BAT-023): ledger write 응답의 권위 cash/quantity 를 portfolio 스냅샷에 write-back(option D 활성화) — 매수/매도 직후 "수량 live·현금 stale" 비대칭 제거. `record_trade`→`LedgerWriteOutcome{result, *_after}`+어댑터 `cash_balance_after`/`asset_quantity_after` 파싱(new+duplicate)(REQ-017), `LedgerWriter` SnapshotStore write-back best-effort+`pending_cash_delta`(REQ-019), 추정 현금 오버레이(`cash_estimated` 라벨)+슬롯 오버레이 유지(REQ-029, OQ-1 B/OQ-4 i), option D 활성화로 REQ-030 Approved→Implemented. 전부 change-req(신규 REQ 없음, OQ-3 a). portfolio-hub#8(`*_after`) 동작 중. 단위 350 passed, 인프라 게이트 385 passed, ruff clean. (change-req/confirm-req-implemented) -- 2026-06-08 :: REQ-028 + REQ-032 change-req→Implemented (CHG-20260608-01/02 / -03/04, dcy-016 후속, BAT-022): `trex-core slots` 가 universe `assets`(portfolio)를 슬롯 상태로 오인 표시하던 갭 정정(10개 슬롯인데 universe 1개·status 없음 — BAT-007 HTTP 슬롯 read 제거 후 슬롯 상태 read 표면 부재가 근인). snapshot envelope 에 read-only per-slot `slots[]`(canonical `slot_to_dict`: slot_id/trading_date/status/symbol/qty/avg_price/entry_price/.../portfolio_id) additive 추가(REQ-028 AC-4, strategy `assets[]` 불변), `trex-core slots` 가 `slots[]`(실제 슬롯 상태) 표시·`--status` 슬롯 status 필터·`universe` 는 `assets[]` 유지(REQ-032). BAT-007 stub/BAT-008 envelope 슬롯 스키마 단일화. 신규 코드 데몬 + 실 인프라 운영 재현 PASS(slots=10·status·DB 일치·universe 분리·비침습). 단위 338 passed, ruff clean. pytest 인프라 게이트는 Human Approval Memo 수용. 둘 다 **Implemented**. (change-req/confirm-req-implemented)