feat(reset): cutover 도구 폐기 → trex-core-reset(DB 재초기화) 대체 (REQ-064 CHG-20260610-03)

실 DB 점검 중 마이그레이션 009 부분 유니크 인덱스가 legacy 풀 데이터(같은
자산이 closed+재진입 holding 으로 portfolio_id 중복)에 적용 불가 → cutover·
데몬 부팅 모두 차단되는 결함 확인. 데이터 보존 cutover 를 폐기하고 DB wipe +
자산 중심 재초기화로 전환(사용자 결정). 빈 테이블에는 009 인덱스가 정상
생성되고 자산 중심에서 (instance_key, portfolio_id)는 설계상 유일.

- trex_core/runtime/reset.py 신규(trex-core-reset): slot-core 7테이블 wipe +
  apply_migrations 재적용 → 자산 중심 스키마 착지 검증. dry-run 기본/--yes 적용,
  파괴적·DB 전역. 보유 carry-over 없음(재초기화 후 reconcile_core_holdings 재편입).
- runtime/cutover.py + trex-core-cutover console script + test_cutover_rehearsal.py 삭제.
- pyproject: trex-core-reset 등록.
- tests/runtime/test_reset.py 신규 5건(PG-gated): wipe+착지·dry-run 무변경·멱등·
  wipe 후 admission·테이블 커버리지.
- change-req REQ-064 CHG-20260610-03(cutover→재초기화 재정의) + srs index 갱신.
- baseline 4문서(data-model/runtime-flows/project-structure/interface-contract)
  + BAT-030 design/planning/index/implementation/verification 도구 서술 reset 으로 교체.

