From 808a851d3911d0d19f9f078e1527e9e9a1ac1ce4 Mon Sep 17 00:00:00 2001 From: Justin Ko Date: Thu, 11 Jun 2026 21:12:38 +0900 Subject: [PATCH] =?UTF-8?q?docs(req-058):=20CHG-20260611-01=20=E2=80=94=20?= =?UTF-8?q?symbol=20=EB=88=84=EB=9D=BD=20=EC=88=98=EC=A0=95=20change-req?= =?UTF-8?q?=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reconcile_universe 슬롯 생성 시 symbol 미저장 구현 누락을 AC-5로 추가. Implemented 상태 유지 (기존 AC 1~4 evidence 유효). Co-Authored-By: Claude Sonnet 4.6 --- ...q-058_universe-slot-reconcile-lifecycle.md | 43 ++++++++++++++++++- docs/srs/index.md | 2 +- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/docs/srs/Integration/req-058_universe-slot-reconcile-lifecycle.md b/docs/srs/Integration/req-058_universe-slot-reconcile-lifecycle.md index c94c02e..91353a6 100644 --- a/docs/srs/Integration/req-058_universe-slot-reconcile-lifecycle.md +++ b/docs/srs/Integration/req-058_universe-slot-reconcile-lifecycle.md @@ -9,13 +9,14 @@ - 슬롯 집합을 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/급격 대량 감소면 생성·은퇴를 보류하고 경고한다. +- 스냅샷 refresh(`get_portfolios` 성공 직후)에 슬롯 reconcile 을 수행한다: (1) universe 신규 자산 → 영구 슬롯 즉시 생성(비활성·멱등, `UNIQUE(instance_key, portfolio_id)`), 이 때 `PortfolioItem.symbol` 을 슬롯의 `symbol` 컬럼에 함께 저장한다; (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/급격 대량 감소면 생성·은퇴를 보류하고 경고한다. `symbol IS NULL` 인 기존 슬롯은 다음 reconcile 시 backfill 한다. ## 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 슬롯 생성·은퇴가 보류되고 마지막 집합이 유지되며 경고가 남는다. +5. Given reconcile 실행, When 슬롯 생성, Then `daily_slot.symbol` 이 `PortfolioItem.symbol` 값으로 채워진다. Given 기존 슬롯의 `symbol` 이 NULL, When reconcile, Then 해당 슬롯의 `symbol` 이 backfill 된다. ## Impacted Area - Docs: docs/runtime-flows.md(reconcile lifecycle) @@ -87,6 +88,46 @@ #### Validation Plan - 승인 후 batch planning 에서 본 REQ AC 를 검증 기준으로 사용. +### CHG-20260611-01 +- Date: 2026-06-11 +- Author: Justin Ko + +#### Change Summary +- `reconcile_universe` 가 슬롯 생성 시 `symbol` 을 저장하지 않던 구현 누락 수정. `PortfolioItem.symbol` 이 슬롯 INSERT 에 포함되도록 시그니처를 `set[str]` → `dict[str, str]` (portfolio_id → symbol) 로 변경. 기존 `symbol IS NULL` 슬롯을 다음 reconcile 시 자동 backfill 하는 로직 추가. + +#### Intent +- 슬롯 생성 시 symbol 이 누락되어 집행 루프에서 심볼을 조회할 수 없는 상태가 발생. `reconcile_universe` 인터페이스가 portfolio_id 집합만 전달하고 symbol 을 버리는 구조적 누락이었음. + +#### Acceptance Criteria Delta +- Added: + - AC-5: 슬롯 생성 시 `symbol` 컬럼이 `PortfolioItem.symbol` 로 채워진다. 기존 `symbol IS NULL` 슬롯은 다음 reconcile 시 backfill 된다. +- Updated: + - Requirement 본문에 "(이 때 `PortfolioItem.symbol` 을 슬롯의 `symbol` 컬럼에 함께 저장한다)" 및 backfill 조건 추가. +- Removed: 없음 + +#### Impacted Area Delta +- Modules: + - `trex_core/application/snapshot_scheduler.py` — `_reconcile_slots`: `set` → `dict` comprehension + - `trex_core/domain/slot/repository.py` — `reconcile_universe` 시그니처 + - `trex_core/domain/slot/in_memory_repository.py` — 생성 시 symbol 전달 + backfill + - `trex_core/adapters/persistence/postgresql.py` — INSERT에 symbol 추가 + backfill UPDATE +- Tests: 29개 테스트 파일 `reconcile_universe` 호출부 `set` → `dict` 일괄 갱신 (420 passed) +- Docs: 없음 (runtime-flows.md 는 reconcile lifecycle 자체 변경 없음) + +#### Delivery Trace Delta +- Batches: BAT-028(구현 근거), BAT-030(e2e 검증 근거) — 기존 evidence 유효, 신규 AC-5 는 커밋 24a6017 으로 검증 +- Releases: rel-006 배포 후 운영 중 발견(슬롯 symbol NULL 확인) — 핫픽스 아닌 즉시 수정 + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no (AC 1~4 는 기존 evidence 그대로 유효) +- Status Recommendation: keep-current (Implemented) +- Follow-up Action: 없음. 420개 테스트 통과 + DB 확인(23개 슬롯 전부 symbol 채움)으로 AC-5 검증 완료. + +#### Validation Plan +- `reconcile_universe` 호출 후 생성된 슬롯의 `symbol` 이 NULL 이 아닌지 단위 테스트 확인. +- 기존 `symbol IS NULL` 슬롯 보유 DB 에서 데몬 재시작 후 전 슬롯 symbol 채움 확인 (test 인스턴스 23/23 confirmed). + ### CHG-20260610-IMPL - Date: 2026-06-10 - Author: Justin Ko diff --git a/docs/srs/index.md b/docs/srs/index.md index 94e8608..b3a9dd4 100644 --- a/docs/srs/index.md +++ b/docs/srs/index.md @@ -159,10 +159,10 @@ ## Recent Change Log Summary +- 2026-06-11 :: REQ-058 change-req (CHG-20260611-01): `reconcile_universe` 슬롯 생성 시 `symbol` 누락 수정. 시그니처 `set[str]` → `dict[str, str]`(portfolio_id→symbol), INSERT에 symbol 포함, `symbol IS NULL` 기존 슬롯 backfill 추가. AC-5 신규 추가. 기존 AC 1~4 evidence 유효 → **Implemented 유지**. - 2026-06-11 :: BAT-035/036 완료 — REQ-073/074/075/021/034/063/048/005 Approved → **Implemented**, REQ-028 → **Deprecated** (trex.snapshot.req 코드 삭제·trex.slots.req 대체) - 2026-06-11 :: REQ-028 Implemented → **Deprecated** (BAT-035, dcy-025): `trex.snapshot.req` envelope (`assets[]`/`View`/`build_snapshot_envelope`) 코드 삭제 완료. 후속 표면은 REQ-073/074 (`trex.slots.req`) 로 대체. - 2026-06-11 :: REQ-073/074/075 신규 생성(Proposed) + REQ-021/034/063 change-req Implemented→**Approved** + REQ-028 폐기 예정 forward-link (draft-req, dcy-025 전략 read 표면 재설계): `trex.snapshot.req`→`trex.slots.req` 즉시 교체 RPC 계약(요청 {filter: status 단일, spy}, 응답 {slots[], degraded/reason/message}, strategy 제외)(REQ-073), 슬롯 단일 행 스키마(asset_quantity hub 정본+qty 미러+warned, cash_balance+cash_estimated, watchdog nested, paused — 필드별 정본 라벨)(REQ-074), baseline 정합(REQ-075). 토폴로지 큐 교체(REQ-021 CHG-01), 워치독 터치=slots.req 기본/spy 무터치·행 흡수(REQ-034 CHG-01), 운영자 CLI 새 표면+spy(REQ-063 CHG-01). REQ-028 은 keep-current + delivery 배치에서 Deprecated 전환(REQ-005 선례). 전략 모듈 lockstep cutover 필요. Next ID: REQ-076. - 2026-06-11 :: REQ-048/005 change-req (dcy-024 safety-sell 비활성화 모드/issue #15, CHG: 048-20260611-01·005-20260611-01): per-instance TOML `safety_sell_enabled: bool=True`(env 제외, REQ-048 AC-10) + `daemon.start()` 조건부 워커 게이팅(false 시 `trex-safety-sell` 미기동·물리 차단) + 기동 로그 `no autonomous liquidation` 경고 + runtime-flows(REQ-005 AC-5/6). `ExecutionService`·운영자 exit/cancel·kill-switch·전략 sell intent 불변. 모드는 기동 로그만 노출(스냅샷 응답 미포함, OQ-3). **신규 REQ 없음**(change-req only, dcy-024 OQ-1). 둘 다 Implemented→**Approved**, 차기 batch 구현·검증 후 복귀. - 2026-06-11 :: REQ-068~072 + REQ-013/061(CHG) → Implemented (confirm-req-implemented, BAT-032 dcy-022 실행 정책): timeout_sec·slippage_pct intent 계약·슬롯 lifecycle(REQ-068), timeout 초과 자동 취소·cancel-fill race(REQ-069), slippage 보호 지정가 dispatch(REQ-070), 운영자 `trex-core cancel` 백스톱(REQ-071), baseline 정합(REQ-072), 집행 루프 확장(REQ-013 CHG-03)·intent 필드 추가(REQ-061 CHG-05). BAT-032 verification — 신규 71 tests, 425 passed 회귀 없음, Blocking Issues 없음. - 2026-06-10 :: REQ-002/006/007/028/032 → Implemented (confirm-req-implemented, BAT-034 dcy-023 allocated_capital 제거): 도메인(Slot/EXPECTED_SLOT_FIELDS)·intent(BuyIntent·수신 무시)·영속(마이그레이션 013 drop + flat 매핑)·snapshot(slots[])·CLI(--capital argparse 거부) 전 경로 제거. 테스트 DSN 013 멱등 round-trip + 컬럼 부재, 428 passed + PG 21 passed + ruff clean, grep 전수 0건. BAT-034 release-candidate. release 시 사전 백업·롤백 절차 필수(비가역 drop). -- 2026-06-10 :: REQ-002/006/007/028/032 change-req (dcy-023 allocated_capital 죽은 필드 제거/issue #22, CHG: 002-02/006-02/007-02/028-01/032-07): 전부 Implemented→**Approved** 되돌림(dcy-023 batch 재구현 대기). Slot 모델·`EXPECTED_SLOT_FIELDS` 필드 제거(REQ-002), `daily_slot` 컬럼 drop 마이그레이션 013 + flat 매핑(REQ-006, 코드 배포와 동시 적용·사전 백업), buy intent payload 즉시 제거·수신 무시(REQ-007), snapshot `slots[]` 노출 제거(REQ-028), CLI `--capital` argparse 제거 + slots detail 행 제거(REQ-032 AC-8). 외부 발신자(trex-orb-v1) 송신 잔존은 unknown key 무시로 무해(운영자 확인).