docs(req): dcy-020 confirm + 추가 사양(FR-6~8/NFR-4~5/OQ-8) + draft-req REQ-056~065

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 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 10:28:38 +09:00
co-authored by Claude Opus 4.8
parent 1db7ab13bb
commit e2a71e50e2
13 changed files with 676 additions and 24 deletions
@@ -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
+1 -1
View File
@@ -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 |
| dcy-020_20260610_asset-centric-slot-model | 20260610 | 자산 중심 슬롯 모델 재설계 — portfolio-hub universe 와 1:1 슬롯 + 활성 슬롯 개수 제어 (일반 슬롯 풀 모델 대체) | [Discovery](./dcy-020_20260610_asset-centric-slot-model.md) | confirmed |
@@ -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.
@@ -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.
@@ -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 대조로 정합 확인.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
+12 -2
View File
@@ -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 <dir>`(기본 `./config`) + `resolve_config_toml`(슬래시 포함 값은 하위호환 명시 경로)(REQ-048), `.env` 를 config-dir 결정적 해석(`--env-file` 우선, 없으면 `<config_dir>/.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 <ISO8601>` (절대시간) 옵션 추가 — 운영자가 매수 진입 명령 하나로 시간 기반 자동청산(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)