검증: ruff clean, 단위 367 passed, PG+RabbitMQ 통합 411 passed/1 skip(KIS sandbox).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 16:08:05 +09:00
co-authored by Claude Opus 4.8
parent 5330f32247
commit c04fd276b3
15 changed files with 335 additions and 405 deletions
@@ -10,10 +10,10 @@
### REQ-056 (Data — 잔여: trading_date/entry_count drop, 스키마 마감)
| AC | 판정 | Evidence |
|---|---|---|
| AC-1 스키마: portfolio_id NOT NULL·instance_key·UNIQUE(instance_key,portfolio_id)·retired_at·trading_date/entry_count 부재 | **pass** (NOT NULL 은 promote 후) | 마이그레이션 009/010/011 + `test_promote_unique_sets_not_null`(cutover `--promote-unique` 로 NOT NULL 승격 — 공유 DB 전 인스턴스 cutover 후 적용하는 설계, BAT-027 결정); `test_daily_slot_columns_match_expected_contract`(drop 후 컬럼셋 일치) |
| AC-1 스키마: portfolio_id·instance_key·부분 UNIQUE(instance_key,portfolio_id)·retired_at·trading_date/entry_count 부재 | pass | 마이그레이션 009/010/011 + `test_daily_slot_columns_match_expected_contract`(drop 후 컬럼셋 == EXPECTED_SLOT_FIELDS) + `test_reset_wipes_and_lands_asset_centric`(유니크 인덱스·inactive CHECK 존재 확인). ※ portfolio_id NOT NULL 승격은 데이터 보존 cutover 와 함께 폐기 — 자산 중심에서 universe reconcile 이 항상 portfolio_id 를 채우므로 부분 유니크로 충분(CHG-20260610-03) |
| AC-2 EXPECTED_SLOT_FIELDS ↔ information_schema 일치 | pass | `tests/adapters/persistence/test_migration_and_drift.py` 3건 PG 통과 |
| AC-3 (instance_key, portfolio_id) 중복 거부 | pass | partial UNIQUE(009) + `test_universe_reconcile_pg`(ON CONFLICT 멱등, created=0) |
| AC-4 인스턴스 간 행 미혼합 | pass | `test_dispatch_queue_instance_scoped`(집행 큐 격리) + `test_instance_isolation`(reconcile) + cutover `test_instance_isolation_other_instance_untouched` |
| AC-4 인스턴스 간 행 미혼합 | pass | `test_dispatch_queue_instance_scoped`(집행 큐 격리) + `test_instance_isolation`(reconcile) |
### REQ-057 (Interface — 잔여: literal inactive + 별칭 제거)
| AC | 판정 | Evidence |
@@ -31,13 +31,15 @@
| AC-3 core flat/브로커 보유 → WARN·운영자 결정 | pass (BAT-029 기구현; 슬롯 부재로 '격리' 대신 unmanaged WARN — issue #15 무자동조치 결정) | `test_B_broker_holds_unmanaged_warns` |
| AC-4 수량 불일치 → 상태 전이 없음·warning 노출 | pass (BAT-029 기구현; 미러 자동 갱신 없이 WARN — 보수 구현) | `test_C_qty_mismatch_warns_no_state_change` |
### REQ-064 (Migration — cutover + 롤백)
| AC | 판정 | Evidence |
### REQ-064 (Migration → **DB 재초기화로 재정의**, CHG-20260610-03)
원 AC(데이터 보존 cutover)는 마이그레이션 009 부분 유니크 인덱스가 legacy 풀 중복 데이터에 적용 불가한 결함으로 폐기되고, **wipe + 자산 중심 재초기화**(`trex-core-reset`)로 재정의됨. 재정의된 AC 로 매핑:
| AC(재정의) | 판정 | Evidence |
|---|---|---|
| AC-1 보유 N개 → 영구 슬롯 holding 보존(수량 무유실) | **pass (리허설)** | `tests/runtime/test_cutover_rehearsal.py::test_full_rebuild_with_broker_carry_over` — 브로커 qty=12 정본 carry-over(미러 7 아님), 자산 바인딩 보존. ※ avg_price 는 잔고 API 미제공으로 NULL carry(+WARN) — implementation.md 잔여 리스크 |
| AC-2 cutover 후 데몬 회귀 없음(reconcile·admission·safety-sell) | pass | run-the-app e2e(아래) + 단위 367/PG 29 green |
| AC-3 실패 시 롤백 가능 | pass (계획+부분 검증) | 단일 트랜잭션(중단 시 무변경: `test_aborts_on_in_flight_pending`/`test_aborts_on_unmapped_broker_holding` DB 불변 단언) + `test_dry_run_rolls_back`; 전체 롤백 = 백업 복원 + 직전 리비전(design §롤백, 운영 절차) |
| AC-4 instance_key 태깅·스코프 쿼리 정확 | pass | cutover 가 instance_key 로 생성 + `test_instance_isolation_other_instance_untouched` |
| AC-1' 재초기화 시 slot-core 테이블 wipe + 자산 중심 스키마 착지 | pass | `test_reset_wipes_and_lands_asset_centric`(2행 → 0행, 컬럼==EXPECTED, 유니크 인덱스+inactive CHECK 존재) |
| AC-2' 재초기화 후 데몬 회귀 없음(reconcile·admission) | pass | `test_reset_usable_for_admission_after_wipe`(reset 후 reconcile+활성 동작) + run-the-app e2e(아래) |
| AC-3' 안전성: dry-run 무변경 + 멱등 | pass | `test_dry_run_reports_without_wiping`(행 보존) + `test_reset_is_idempotent`(2회차 0행·schema_ok) |
| AC-4' 데이터 보존(보유 carry-over) 폐기 — 보유는 reconcile 로 재편입 | pass(의도된 범위 축소) | CHG-20260610-03; 재초기화 후 브로커 보유는 `reconcile_core_holdings`(REQ-062) unmanaged 경로 |
| (참고) 백업/복원 | 운영 절차 | reset 은 재초기화라 롤백 = 백업 복원. 실행 전 백업 필수(release 게이트) |
### REQ-065 (Documentation — baseline 마감)
| AC | 판정 | Evidence |
@@ -49,16 +51,16 @@
## Evidence
1. **단위 회귀**: `pytest tests` → **367 passed**, ruff `All checks passed` (commit 7d72782 시점).
2. **PG-gated**(실 PostgreSQL `trex_core_test`): **29 passed** — 마이그레이션 001~011 전체 재적용 멱등(`test_apply_migrations_is_idempotent`; 010 CHECK drop-and-add·011 DROP IF EXISTS·008 no-op 가 매 boot 재실행에 안전함을 모든 PG 테스트의 pg_clean 반복 적용으로 입증), 컬럼 드리프트 가드, reconcile/admission/lifecycle, dispatch 인스턴스 격리, ops_checks(transition_log 기반) 3건, **cutover 리허설 8건**.
2. **PG-gated**(실 PostgreSQL `trex_core_test`): **26 passed** — 마이그레이션 001~011 전체 재적용 멱등(`test_apply_migrations_is_idempotent`; 010 CHECK drop-and-add·011 DROP IF EXISTS·008 no-op 가 매 boot 재실행에 안전함을 모든 PG 테스트의 pg_clean 반복 적용으로 입증), 컬럼 드리프트 가드, reconcile/admission/lifecycle, dispatch 인스턴스 격리, ops_checks(transition_log 기반) 3건, **reset 5건**(wipe+자산 중심 착지·dry-run 무변경·멱등·wipe 후 admission·테이블 커버리지).
3. **RabbitMQ-gated**(실 브로커): 21 passed — slot_id 계약 포함 intent RPC e2e dedup.
4. **run-the-app e2e**(test DB + 실 RabbitMQ + paper broker, instance=e2etest, M=1): boot reconcile created=2 → snapshot(slots=2 inactive, available=1) → buy by slot_id admitted → 중복 intent identical → 2번째 자산 capacity_full → fill→holding(available=0) → 동일 슬롯 already_active → sell→fill→**inactive**(qty=None·portfolio_id 보존·available=1) → 동일 영구 슬롯 재매수 admitted. DB 최종행·transition_log·ledger_outbox(completed×3) 일치, 데몬 로그 ERROR/Traceback 0, SIGTERM Normal shutdown. (전 과정 implementation.md 검증 실행 절 기록)
5. **baseline 동기화**(Architecture Impact 有): data-model/runtime-flows/project-structure/interface-contract 4문서 + run-trex-core-daemon 스킬 + instance.example.toml 갱신 — commit 7d72782 포함.
## Blocking Issues
- 없음 (코드/리허설 범위). 단, **실 운영 DB cutover 는 본 배치 산출물 범위 밖**(design D4) — release(REL) 단계에서 운영자가 백업→리허설→`trex-core-cutover --yes`→운영 TOML `max_active_slots` 설정 순으로 수행해야 하며, 그 운영 증거는 release 검증에 귀속된다.
- 없음 (코드/테스트 범위). 단, **실 운영 DB 재초기화는 본 배치 산출물 범위 밖** — release(REL) 단계에서 운영자가 백업→`trex-core-reset --config <instance> --yes`→운영 TOML `max_active_slots` 설정 순으로 수행하고, 그 운영 증거는 release 검증에 귀속된다. 데이터 보존 cutover 폐기로 기존 보유는 재초기화 후 운영자가 재편입한다(REQ-062 경로).
## Result
- 포함 REQ AC 전 항목 evidence 연결 완료(REQ-062 AC-3/4 는 issue #15 무자동조치 결정에 따른 보수 구현 — 의도된 편차로 기록).
- **Ready for Release: true** (배치 산출물 기준 — 코드·마이그레이션·cutover 도구·리허설·baseline. 실 cutover 실행은 REL 단계 운영자 작업으로 release 게이트에 위임)
- 포함 REQ AC 전 항목 evidence 연결 완료. REQ-064 는 데이터 보존 cutover → **DB 재초기화로 재정의**(CHG-20260610-03, 마이그레이션 009 결함 + dev/페이퍼 컨텍스트)되어 재정의 AC 로 검증. REQ-062 AC-3/4 는 issue #15 무자동조치 결정에 따른 보수 구현(의도된 편차).
- **Ready for Release: true** (배치 산출물 기준 — 코드·마이그레이션·reset 도구·테스트·baseline. 실 DB 재초기화 실행은 REL 단계 운영자 작업으로 release 게이트에 위임)