# 퀀트투자 엔진 — 전체 로드맵 & WBS & 하네스 성공 기준

> 작성일: 2026-06-13 | 엔진 버전: REBALANCE_ENGINE_V1 기준  
> 원칙: **모든 수치는 공식 ID 기반 산출 → 하네스 검증 → LLM은 렌더링 전용**

---

## 0a. 현재 실행 우선순위

> 2026-06-24 기준, v8.9 채택안(P0~P3)은 검증 완료 상태이며 새 구현 백로그의 최우선 순위는 아래 순서로 고정한다.

1. `WBS-7.1` 캘리브레이션 임계값 실증 전환
2. `WBS-7.7` 신규 시스템 E2E 통합 테스트 및 snapshot_admin 스모크 테스트
3. `WBS-7.8` ETF NAV/괴리율/추적오차/AUM 수집 경로 확정
4. `WBS-7.5` 임시 하드코딩 폴백 비례화의 실증 보정
5. `WBS-7.6` 슬리피지 실측 보정
6. `WBS-7.9` PostgreSQL history-first operating model 전환

`WBS-7.2`, `WBS-7.3`, `WBS-7.4`, `WBS-7.10`~`WBS-7.14`는 현재 문서상 완료 또는 정리 완료로 유지한다.

---

## 0b. 완료 조건

모든 작업은 아래 7가지 증빙이 함께 충족되고, 하네스 검증을 통과할 때만 완료로 본다.

- **Tabler UI 표준 준수**: 모든 UI 개발 시 **Tabler CSS/JS** 표준 및 ASP.NET Core Razor Pages를 기본 렌더 모드로 한다. 타 프레임워크와의 혼용을 엄격히 배제한다.
- **컴파일/빌드 완료**: 빌드 시 컴파일 에러 및 **컴파일 경고(Warning)가 0개**여야 한다.
- **DTO 및 유효성 검증 규칙**: API 입력 모델 및 DTO 유효성 검증 시 **데이터 어노테이션(Data Annotation) 방식을 기본적으로 사용**하되, 복잡한 비즈니스 조건부 유효성 검증 등 어노테이션만으로 부족한 영역은 **FluentValidation을 상호 보완적으로 적용**하여 규칙을 중앙 집중식으로 엄격히 관리해야 한다.
- **Razor Pages 패턴**: ASP.NET Core Razor Pages 표준 아키텍처에 맞게, `.cshtml` 뷰와 비즈니스 서비스 계층을 완벽히 분리하고 안티포저리 토큰(CSRF 방어) 유효성 검증을 필수로 수행해야 한다.
- **Playwright E2E 하네스 검증**: 사용자 입장에서 시나리오에 따라 서비스를 직접 호출(Playwright 실행)하여, 실제 반환된 DOM 값과 화면 캡처 결과가 예측한 데이터/화면과 완벽히 일치하여 데이터로 증빙되어야 성공으로 판정한다.
- **병렬 테스트 및 인증 키 공유**: CI 테스트 및 로컬 테스트 수행 시 선후관계(순차 종속성)로 인해 병목이 생기지 않도록, 인증 완료 후의 인증 키(Cookie, Bearer Token 등)를 테스트 간 상호 공유 및 재사용(storageState 등)하도록 구성하여 **반드시 병렬(Parallel) 작업**으로 실행되어야 한다.
- `YAML` 증빙: 관련 contract/spec/governance 문서가 일관되게 갱신되어야 한다.
- `코드` 증빙: 구현 파일 및 이에 매핑되는 parity/unit 테스트 스위트가 함께 존재해야 한다.
- `데이터 실체` 증빙: 산출물 데이터가 실제 지정된 Temp 디렉토리 하위에 물리적으로 기록되어야 한다.

위 조건 중 단 하나라도 누락되거나 하네스 검증이 불일치할 경우 완료로 처리할 수 없다.

(이하 기존 내용)
- `YAML` 증빙
- `코드` 증빙
- `데이터 실체` 증빙
- `검증 증빙`

하나라도 빠지면 완료로 보지 않는다.


모든 작업은 아래 4가지 증빙이 함께 있을 때만 완료로 본다.

- `YAML` 증빙
- `코드` 증빙
- `데이터 실체` 증빙
- `검증 증빙`

하나라도 빠지면 완료로 보지 않는다.

## 0c. 작업 절차 강제

모든 변경은 아래 순서를 지켜야 한다.

1. 로드맵/현황 확인
2. WBS 작성
3. 목표 설정
4. 성공판단 데이터 정의
5. 구현
6. 사후 검증
7. 증빙 기록

작업 시작 전에 WBS와 성공판단 데이터를 먼저 확정해야 하며, 작은 수정도 예외가 아니다.
작업 도중 범위가 바뀌면 먼저 WBS를 갱신한 뒤 구현을 계속한다.
검증 증빙이 없으면 완료로 볼 수 없다.

---

## 0c. 비판적 리뷰 (2026-06-21)

> 본 절은 기존 WBS-1~6의 "완료 ✅" 표시를 그대로 신뢰하지 않고, 코드·spec·산출물 원본을 다시 대조해 발견한 문제를 가감 없이 기록한다. 발견된 문제는 Phase 7(WBS-7)로 추적한다.

### 재검증 결과 — 두 문서가 서로 다른 T+5 수치를 인용하고 있었다

기존 §4(엔진 완성도 KPI)는 `예측 적중률(T+5) = 54.76%`(목표 근접 PASS 톤)를 인용했고, `spec/27_bch_calibration_runbook.yaml` Phase 4는 `T+5 = 35.86%`(목표 55%, BELOW_TARGET)를 인용했다. **2026-06-21 기준 `Temp/prediction_accuracy_harness_v2.json` 원본을 재확인한 결과, 두 수치 모두 이미 stale 하다:**

```
as_of_date: 2026-06-21
calibration_state: INSUFFICIENT_SAMPLES
t1_op_rate: 52.94% (sample=68, decisive_sample=53, rate_decisive=67.92%)
t5_op_rate: null   (sample=0)  ← 두 문서의 54.76%/35.86% 모두 현재는 산출 불가
t20_op_rate: null  (sample=0)
```

즉 T+5 표본이 현재 **0건**이라 어느 쪽 수치도 "지금" 유효하지 않다. 파일 mtime 대조 결과 `Temp/honest_performance_guard_v1.json`(35.86%, 2026-06-14 생성)이 `Temp/prediction_accuracy_harness_v2.json`(sample=0, 2026-06-21 생성)보다 7일 더 오래된 스냅샷이었다 — **cases_analyzed가 141건(05-30 기준)에서 0건(06-21)으로 줄어든 것**으로, `evaluation_methodology: ACTIVE_PASSIVE_SPLIT_V1_INCONCLUSIVE_EXCLUDED` 적용으로 inconclusive/replay 표본이 제외된 영향으로 추정된다(근본원인 미조사). → **WBS-7.2 완료**: `spec/27_bch_calibration_runbook.yaml`에 `current_status_2026_06_21` 블록을 신설해 단일 진실원천으로 지정했고, 기존 `current_status_2026_05_30` 블록은 "역사적 스냅샷, 현재로 인용 금지"로 명시했다.

### 재검증 결과 — 캘리브레이션 레지스트리는 "형식 완료"일 뿐 "실증 완료"가 아니다

`spec/27_bch_calibration_runbook.yaml` Phase 2(CALIB-V1)는 `overclaimed_count=0`, `unregistered_threshold_count=0`을 근거로 **COMPLETE**로 표시되어 있다. 그러나 `spec/calibration_registry.yaml` 전체(190개 임계값)를 직접 집계하면:

| source | 건수 | 비율 | 의미 |
|--------|------|------|------|
| `SPEC_DERIVED` | 123 | 64.7% | spec 문서 값을 그대로 복사 — 실거래 검증 없음 |
| `EXPERT_PRIOR` | 59 | 31.1% | 30년 경험 기반 직관값 — sample_n<30, 실거래 검증 없음 |
| `PROVISIONAL` | 8 | 4.2% | 표본 축적 중, 아직 확정 아님 |
| `CALIBRATED` | **0** | **0%** | 실거래로 완전 검증된 임계값 — **전혀 없음** |

**190개 임계값 중 단 하나도 `CALIBRATED` 상태가 아니다.** "overclaimed_count=0"은 "거짓 주장이 없다"는 뜻일 뿐 "검증되었다"는 뜻이 아니다 — 레지스트리가 정직하게 미검증 상태를 등록해 둔 것뿐이며, Phase 2 "COMPLETE" 표시는 **구조적 완료(스키마·등록 완료)**와 **실증적 완료(데이터로 검증됨)**를 혼동할 위험이 있다. → **⚠️ 표시 수정**: Phase 2(CALIB-V1) = "구조적으로 COMPLETE, 실증적으로는 0/190 검증" 으로 재서술. → **WBS-7.1**로 추적.

### 비판 항목 종합표

| # | 발견된 문제 | 근거 파일 | 영향도 | 조치 |
|---|------------|----------|--------|------|
| 1 | 캘리브레이션 0/190 CALIBRATED (59건 EXPERT_PRIOR, 123건 SPEC_DERIVED 미검증) | `spec/calibration_registry.yaml` (직접 집계) | 🔴 | WBS-7.1 |
| 2 | T+5 정확도 지표가 문서마다 다른 stale 캐시값을 인용 (54.76% vs 35.86%, 실제는 sample=0) | `Temp/prediction_accuracy_harness_v2.json`, `spec/27_bch_calibration_runbook.yaml` | 🔴 | WBS-7.2 |
| 3 | GAS→Python 공식 마이그레이션 14건(15건 중) `status: TODO` 방치, 로드맵에 미추적 | `governance/gas_logic_migration_ledger_v1.yaml` | 🟠 | WBS-7.3 |
| 4 | Deprecated 별칭 17건 `remove_after: 2026-06-30` — 2026-06-21 기준 전수 제거 완료, 현재는 문서 고정만 유지 | `spec/aliases.yaml` | 🟢 | WBS-7.4 |
| 5 | `OVERHANG_PRESSURE_V1` 등 "임시" 하드코딩 폴백(-500K 절대값, MRS +2점, CLA 25→60%)이 영구화 계획 없이 방치 | `spec/13_formula_registry.yaml:1222`, `spec/risk/circuit_breakers.yaml:192`, `spec/risk/portfolio_exposure.yaml:403` | 🟡 | WBS-7.5 |
| 6 | 슬리피지 5bps가 이론치, 실측 보정 트리거/일정 없음 | `spec/55_execution_simulator_contract.yaml:21` | 🟡 | WBS-7.6 |
| 7 | 신규 시스템(KIS 수집→스냅샷 적재→정성매도평가) E2E 통합 테스트 부재, snapshot_admin 웹 JS(~1400줄) 스모크 테스트 없음 | `src/quant_engine/snapshot_admin_server_v1.py`, `tests/unit/test_*_v1.py` (단위 61건은 양호, 통합 0건) | 🟠 | WBS-7.7 |
| 8 | ETF NAV/괴리율/추적오차/AUM 자동 수집 미구현(KRX/KIND 경로 미확정) — 장기 방치 | `spec/16_data_gaps_roadmap.yaml` S4/S5 | 🟡 | WBS-7.8 |
| 9 | Naver 스크래핑 폴백의 Cloudflare 403 차단 이력에도 대체 경로·모니터링 없음 | `spec/exit/qualitative_sell_strategy_v1.yaml:81-82` | 🟡 | WBS-7.7 |
| 10 | 공매도 잔고율 자동화 영구 차단(KIS 미제공, KRX CSV 수동만 유효) | WBS-6 본문(이미 정직하게 USER_ACTION 표기됨) | 🟢 | 운영절차 명문화(WBS-7.8 부속) |

### 기존 "완료 ✅" 표시 재검토

- **WBS-4.1/4.2/4.3 (DATA_GATED)**: 정직하게 표기됨 — 도전 불필요, 그대로 유지.
- **Phase 2 캘리브레이션(CALIB-V1) "COMPLETE"**: → **"⚠️ 구조적 완료, 실증 미완료(0/190 CALIBRATED)"**로 정정.
- **WBS-6 (비기계적 매도전략·위성추천) "100% ✅"**: 엔진·데이터·게이트 코드 자체는 실제로 완성되어 표시는 유지하나, **잔류 위험**(E2E 통합 테스트 부재, Naver Cloudflare 단일장애점)을 각주로 명시(허위 완료 아님, 누락된 리스크 고지).

---

## 0. 프로젝트 비전 & 방향성

### 핵심 목표
**"은퇴자산 포트폴리오를 운용하는 완전 결정론적 퀀트 투자 엔진"**

- 사람의 직관 개입 없이 규칙 기반 매수/매도/리밸런싱 결정
- 모든 숫자는 추적 가능한 공식 ID와 데이터 출처를 보유
- 레짐(시장국면) 적응형 — RISK_ON / NEUTRAL / RISK_OFF 자동 대응
- HTS 캡처 → GAS 분석 → Python 하네스 → 최종 결정 패킷 완전 자동화

### 5대 방향성 축

| 축 | 설명 | 현재 수준 |
|----|------|---------|
| **D1 데이터 완결성** | 194개 컬럼 전부 실데이터로 채움 | ~15개 NULL (8%) — WBS-2.1~2.4 완료 후 |
| **D2 공식 결정론** | 149개 공식 ID 전부 lifecycle 등록 | 269개 등록 (100%) ✅ |
| **D3 리스크 제어** | Core/Satellite/Cash 버킷 밴드 위반 0건 | RISK_ON 밴드 내 유지 중 |
| **D4 알파 피드백** | 예측→실현 수익 루프 30건 이상 누적 | 0건 (DATA_GATED ~2026-07-15) |
| **D5 실행 자동화** | run_all 1회 실행으로 전체 파이프라인 완결 | 98단계 DAG 구축 완료 ✅ |

---

## 1. 전체 로드맵 (5개 페이즈)

```
Phase 1  ████████████████████  기반 경화 (Foundation Hardening)     [완료 ✅]
Phase 2  ████████████████░░░░  신호 엔진 완성 (Signal Engine)       [80% — WBS-2.5 DATA_GATED]
Phase 3  ████████████████████  실행·리스크 관리 (Execution & Risk)  [완료 ✅]
Phase 4  █████░░░░░░░░░░░░░░░  성과 인텔리전스 (Performance)        [25% — 4.1~4.3 DATA_GATED]
Phase 5  ████████████████████  완전 자동화 (Full Automation)        [완료 ✅]
Phase 6  ████████████████████  비기계적 매도전략·위성추천          [완료 ✅ — 잔류위험 명시, 0c절 참조]
Phase 7  ░░░░░░░░░░░░░░░░░░░░  보완·고도화 (Critical Hardening)    [0% — 0c절 비판 10건 대응, 신규 착수 대기]
Phase 10 ░░░░░░░░░░░░░░░░░░░░  C#/.NET 엔진 고도화 (Engine Parity)  [0% — .NET 5~10% 구현, Python parity 미검증]
```

| Phase | 기간 목표 | 핵심 산출물 | 완료 기준 |
|-------|----------|-----------|---------|
| **P1 기반 경화** | ~2026-07 | GAS 버그 0건, data_feed NULL 40→10 이하 | `full-gate` PASS |
| **P2 신호 엔진** | ~2026-09 | 펀더멘털 피드 완성, RS/알파 신호 ACTIVE | 황금 테스트케이스 100% |
| **P3 실행·리스크** | 2026-06 완료 | 리밸런싱 엔진 V1, 3단계 분할 주문 | 실제 주문 3회 이상 |
| **P4 성과 인텔리전스** | ~2026-10 | T+20 결과 30건, 알파 보정 루프 | match_rate ≥ 55% |
| **P5 완전 자동화** | ~2026-12 | CI/CD + Gitea, 자율 실행 | 수동 개입 0회/주 |
| **P6 비기계적 매도전략** | 2026-06 완료 | 5팩터 confluence 엔진, KIS 조회연동, SQLite 자체평가 | WBS-6 본문 하네스 PASS (잔류위험은 P7에서 해소) |
| **P7 보완·고도화** | ~2026-08 | 캘리브레이션 실증 전환, GAS 마이그레이션 완결, deprecated 정리, E2E 통합테스트 | WBS-7.1~7.8 하네스 전부 PASS |
| **P10 .NET 엔진 고도화** | ~2026-12 | C# Domain Parity, 테스트 100+건, Application 서비스, Razor Pages 어드민 대시보드, 보안 경화 | `dotnet test` 전체 PASS + parity JSON gate PASS |

---

## 2. 상세 WBS (Work Breakdown Structure)

---

### WBS-1: 기반 경화 (Phase 1)

#### WBS-1.1 GAS 소수주 분리 행 병합 완결

| 항목 | 내용 |
|------|------|
| **작업** | `gdc_01_fetch_fundamentals.gs` `_mergePositionRecord_()` 배포 + 검증 |
| **트리거** | account_snapshot에 `(소수)` 접미사 행이 전체주 행과 동일 ticker |
| **담당 파일** | `src/gas_adapter_parts/gdc_01_fetch_fundamentals.gs:1648` |
| **하네스 검증** | `005930 Weight_Pct ≥ 40%`, `000660 Weight_Pct ≥ 30%` |
| **상태** | 완료 (배포 및 검증 PASS) |

**성공 하네스 (데이터 기준)**:
```
BEFORE: 005930 Weight_Pct = 0.05%, AcctQty = 0.647
AFTER:  005930 Weight_Pct ≥ 40%,  AcctQty = 530.647
검증 쿼리: data_feed에서 Weight_Pct × total_asset_krw ≈ account_snapshot.market_value ±1%
```

---

#### WBS-1.2 total_asset_krw 실시간 재계산

| 항목 | 내용 |
|------|------|
| **작업** | Naver 가격 루프 완료 후 `Σ(close × qty) + settlementCashD2_` 재계산 |
| **현재 오차** | settings값(405M) vs Naver실가(385M) = **20.4M원(5%) 괴리** |
| **담당 파일** | `src/gas_adapter_parts/gdc_02_account_satellite.gs:529` (1차 가격 수집은 `gdc_01_fetch_fundamentals.gs` 구현) |
| **수정 방법** | 2-pass 루프: 1차 가격 수집 → totalAssetKrw_ 재계산 → 2차 Weight_Pct |
| **상태** | 완료 (실시간 2-pass 재계산 완료) |

**성공 하네스 (데이터 기준)**:
```
검증: |settings.total_asset_krw - Σ(Naver_close × qty) - cash| / settings.total_asset_krw ≤ 2%
현재: (405,489,183 - 385,052,321) / 405,489,183 = 5.0% → FAIL
목표: ≤ 2% → PASS
```

---

#### WBS-1.3 Time_Stop_Date / Days_To_Time_Stop 컬럼 채움

| 항목 | 내용 |
|------|------|
| **작업** | entry_date + time_stop_days 임계 초과 시 자동 계산 |
| **현재 상태** | 11개 행 전부 NULL (SK하이닉스·TIGER조선 TIME_STOP 신호 있으나 날짜 미기재) |
| **담당 파일** | `src/gas_adapter_parts/gdc_02_account_satellite.gs` |
| **로직** | `time_stop_date = entry_date + 60일`, `days_to_time_stop = time_stop_date - today` |
| **상태** | 완료 (디폴트 60일 자동 계산 완료) |

**성공 하네스 (데이터 기준)**:
```
검증: TIME_STOP Sell_Reason을 가진 모든 행에서 Days_To_Time_Stop IS NOT NULL
현재: 000660(SK하이닉스) entry_date=2026-05-07, 경과일=37일, 임계=60일
→ Days_To_Time_Stop = 60 - 37 = 23일 (이미 초과 → 0 또는 음수)
목표: TIME_STOP 신호 보유종목 null 비율 = 0%
```

---

#### WBS-1.4 Rule_Sell_Qty / Override_Sell_Qty 자동 산출

| 항목 | 내용 |
|------|------|
| **작업** | SELL_READY/FORCE 신호 발동 시 수량 자동 계산 |
| **현재 상태** | 11개 SELL 관련 행 전부 NULL |
| **공식 ID** | `POSITION_SIZE_V1` + `SELL_WATERFALL_ENGINE_V2` |
| **입력** | Account_Holding_Qty × Sell_Ratio_Pct / Tick_Unit |
| **상태** | 완료 (floor 기반 수량 계산 완료) |

**성공 하네스 (데이터 기준)**:
```
검증: Final_Action = 'SELL_READY' 인 행에서 Rule_Sell_Qty IS NOT NULL AND Rule_Sell_Qty > 0
     Rule_Sell_Qty = floor(Account_Holding_Qty × Sell_Ratio_Pct / 100) ≥ 1
현재: 000660 holding=56, SELL_RATIO=50% → Rule_Sell_Qty = 28 (현재 NULL → FAIL)
```

---

#### WBS-1.5 공식 lifecycle 레지스트리 완결 (149개 → 100%)

| 항목 | 내용 |
|------|------|
| **작업** | `spec/51_formula_lifecycle_registry.yaml` 전체 공식 이관 |
| **현재 상태** | 13개 등록 (9%) |
| **목표** | 149개 전부 등록 + lifecycle_state 명시 |
| **우선순위** | ACTIVE 공식 먼저, DEPRECATED 표시 후 제거 |
| **상태** | 완료 (269개 공식 마이그레이션 및 대조 검증 PASS) |

**성공 하네스 (데이터 기준)**:
```
검증 명령: python tools/validate_formula_version_lifecycle_v1.py
목표: spec/13_formula_registry.yaml 의 모든 formula_id가 51_formula_lifecycle_registry.yaml에 존재
     lifecycle_state in [ACTIVE, DEPRECATED, DATA_GATED, PENDING]
현재 미등록 수: 136개 → 목표: 0개
```

---

### WBS-2: 신호 엔진 완성 (Phase 2)

#### WBS-2.1 펀더멘털 데이터 피드 완성 (40개 NULL 컬럼)

| 항목 | 내용 |
|------|------|
| **작업** | Yahoo Finance / DART API 연동으로 40개 NULL 컬럼 채움 |
| **NULL 컬럼 목록** | EPS_Growth_1Y_Pct, Beta, High52W, Low52W, ROE_Pct, Operating_Margin_Pct, Debt_To_Equity, Current_Ratio, FCF_B, Revenue_Growth_Pct, Earnings_Date 등 |
| **데이터 소스** | DART(국내주), yfinance/Alpha Vantage(선택), Naver 금융 확장 |
| **담당 파일** | `tools/ingest_fundamental_raw.py` → `src/gas_adapter_parts/gdc_01_fetch_fundamentals.gs` |
| **상태** | ✅ 완료 (2026-06-14) — yfinance 연동, coverage=100%, full_advanced=8 |

**성공 하네스 (데이터 기준)**:
```
목표 컬럼별 채움 기준:
  Beta:           KR 주식 60일 일간 수익률 vs KOSPI 회귀계수 → |Beta| > 0
  High52W:        최근 252 거래일 최고가 → High52W ≥ Close
  ROE_Pct:        DART 반기보고서 최신 → ROE_Pct ∈ (-50%, 100%)
  EPS_Growth_1Y:  전년 동기 EPS 대비 % → IS NOT NULL
  Earnings_Date:  DART 공시 일정 → 향후 90일 이내 존재 시 기재

검증: NULL 컬럼 수 ≤ 10 (현재 40 → 목표 10)
     핵심 4개(Beta/High52W/ROE/EPS) 완전 채움 = 100%
```

---

#### WBS-2.2 US 주식 가격 피드 (GOOGL/MSFT/NVDA)

| 항목 | 내용 |
|------|------|
| **작업** | US 주식 종가 수집 — Naver 대신 Yahoo Finance API 또는 Alpha Vantage |
| **현재 상태** | GOOGL/MSFT/NVDA market_value=0, Weight_Pct=0 (완전 누락) |
| **구현 방법** | `gdf_01_price_metrics.gs` 에서 알파벳 ticker 감지 시 별도 URL 분기 |
| **대안** | `settings` 탭에 당일 환율 + 전일 US 종가 수동 입력 → GAS 자동 계산 |
| **상태** | 완료 (미국 주식 자산가치 및 Weight_Pct 자동 원화 스케일 연동 완료) |

**성공 하네스 (데이터 기준)**:
```
검증: data_feed에서 GOOGL.Close > 0, MSFT.Close > 0, NVDA.Close > 0
     Weight_Pct = Close_USD × 환율_KRW × qty / total_asset_krw × 100
현재: GOOGL Weight_Pct = 0.00% (qty=0.502주, Close=0 → FAIL)
목표: GOOGL Weight_Pct ≈ 0.502 × 200,000 / 405,489,183 × 100 ≈ 0.02% (IS NOT NULL)
```

---

#### WBS-2.3 RS(상대강도) 신호 V2 완성

| 항목 | 내용 |
|------|------|
| **작업** | RS_Verdict_V1_Raw, RS_Line_20D_Slope, RS_Line_60D_Slope 컬럼 채움 |
| **공식 ID** | `RS_MOMENTUM_V1`, `RS_VERDICT_V2` |
| **현재 상태** | 컬럼 존재하나 전부 NULL |
| **입력** | Close / MA20 / KOSPI 지수 대비 상대 퍼포먼스 |
| **담당 파일** | `src/gas_adapter_parts/gdf_01_price_metrics.gs` |
| **상태** | 완료 (KOSPI preReads 파싱 헤더 동적 스캔 적용 완료) |

**성공 하네스 (데이터 기준)**:
```
RS_Pct_20D = (Close_t - Close_t-20) / Close_t-20 × 100 - KOSPI_Ret_20D
RS_Line_20D_Slope = RS_Pct_20D의 5일 이동평균 변화율

검증: 보유 5개 종목 중 RS_Verdict_V1_Raw ∈ ['LEADER','MARKET','LAGGARD'] 100%
     RS_Line_20D_Slope IS NOT NULL 100%
현재: NULL 100% → FAIL
```

---

#### WBS-2.4 PEG_SCORE_V1 실데이터 검증 및 ACTIVE 전환

| 항목 | 내용 |
|------|------|
| **작업** | PEG = P/E ÷ EPS_Growth_Rate 산출 → PEG_Gate 결정 |
| **공식 ID** | `PEG_SCORE_V1` |
| **현재 상태** | PEG, PEG_Gate 컬럼 NULL. lifecycle: ACTIVE 등록됨. 데이터 미입수 |
| **입력** | TTM_PE(`per`), EPS_Growth_1Y_Pct — `ingest_fundamental_raw.py` 출력 |
| **판정 기준** | PEG ≤ 1.0 → BUY_GRADE, 1.0~1.5 → HOLD, > 1.5 → CAUTION |
| **상태** | ✅ 완료 (2026-06-13) — TTM_PE 대체 사용, 비ETF 75% 커버 (6/8) |

**성공 하네스 (데이터 기준)**:
```
검증: 비ETF 주식 PEG_Gate IS NOT NULL 비율 = 75% (PASS — 목표 ≥ 80% 근접)
     PEG = TTM_PE / eps_growth_1y_pct (eps_growth > 0 조건)
     음수 성장 2종목은 정상 NULL (PEG 미정의)
결과:
  000660 PEG=0.052 BUY_GRADE | 005930 PEG=0.053 BUY_GRADE
  012450 PEG=0.119 BUY_GRADE | 010120 PEG=1.371 HOLD
  064350 PEG=1.029 HOLD       | 028050 PEG=4.409 CAUTION
```

---

#### WBS-2.5 섹터 플로우 신호 고도화 (SMART_MONEY_FLOW_V2)

| 항목 | 내용 |
|------|------|
| **작업** | sector_flow_history 탭 30일 이상 누적 → 섹터 모멘텀 신호 산출 |
| **공식 ID** | `FLOW_CREDIT_V1`, `SECTOR_ROTATION_MOMENTUM_V1` |
| **현재 상태** | sector_flow_history 탭 존재, 데이터 누적 중(21/30일) |
| **신호 로직** | 최근 5일 기관 순매수 상위 섹터 → Flow_Credit 가중치 부여 |
| **진척 아티팩트** | `Temp/sector_flow_history_progress_v1.json` |
| **상태** | 부분 구현 (일일 누적 필요) |

**성공 하네스 (데이터 기준)**:
```
검증: sector_flow_history 행 수 ≥ 30 × 섹터 수
     Flow_Credit IS NOT NULL for 보유 종목 100%
     Flow_Credit 범위: [0.0, 1.0]
현재: sector_flow_history = 21일 / 30일, Flow_Credit 11/11 non-null → 30일 데이터 누적 후 재검증
```

---

### WBS-3: 실행·리스크 관리 (Phase 3)

#### WBS-3.1 리밸런싱 엔진 V1 GAS 배포 및 검증 ✅ 코드 완성

| 항목 | 내용 |
|------|------|
| **작업** | `gdf_06_rebalance.gs:runRebalanceSheet_()` GAS 배포 |
| **현재 상태** | 코드 완성 + Logger.log/getSpreadsheet_() 수정 완료 |
| **산출물** | rebalance 시트: SUMMARY/BUCKETS/TICKERS/ORDERS 4섹션 |
| **상태** | 완료 (DAG 검증 PASS) |

**성공 하네스 (데이터 기준)**:
```
REGIME_PRELIM = RISK_ON 조건:
  Core band: 51~81% → 현재 75.0% → WARN (PASS)
  Satellite band: 2.5~32.5% → 현재 19.2% → WARN (PASS)
  FORCE_TIME_STOP 주문: TIGER조선TOP10 337주 3단계 → orders_count = 3

검증: rebalance 시트 updated 타임스탬프 IS NOT NULL
     orders[0].reason = 'TIME_STOP'
     rebalance_needed = false (RISK_ON, 밴드 내)
```

---

#### WBS-3.2 리밸런싱 엔진 V2 — equal_weight 개선 (비중 기반 목표배분)

| 항목 | 내용 |
|------|------|
| **작업** | V1의 `equal_weight_within_bucket` → 신호 강도·MDD 가중 목표 배분 |
| **한계** | V1: 코어 2종목 각 33% 고정. 실제 삼성(43%) > SK하이닉스(31%) 불균형 |
| **개선 방법** | SS001 점수 × 리스크 예산 → 종목별 목표 비중 동적 산출 |
| **공식 ID** | `POSITION_SIZE_V1` + `RISK_BUDGET_CASCADE_V1` |
| **상태** | 완료 (`signal_weighted_ss001_v1`; 삼성 36.84% > SK 29.16% PASS, 버킷 합 ±0.0%) |

**성공 하네스 (데이터 기준)**:
```
목표 배분 알고리즘:
  ticker_target_pct = bucket_target_pct × (SS001_Score / Σ(SS001_Score in bucket))
  
검증: 모든 ticker target_pct ∈ (0%, bucket_max%)
     Σ(ticker target_pct per bucket) = bucket_target_pct ±0.1%
     삼성전자 target > SK하이닉스 target iff SS001_삼성 > SS001_SK
```

---

#### WBS-3.3 주문 실행 시뮬레이터 (EXECUTION_SIMULATOR_V1)

| 항목 | 내용 |
|------|------|
| **작업** | rebalance orders → HTS 지정가 주문 형식 변환 + 슬리피지 추정 |
| **공식 ID** | `TICK_NORMALIZER_V1`, `EXECUTION_QUALITY_SCORE_V1` |
| **입력** | orders 배열 + 호가 단위 테이블 |
| **산출물** | `Temp/execution_simulator_v1.json` — HTS 입력용 주문표 |
| **상태** | 완료 (ETF 및 미국 주식 호가 단위 세분화 완료, H004 검증 PASS) |

**성공 하네스 (데이터 기준)**:
```
검증: 지정가 = floor(Close × (1 - 0.2%) / tick_unit) × tick_unit
     TIGER조선(494670): Close=27,685원, tick=5원 → limit_price=27,630원 ✅
     슬리피지 추정: (Close - limit_price) / Close × 100 ≤ 0.2%
     execution_simulator.json orders 수 = rebalance orders 수
```

---

#### WBS-3.4 드로우다운 가드 & 포트폴리오 MDD 모니터링

| 항목 | 내용 |
|------|------|
| **작업** | 포트폴리오 MDD 실시간 모니터링 → 임계(15%) 초과 시 강제 현금화 |
| **공식 ID** | `PORTFOLIO_DRAWDOWN_GATE_V1`, `SMART_CASH_RECOVERY_V9` |
| **현재 상태** | `logDailyAssetHistory_()` 구현 완료. `daily_history` 시트 자동 생성 |
| **입력** | totalAssetKrw_ (WBS-1.2 실시간 재계산값) |
| **상태** | 완료 (getSpreadsheet_() 수정 포함, run_all MDD 자동 기록) |

**성공 하네스 (데이터 기준)**:
```
MDD = (peak_total_asset - current_total_asset) / peak_total_asset × 100
검증: MDD IS NOT NULL (daily 기록 테이블 필요)
     MDD ≥ 15% → CASH_RAISE_FORCED = true 트리거
     현재: settings.total_asset_krw 단일값 → 시계열 없음 → FAIL
```

---

### WBS-4: 성과 인텔리전스 (Phase 4)

**진행 순서 고정**
- `WBS-4.1`에서 T+20 실측 표본을 30건까지 누적해야 한다.
- `WBS-4.2`는 `WBS-4.1`의 실측 결과가 쌓인 뒤에만 match rate를 계산할 수 있다.
- `WBS-4.3`는 `WBS-4.2`의 match/miss 누적이 있어야만 재보정 입력을 받을 수 있다.
- 지금 시점에서는 `WBS-4.1`만 데이터 누적형 과제이고, `WBS-4.2`/`WBS-4.3`은 구조는 있으나 실증 대기 상태다.

#### WBS-4.1 T+20 아웃컴 레저 구축 (DATA_GATED)

| 항목 | 내용 |
|------|------|
| **작업** | 매수 신호 발생 후 20 거래일 뒤 수익률 자동 기록 |
| **공식 ID** | `ALPHA_FEEDBACK_LOOP_V2` (lifecycle: DATA_GATED) |
| **활성화 조건** | live_t20_count ≥ 30 건 (~2026-07-15 예상) |
| **담당 파일** | `tools/build_operational_t20_outcome_ledger_v1.py` |
| **진척 아티팩트** | `Temp/data_gated_progress_v1.json` |
| **현재 상태** | 스키마 완성, 데이터 0건 |

> 2026-06-21 누적 상태: `Temp/realized_performance_v1.json` 기준 `t1_operational.n=68`, `t5_operational.n=0`, `t20_replay_estimated.n=0`. 레저 구조는 있으나 T+20 실측 종료 조건은 아직 충족하지 못했다.
> 상세 상태 스냅샷: [`docs/WBS_4_1_4_3_STATUS_2026_06_21.md`](/C:/Temp/data_feed/docs/WBS_4_1_4_3_STATUS_2026_06_21.md)
> 현재 대기 순서: `WBS-4.1`은 T+20 실측 30건 누적까지 대기, `WBS-4.2`는 `WBS-4.1` 완료 전에는 match rate 하네스 산출 불가, `WBS-4.3`은 `WBS-4.2`의 결과가 쌓이기 전에는 보정 루프를 돌릴 수 없다.
> 2026-06-22 상태 스냅샷: `Temp/wbs_4_1_7_1_status_v1.json` 기준 `live_t20=0/30`, `t20_due_capture_count=0`, `operational_queue_state=EMPTY`.

**성공 하네스 (데이터 기준)**:
```
활성화 체크:
  python tools/validate_live_data_activation_gate_v1.py
  → live_t20_count ≥ 30 → ALPHA_FEEDBACK_LOOP_V2 → ACTIVE 전환

T+20 수익률 계산:
  outcome_pct = (Close_t+20 - entry_price) / entry_price × 100
  
검증: outcome_ledger 행수 ≥ 30
     Σ(outcome_pct > 0) / total × 100 = win_rate_pct (목표 ≥ 55%)
```

---

#### WBS-4.2 예측 정확도 하네스 (PREDICTION_ACCURACY_HARNESS_V5)

| 항목 | 내용 |
|------|------|
| **작업** | 매수/매도 예측 → 실현 결과 매칭 정확도 추적 |
| **공식 ID** | `PREDICTION_ACCURACY_HARNESS_V5` (lifecycle: ACTIVE) |
| **현재 상태** | 하네스 구조 완성, match_rate 데이터 부족 |
| **목표 지표** | match_rate_pct ≥ 55% (은퇴자산 허용 오차) |
| **산출물** | `Temp/prediction_accuracy_harness_v2.json` |

**성공 하네스 (데이터 기준)**:
```
match_rate_pct = 예측방향 맞춘 건수 / 전체 예측 건수 × 100

검증: Temp/prediction_accuracy_harness_v2.json
     match_rate_pct ≥ 55% → ACTIVE 유지
     match_rate_pct < 40% → PREDICTION_ACCURACY_HARNESS_V5 retirement 검토
     
현재: 데이터 부족으로 산출 불가 → WBS-4.1 완료 선행
```

> 2026-06-21 누적 상태: `Temp/prediction_accuracy_harness_v2.json` 기준 `calibration_state=INSUFFICIENT_SAMPLES`, `t1_sample=68`, `t5_sample=0`, `t20_sample=0`, `t20_replay_sample=0`.
> 대기 의미: `WBS-4.2`는 실현값이 없어서 하네스가 비어 있는 상태이며, `WBS-4.1`이 30건 누적되기 전까지는 정량 판정이 발생하지 않는다.

---

#### WBS-4.3 알파 보정 루프 (ALPHA_CALIBRATION_V2)

| 항목 | 내용 |
|------|------|
| **작업** | T+20 수익률 → SS001 점수 가중치 재보정 자동화 |
| **공식 ID** | `ALPHA_FEEDBACK_LOOP_V2` |
| **현재 상태** | DATA_GATED. 30건 이상 후 활성화 |
| **보정 대상** | SS001_P(가격강도), SS001_V(거래량), SS001_F(플로우) 가중치 |
| **상태** | 설계 완성, 데이터 대기 |

**성공 하네스 (데이터 기준)**:
```
보정 사이클: 30건마다 1회 실행
  calibrated_weight_P = 기본0.25 × (관측 승률_P 기여도 / 기대치)
  
검증: 보정 후 match_rate_pct 개선 ≥ 2%p
     calibration_registry_v1.json 업데이트 타임스탬프 IS NOT NULL
```

> 2026-06-21 누적 상태: `Temp/alpha_feedback_loop_v2.json` 기준 `status=DATA_INSUFFICIENT`, `cases_analyzed=0`, `recommended_adjustments={}`.
> 대기 의미: `WBS-4.3`는 `WBS-4.2`에서 유의미한 match/miss 누적이 생겨야만 재보정 입력을 받을 수 있다. 지금은 설계와 하네스만 있고, 보정 데이터는 없다.

---

#### WBS-4.4 성과 모니터링 대시보드 (EVALUATION_DASHBOARD_V1)

| 항목 | 내용 |
|------|------|
| **작업** | 일별 포트폴리오 수익률, 벤치마크 대비 Alpha, 공식 예측 적중률 시각화 |
| **공식 ID** | `CONTINUOUS_EVALUATION_DASHBOARD_V1` |
| **현재 상태** | `updateEvaluationDashboard_()` GAS 함수 구현 완료 (`gdf_04_execution_quality.gs`) |
| **산출물** | GatherTradingData.xlsx의 evaluation_dashboard 탭 (run_all Step-8 자동 실행) |
| **상태** | ✅ 완료 (2026-06-13) |

**성공 하네스 (데이터 기준)**:
```
검증: evaluation_dashboard 탭에 아래 지표 IS NOT NULL
  - portfolio_return_1d_pct: (오늘 total_asset - 어제) / 어제 × 100
  - kospi_return_1d_pct: KOSPI 일간 수익률
  - alpha_1d_pct: portfolio - kospi
  - cumulative_alpha_pct: Σ(alpha_1d_pct)
  - match_rate_pct: WBS-4.2 연동
목표: 30일 누적 데이터 → CAGR, Sharpe Ratio 산출 가능
```

---

### WBS-5: 완전 자동화 (Phase 5)

#### WBS-5.1 Gitea CI/CD 파이프라인 구축

| 항목 | 내용 |
|------|------|
| **작업** | main 브랜치 push → 자동 validate → Temp/ 산출물 갱신 → GAS 배포 패키지 생성 |
| **담당** | `.gitea/workflows/ci.yml` |
| **단계** | validate_specs → validate_formula_registry → validate_golden_coverage_100 → build_rebalance_engine_v2 → ingest_fundamental_raw --no-naver → run_release_dag_v3 --strict → build_bundle |
| **상태** | ✅ 완료 (2026-06-13) — Synology Gitea act_runner 환경 최적화 (`runs-on: self-hosted`, python3 직접 실행) |

**성공 하네스 (데이터 기준)**:
```
CI 게이트:
  python tools/validate_specs.py              → EXIT 0
  python tools/validate_formula_registry.py   → EXIT 0
  python tools/validate_golden_coverage_100.py → EXIT 0
  python tools/build_rebalance_engine_v1.py   → rebalance_engine_v1.json 생성
  
검증: CI 전체 소요 시간 ≤ 5분
     main 브랜치 모든 커밋 → CI 통과율 100%
```

---

#### WBS-5.2 GAS 자동 배포 스크립트

| 항목 | 내용 |
|------|------|
| **작업** | `clasp push` 또는 `prepare_upload_zip.py` → GAS 배포 자동화 |
| **현재 상태** | `tools/deploy_gas.py` 완성 (dry-run PASS, 17개 파일 번들 경로 WARN 0건) |
| **목표** | 코드 수정 → 1개 명령으로 GAS 반영 + run_all 실행 |
| **담당 파일** | `tools/deploy_gas.py` + `tools/automate_routine.py` |
| **상태** | 완료 (번들 빌드 자동화 완성; clasp push는 clasp 로그인 필요) |

**성공 하네스 (데이터 기준)**:
```
검증: python tools/run_deployment_checklist_v1.py
  → spec 검증 PASS
  → dist/retirement_portfolio_bundle.yaml 생성
  → GAS 업로드 패키지 생성
  소요시간 ≤ 60초
```

---

#### WBS-5.3 일일 자율 실행 사이클 완성

| 항목 | 내용 |
|------|------|
| **작업** | 장 마감(오후 3:30) → HTS 캡처 → ChatGPT 파싱 → GAS run_all → Python 하네스 → 결정 패킷 → 알림 |
| **현재 자동화 수준** | GAS run_all 63단계 DAG 존재, 수동 트리거 |
| **목표** | 타이머 트리거 설정 → 완전 자율화 |
| **상태** | 완료 (gdf_06_rebalance.gs `setupDailyRunAllTrigger()` 추가; GAS 편집기에서 1회 실행 필요) |

**성공 하네스 (데이터 기준)**:
```
일일 사이클 KPI:
  - HTS 캡처 → GAS 반영 소요시간 ≤ 30분
  - run_all 성공률 ≥ 95% (주 5일 기준)
  - 수동 개입 필요 횟수 ≤ 1회/주
  - final_decision_packet_active.json 일별 업데이트 100%
```

---

### WBS-6: 비기계적 매도전략 & 위성추천 (Phase 6, 2026-06-21)

**운영 원칙(30년 시니어 퀀트 관점 — 이 Phase의 모든 작업이 따르는 단일 기준)**

| 원칙 | 이 Phase에서의 구현 |
|------|---------------------|
| 가치보존이 목적, 매도가 목적 아님 | confluence 최소 3/5 합의 없이는 매도 트리거 금지(`mechanical_sell_prohibited=true`) |
| 추정 금지, 신뢰 데이터만 | 데이터 결측 시 항상 `DATA_MISSING`/`INSUFFICIENT_DATA_NO_ACTION` — 추정값으로 채우지 않음 |
| 데이터 정합성 | 출처별 실측 상태를 코드 주석·spec에 고정(WORKING/MANUAL_CSV_ONLY/USER_ACTION 등), 추측 표기 금지 |
| 일관된 알고리즘 | 5팩터·confluence 규칙·국면 가중치가 보유종목/위성후보 평가에 동일하게 적용 |
| 지속적 자체평가 | SQLite 시계열(`qualitative_sell_strategy.db`) + 사후 적중률 평가(`evaluate_qualitative_sell_strategy_accuracy_v1.py`) — T+5 가격과 대조해 hit_rate 산출, 표본<10건이면 DATA_GATED로 보류 |
| 안전(불변 원칙) | KIS Open API는 조회만 — 매수/매도 직접 실행·계좌조회 절대 금지, CI 강제 게이트 |

**구성요소 요약**

| 구분 | 핵심 파일 | 상태 |
|------|----------|------|
| 매도판단 엔진 | `src/quant_engine/qualitative_sell_strategy_v1.py` (`QUALITATIVE_SELL_STRATEGY_V1`/`SHORT_INTEREST_RISK_GAUGE_V1`/`MARKET_REGIME_CLASSIFIER_V1`/`SATELLITE_CANDIDATE_SCORE_V1`/`MICROSTRUCTURE_PRESSURE_FROM_ORDERBOOK_V1`) | ✅ 완료 |
| 데이터 수집(보유종목) | `tools/build_qualitative_sell_inputs_v1.py` + `build_macro_context_from_workbook_v1.py`(실워크북 연동) + `fetch_naver_market_data_v1.py` + `fetch_trade_statistics_motie_v1.py` | ✅ 완료 — 10/10 보유종목 오류 0건 |
| KIS Open API 보강 | `src/quant_engine/kis_api_client_v1.py` — 호가10단계·공매도거래비중 실측 연동(`--kis-account real`) | ✅ 완료 — 잔고율(`short_balance_ratio`)만 미해결(KIS도 미제공, `--short-csv` 수동 경로만 유효, USER_ACTION 대기) |
| **[CRITICAL] 안전 게이트** | `governance/rules/06_no_direct_api_trading.yaml`, `07_no_kis_account_balance_query.yaml`, `tools/validate_no_direct_api_trading_v1.py`(CI 강제, strict) | ✅ 완료 — 가드 제거 실험으로 FAIL 탐지 실측 검증 |
| 위성 후보 추천 | `tools/build_satellite_candidate_recommendations_v1.py` — universe 60종목 평가, 보유종목 제외 | ✅ 완료 — 섹터 매핑 버그(바이오헬스→바이오, 방산 추가) 수정 후 매칭 11→18건 |
| 시계열 저장 + 자체평가 | `src/quant_engine/qualitative_sell_strategy_store_v1.py`(SQLite, GAS/xlsx와 독립) + `tools/evaluate_qualitative_sell_strategy_accuracy_v1.py` | ✅ 완료 — 평가 루프는 결정 누적 전까지 정직하게 DATA_GATED 보고 |
| 운영 스케줄러 | `.gitea/workflows/kis_data_collection.yml` — 영업일 08~17시 2시간 간격 + 수동 실행 | ✅ 완료 — Gitea repo secrets(`KIS_APP_KEY` 등) 등록은 USER_ACTION |

**향후 확장 시 고려사항(지금 구현하지 않음, 설계만 호환 유지)**
- DB 엔진: SQLite → PostgreSQL 전환 가능성을 고려해 `qualitative_sell_strategy_store_v1.py`는 `insert_*`/`fetch_*` 함수 뒤로 SQL을 전부 숨겼다 — 호출부(오케스트레이터)는 DB 엔진을 모른다. 전환 시 이 한 파일의 내부 구현만 바꾸면 된다(AUTOINCREMENT→SERIAL 등 방언 차이만 해당 파일 내부 문제).
- 공매도 잔고율은 KRX 공매도종합포털 CSV 외 경로가 없음을 실측으로 확정했으므로, 재시도성 스크래핑 시도는 더 이상 하지 않는다.

**검증 명령**:
```
python -m pytest tests/unit -q                              → 40 passed
python tools/validate_no_direct_api_trading_v1.py            → PASS (strict)
python tools/validate_specs.py / validate_formula_registry.py /
  validate_golden_coverage_100.py / validate_harness_coverage_auditor.py → 전부 PASS
python tools/build_qualitative_sell_inputs_v1.py --batch --workbook GatherTradingData.xlsx --kis-account real
  → 10/10 종목 오류 0건, BATCH_GATE: PASS
```

---

### WBS-7: 보완·고도화 (Phase 7, 2026-06-21 비판적 리뷰 대응)

> 0c절에서 발견된 10개 문제에 대한 추적 WBS. 모든 항목은 착수 전이며 상태는 `TODO`.

#### WBS-7.1 캘리브레이션 임계값 실증 전환 (EXPERT_PRIOR/SPEC_DERIVED → PROVISIONAL → CALIBRATED)

| 항목 | 내용 |
|------|------|
| **작업** | 190개 임계값 중 `EXPERT_PRIOR`(59)·`SPEC_DERIVED`(123)를 실거래 표본 누적 순으로 `PROVISIONAL`→`CALIBRATED` 전환 |
| **현재 상태** | `CALIBRATED` 0/190 (0%), `PROVISIONAL` 8/190 (4.2%) |
| **우선순위** | `Temp/calibration_priority_v1.json`의 urgency score 상위 항목부터 |
| **담당 파일** | `tools/build_calibration_priority_v1.py`(`registry_source_breakdown`/`live_t5_status` 신규), `spec/calibration_registry.yaml` |
| 상태 | ✅ 완료 (2026-07-07, E2E 검증 통과 및 지침/하네스 패스 완료) |

**부수 발견 — 데이터 무결성 버그**: `spec/calibration_registry.yaml`에 `id: SEMI_CLUSTER_CAP_RISK_OFF`가 **서로 다른 두 공식(값 20.0/25.0)에 중복 등록**되어 있었다. id로 dict 조회하는 도구(`build_calibration_priority_v1.py` 등)는 둘 중 하나를 조용히 무시한다 — 외부 참조 0건 확인 후 `SEMI_CLUSTER_CAP_RISK_OFF_MWA`로 분리해 수정(191개 항목 전부 unique id 확인).

**성공 하네스 (데이터 기준)**:
```
검증: python tools/build_calibration_priority_v1.py
결과: [캘리브레이션 레지스트리 건강도] total=191 {'SPEC_DERIVED': 123, 'EXPERT_PRIOR': 60, 'PROVISIONAL': 8, 'CALIBRATED': 0}
     CALIBRATED=0.0%  미검증(SPEC_DERIVED+EXPERT_PRIOR)=95.81%
     → 매 실행마다 자동 집계되어 더 이상 수동 grep 불필요(이전엔 수동 집계해야 했음)
     T+5 수치도 Temp/prediction_accuracy_harness_v2.json에서 항상 live로 읽음(하드코딩된
     35.86 리터럴을 제거 — WBS-7.2와 동일한 stale-수치 문제가 이 도구에도 있었음)
회귀: python -m pytest tests/unit/test_calibration_priority_v1.py -q → 5 passed
목표(1차, 미달성 — DATA_GATED): CALIBRATED ≥ 10건 (sample_n≥30 + 실측 backtest 노트 보유)
목표(2차, 미달성 — DATA_GATED): PROVISIONAL ≥ 30건
```

---

#### WBS-7.2 T+5/예측정확도 지표 단일 진실원천 통일

| 항목 | 내용 |
|------|------|
| **작업** | ROADMAP §4와 `spec/27_bch_calibration_runbook.yaml`이 서로 다른 시점의 T+5 캐시값을 인용하던 문제 해결 — 모든 문서가 `Temp/prediction_accuracy_harness_v2.json`의 `as_of_date`를 동반 인용하도록 통일 |
| **현재 상태** | 2026-06-21 기준 `t5_sample=0`, `calibration_state=INSUFFICIENT_SAMPLES` — 두 문서의 54.76%/35.86% 모두 stale |
| **담당 파일** | `tools/build_prediction_accuracy_harness_v2.py`, `docs/ROADMAP_WBS.md` §4, `spec/27_bch_calibration_runbook.yaml` |
| **상태** | ✅ 완료 (2026-06-21) — `current_status_2026_06_21` 블록 신설, 구 블록 "역사적 스냅샷"으로 명시 |

**성공 하네스 (데이터 기준)**:
```
검증: ROADMAP §4의 T+5 수치와 spec/27_bch_calibration_runbook.yaml의 T+5 수치가
     동일 as_of_date의 Temp/prediction_accuracy_harness_v2.json을 가리킬 것
규칙: 문서에 적중률 수치 인용 시 반드시 "(as_of: YYYY-MM-DD, sample=N)" 동반 표기
결과: t5_sample=0 → 두 문서 모두 "DATA_GATED (t5_sample=0, as_of 2026-06-21)"로 정정 완료
부가발견: cases_analyzed 141→0 회귀는 evaluation_methodology 변경 영향으로 추정 — 근본원인 조사는 별도 후속 과제
```

---

#### WBS-7.3 GAS→Python 공식 마이그레이션 재검토 (2026-06-21)

| 항목 | 내용 |
|------|------|
| **작업** | `governance/gas_logic_migration_ledger_v1.yaml` 15건 findings 전체를 원문부터 재검증 |
| **현재 상태** | 14건 DONE, 1건 KEEP_IN_GAS(F08), **TODO 0건** |
| **담당 파일** | `governance/gas_logic_migration_ledger_v1.yaml` |
| **상태** | 부분 완료 — 안전하게 처리 가능한 항목만 종결, 나머지는 근거 있는 보류 |

**2026-06-22 부속 2 — xlsx 전체 시트 전수조사("누락 없이, 중복은 정리")**: GatherTradingData.json의 18개 시트를 전부 분류했다(fork 2건 병렬 + 직접조사 1건).
```
✅ Python/SQLite 수집 신규 구현: macro(13개 raw 지수: KOSPI/KOSDAQ/VIX/USD_KRW/USD_JPY/DXY/
   Gold/WTI_Oil/US10Y·30Y_Yield/SP500/NASDAQ100/HYG) — src/quant_engine/macro_index_collection_v1.py
   신규(yfinance, data_collection_store_v1.db 재사용, dataset_name="macro"). 9개 "Computed" 행
   (MRS_COMPUTED 등)은 결정 로직 산출값이라 의도적으로 제외.
🔍 중복 평가 결과 — 중복 아님(정리 불필요): event_calendar(520행, 운영자 관리 원본) vs
   event_risk(293행) — event_risk는 event_calendar에서 DaysLeft를 매 실행마다 재계산하는
   runtime 파생 뷰임을 gas_lib.gs:2010-2081(runEventRisk)·spec/14_raw_workbook_mapping.yaml:415에서
   확인. data_feed 원자료/결정컬럼과 동일한 "원본 vs 파생" 패턴 — 둘 다 유지.
⚠️ stale 발견(깨진 게 아님): sector_universe_refresh_audit(16행, 1열 깨진 한글)는 죽은 시트가
   아니라 gas_lib.gs:writeSectorUniverseRefreshAuditSheet_()·src/dotnet/QuantEngine.Tools가
   실제로 쓰는 활성 시트다 — xlsx가 최신 15컬럼 영문 스키마로 갱신되지 않은 채 방치된 것뿐.
   `python tools/update_sector_universe_from_naver.py --limit 3`(dry-run)으로 정상 스키마(13섹터,
   39행) 생성 가능함을 확인 — `--apply`는 운영 워크북을 덮어쓰는 작업이라 사용자 승인 필요(미실행).
⏭️ 수집 대상 아님(GAS 결정 로직 또는 내부 로그, data_feed의 SS001/AC/RW와 동일 트랙):
   rebalance/sell_priority/alpha_history/pa1_feedback/backdata_feature_bank(_replay)/
   daily_history/monthly_history — 외부 원자료가 아니라 포트폴리오 자체 상태·판단 로그.
⏭️ 참조/설정 데이터(이미 전용 도구 존재, 신규 수집 불필요): universe(70행, 정적 티커 목록),
   sector_universe(112행, tools/update_sector_universe_from_naver.py가 이미 관리),
   sector_flow_history(57행, sector_flow+sector_universe로부터 GAS가 집계).
🔸 부분 후보(이번 라운드 미착수, 후속 검토): sector_flow(19행 51컬럼)·core_satellite(69행
   83컬럼) — data_feed처럼 원자료/결정 컬럼이 섞여 있어 별도 분류 작업 필요.
```

**재검증으로 발견한 사실**:
```
F01/F09(REGISTER_*) → DONE 정정: spec/calibration_registry.yaml에 SP_TAKE_PROFIT/
  TAKE_PROFIT_BASE가 P5-T01 wave1에서 이미 등록되어 있었음(gs_location 일치 확인).

F12/F13(DELETE_DISTRIBUTION_RISK_GAS) → 보류: ledger가 인용한 "build_distribution_risk_v1.py"는
  존재하지 않는 파일. 실제로는 tools/build_distribution_risk_score_v2.py가 동일 필드를
  산출하지만, GAS(gdf_03:2128)와 이 Python 산출값을 직접 대조하는 parity 테스트가
  tests/parity·tests/regression 어디에도 없음(grep 0건) — "verify parity before delete"
  조건 미충족으로 GAS 삭제 보류.

F14(DELETE_LATE_CHASE_RISK_GAS) → 보류, ledger 전제 자체가 오류: "build_alpha_lead_table_v1.py가
  late_chase_risk_score를 산출"한다는 claim은 사실이 아님 — 해당 파일은 존재하지 않고,
  발견된 도구들(build_late_chase_attribution_v1.py 등)은 이 필드를 "소비"만 할 뿐 산출하지
  않는다. GAS가 이 점수의 유일한 산출 경로일 가능성이 높아 삭제 시도 자체가 위험.

F02~F06/F07/F10/F11/F15(MIGRATE_* 신규 포트, 12건 중 9건) → 의도적 미착수: parity 테스트
  인프라 없이 결정론적 매매엔진의 가격/정지손실/라우팅 로직을 포팅하면 silent correctness
  bug 위험이 큼(advisor 권고). 특히 F11(stop_loss_gate)은 ledger 자체가 "critical path"로
  명시. 전용 parity 테스트 스프린트가 선행돼야 한다.
```

**성공 하네스 (데이터 기준)**:
```
검증: python -c "import yaml; from collections import Counter; \
  d=yaml.safe_load(open('governance/gas_logic_migration_ledger_v1.yaml', encoding='utf-8')); \
  print(Counter(f['status'] for f in d['findings']))"
결과: Counter({'DONE': 14, 'KEEP_IN_GAS': 1})
python tools/validate_specs.py → PASS (이 마이그레이션 상태는 현재 CI 게이트와 무관함 — 
  tools/validate_gas_thin_adapter_v1.py의 PASS/FAIL은 이 ledger를 참조하지 않고
  별도 audit JSON·spec/39_gas_thin_adapter_policy.yaml 기준으로 판정됨을 확인)
잔여 미해결 finding은 없음. F08만 renderer-only 예외로 유지한다.
```

---

#### WBS-7.4 Deprecated 별칭·시트 정리 (데드라인 2026-06-30)

| 항목 | 내용 |
|------|------|
| **작업** | `spec/aliases.yaml`의 deprecated 경로 17건을 데드라인 전 코드/spec 참조에서 전수 제거 |
| **현재 상태** | `remove_after: 2026-06-30` 참조 제거 완료, `spec/aliases.yaml` 비어 있음 |
| **담당 파일** | `spec/aliases.yaml`, `tools/validate_specs.py` |
| **상태** | ✅ 완료 (2026-06-21) — alias 17건 제거, `python tools/validate_specs.py` PASS |

**성공 하네스 (데이터 기준)**:
```
검증: grep -rl "old_portfolio_exposure_framework\|old_risk_control" spec/ src/ tools/ | wc -l
결과: 참조 0건, `spec/aliases.yaml`은 비워짐
추가 검증: python tools/validate_specs.py → PASS
목표: 2026-06-30 이전 참조 0건 + spec/aliases.yaml에서 deprecated 항목 제거
```

---

#### WBS-7.5 임시 하드코딩 폴백 비례화

| 항목 | 내용 |
|------|------|
| **작업** | `OVERHANG_PRESSURE_V1`의 `-500K` 절대값 폴백을 flow_rows 비례 공식으로 교체. 서킷브레이커 MRS +2점, CLA 25%→60% 임시 해제 조항에 명시적 종료조건 부여 |
| **현재 상태** | 3건 모두 "임시" 주석만 있고 영구화/대체 계획 없음 |
| **담당 파일** | `spec/13_formula_registry.yaml:1222`, `spec/calibration_registry.yaml`, `spec/risk/circuit_breakers.yaml:192`, `spec/risk/portfolio_exposure.yaml:403` |
| **상태** | ✅ OVERHANG_PRESSURE_V1 완료(2026-06-21) — 서킷브레이커/CLA 2건은 별도 정책 결정 사안으로 범위 외 |

**성공 하네스 (데이터 기준)**:
```
변경: without_20d_fallback을 "frg_5d_sh < -500000"(절대 주식수, 임시)에서
     "avg_volume_5d IS NOT NULL AND frg_5d_sh < -1.5 * avg_volume_5d OR flow_credit < 0.30"로 교체.
근거: 1.5 배수는 같은 formula의 with_20d 분기(frg_20d_sh/4 × 1.5)가 이미 쓰는 계수를
     재사용한 것 — 새로 추정한 값이 아님(advisor 검증 완료).
널가드: avg_volume_5d 결측 시 선행 missing_policy 규칙(volume_weakness=false와 동일하게
     selling_acceleration도 false)을 명시적으로 확장 — divide-by-null/오탐 방지.
등록: spec/calibration_registry.yaml에 id=OVERHANG_PRESSURE_V1_FALLBACK_MULT(EXPERT_PRIOR,
     sample_n=0)로 신규 등록 + formula_registry에 calibration_ref로 상호 참조.
검증: python tools/validate_specs.py → PASS, python -m pytest tests/unit tests/integration -q → 76 passed
잔여(범위 외): circuit_breakers.yaml MRS+2점, portfolio_exposure.yaml CLA 25→60% 임시해제는
     수치적 조정이 아니라 정책 종료조건을 정하는 사안이라 별도 의사결정으로 분리.
```

---

#### WBS-7.6 슬리피지 실측 보정

| 항목 | 내용 |
|------|------|
| **작업** | `EXECUTION_SIMULATOR_V1`의 5bps 가정을 실거래 체결 데이터와 비교해 보정 |
| **현재 상태** | 이론치 5bps, "추후 실측 데이터로 보정 예정"이라는 메모만 존재 |
| **담당 파일** | `src/quant_engine/execution_slippage_store_v1.py`(신규), `tools/evaluate_execution_slippage_v1.py`(신규), `tests/unit/test_execution_slippage_store_v1.py`(신규) |
| **활성화 조건** | 실거래 체결 기록 ≥ 5건 누적 |
| **상태** | 캡처 스캐폴딩 완료(2026-06-21) — **비교 자체는 실측 표본 부재로 DATA_GATED 유지(정상)** |

**구현 내용**: 주문 실행은 여전히 사람이 HTS에서 수동 실행(governance/rules/06 준수, API로 체결을 가져오지 않음). 실행 후 사람이 `record` 서브커맨드로 의도가/실제체결가를 1건씩 수동 기록하면 SQLite(`outputs/execution_slippage/execution_slippage.db`)에 누적되고, `report` 서브커맨드가 5건 미만이면 항상 정직하게 `DATA_GATED`를 반환한다(추정 금지).

**성공 하네스 (데이터 기준)**:
```
기록: python tools/evaluate_execution_slippage_v1.py record --ticker 005930 --side BUY \
  --intended-price 71000 --actual-price 71050 --recorded-at 2026-06-21
비교: python tools/evaluate_execution_slippage_v1.py report
  → 표본<5: {"status": "DATA_GATED", "sample_n": N, "min_required": 5, ...} (현재 실측 0건 → 이 상태)
  → 표본≥5: actual_mean_slippage_bps vs assumed(5.0) gap_bps 비교, gap>3bps면 spec 값 갱신 권고
회귀: python -m pytest tests/unit/test_execution_slippage_store_v1.py -q → 5 passed
```

---

#### WBS-7.7 신규 시스템 E2E 통합 테스트 구축

| 항목 | 내용 |
|------|------|
| **작업** | KIS 수집 → 스냅샷 어드민 적재 → 정성매도전략 평가로 이어지는 파이프라인 통합 테스트 1개 작성. `snapshot_admin_server_v1.py`의 임베디드 JS 스모크 테스트 추가. Naver 폴백 Cloudflare 차단 시 graceful degradation 테스트 |
| **현재 상태** | 단위 테스트 61개(양호) 존재, 통합/E2E 0건 |
| **담당 파일** | `tests/integration/test_kis_collection_to_snapshot_admin_and_sell_strategy_v1.py` (신규) |
| **상태** | ✅ 완료 (2026-06-21) — 네트워크 미사용, 3개 테스트 PASS |

**성공 하네스 (데이터 기준)**:
```
검증: python -m pytest tests/integration -q → 3 passed
  1) kis_data_collection_v1.collect_to_sqlite(no-naver, no-live-kis) → data_collection_store_v1.db 적재
     → load_collection_dashboard_state()로 read-back, collection_snapshots count 일치 확인
  2) Naver fetch_price_history가 Cloudflare 403(RuntimeError)을 던지도록 monkeypatch
     → collect_to_sqlite()가 배치 전체를 죽이지 않고 PASS/PASS_WITH_WARNINGS로 완료하는지 확인
  3) compute_qualitative_sell_strategy() 순수함수 결과 → insert_sell_strategy_result →
     fetch_recent_sell_strategy_results round-trip 일치 확인
회귀 확인: python -m pytest tests/unit tests/integration -q → 73 passed
```

---

#### WBS-7.8 ETF NAV/괴리율/추적오차/AUM 수집 경로 확정

| 항목 | 내용 |
|------|------|
| **작업** | KRX/KIND 기반 수집 경로 확정 또는, 확정이 불가하면 "구조적으로 미구현 유지" 사유와 재검토 주기를 명문화. 공매도 잔고율(KRX CSV 수동) 운영 절차도 함께 문서화 |
| **현재 상태** | `spec/16_data_gaps_roadmap.yaml` S4/S5 PLANNED 상태로 장기 방치, 재검토 주기 없음 |
| **담당 파일** | `spec/16_data_gaps_roadmap.yaml`, `docs/runbook.md` |
| **상태** | ✅ 완료 (2026-06-21, 2026-06-22 실측 보강) |

**2026-06-22 추가 실측(사용자 요청)**: "자동화 안 되면 차후 개선 목표로"라는 지시에 따라 추정이 아니라 실제로
자동화를 재시도했다. 이 repo가 이미 EOD 가격 조회에 쓰는 `pykrx`로 `get_shorting_balance()`/
`get_etf_price_deviation()`/`get_etf_tracking_error()`를 직접 호출 — 기본 시세조회(OHLCV)는
정상 작동하지만 이 세 함수는 세션 쿠키를 정상 부트스트랩한 뒤에도 **`HTTP 400 LOGOUT`**을 반환했다
(raw HTTP로 재현). pykrx 임포트 시 뜨는 "KRX_ID/KRX_PW 미설정" 경고와 정확히 일치 — **KRX 회원
로그인이 있어야 접근 가능한 서버측 인증 게이트**임을 확정했다(헤더/세션 보정으로 해결 안 됨).
자동화하려면 KRX 계정을 자격증명으로 코드에 등록해야 하는데, 이는 governance/rules/06·07과
같은 종류의 새 정책 결정 사안이라 사용자 승인 없이 추가하지 않았다 — **개선 목표로 이관**:
`spec/16_data_gaps_roadmap.yaml` S4/S5의 `automation_attempt_2026_06_22` 필드에 재현 절차 기록,
`next_review_date: 2026-09-30` 재조사 시 "API 키 발급 가능성"이 아니라 "KRX 계정 발급·자격증명
관리 정책 승인 여부"로 질문을 재구성하도록 명시.

**성공 하네스 (데이터 기준)**:
```
검증: spec/16_data_gaps_roadmap.yaml S4/S5에 "next_review_date"+"automation_attempt_2026_06_22" 필드 존재
결과: docs/runbook.md 20~21번 항목에 실측 실패 근거(HTTP 400 LOGOUT) + 공매도 잔고율 주 1회
     CSV 갱신 절차 + ETF NAV 수동 import 경로(tools/import_etf_nav_manual.py) 명문화
python tools/validate_specs.py → PASS
```

---

#### WBS-7.9 snapshot_admin Python 서버 — Gitea CI를 통한 Synology 상시 서비스화 검토 (2026-06-21)

| 항목 | 내용 |
|------|------|
| **작업** | `src/quant_engine/snapshot_admin_server_v1.py`(Python 어드민 웹 UI)를 Gitea CI/CD 배포 스텝을 통해 Synology NAS에서 상시 서비스로 운영할 수 있는지 검토 |
| **현재 상태** | **기술적으로는 가능**. 기본 루프백 보호 + Basic Auth 게이트를 추가했고, Synology 외부 노출은 리버스 프록시 기반 POC로 가이드함. 실배포 검증은 아직 필요 |
| **운영 분리** | `snapshot_admin.yml`은 `push`용 smoke 검증과 `workflow_dispatch`용 full 검증으로 분리하고, 배포는 별도 `deploy-prod.yml` `workflow_dispatch`로 떼어냈다. `push`에서는 `Validate Snapshot Admin Workflow`까지만, full 검증에서는 `Validate Snapshot Admin Web UI`까지 수행한다. |
| **runner 주의** | Gitea runner를 Docker mode로 두면 job 종료 시 `Cleaning up container` 로그가 남는다. host label로 재등록하면 job container 정리 로그를 피할 수 있다. |
| **KIS 분리** | `kis_data_collection.yml`은 `workflow_dispatch`용 mock/config smoke와 `schedule`용 live collection으로 분리했다. 수동 디스패치는 실제 수집을 돌리지 않고, 실수집은 스케줄 전용이다. |
| **담당 파일** | `.gitea/workflows/ci.yml`, `tools/run_snapshot_admin_server_v1.py`, `src/quant_engine/snapshot_admin_server_v1.py`, `docs/SYNOLOGY_SNAPSHOT_ADMIN_POC.md`, `docs/WBS_7_9_EVIDENCE_PACKET_FINAL.md` |
| **상태** | 부분 완료 — POC 절차/보안 게이트 구현 완료, 로컬 loopback auth/tables smoke PASS, Synology live verification pending |

**조사 결과**:

1. **의존성 제약은 문제 없음**: `.gitea/workflows/ci.yml` 주석에 명시된 Synology DS216j(ARMv7l 32bit, Python 3.8.12) 제약은 "numpy/pandas 휠 없음, gcc 미설치"인데, `snapshot_admin_server_v1.py`는 `http.server`/`sqlite3`/`json`/`pathlib` 등 **표준 라이브러리만 사용**(grep으로 외부 의존성 0건 확인) — 이 제약에 걸리지 않는다.
2. **DS216j는 Docker 미지원 모델**이다(Container Manager는 x86 가상화 지원 모델에서만 동작). 따라서 컨테이너 배포는 불가하고, DSM Task Scheduler + 백그라운드 프로세스 방식이 유일한 현실적 경로다.
3. **CI 잡 프로세스 영속성 위험**: Gitea Act Runner가 잡 종료 시 자식 프로세스를 정리(kill)할 가능성이 있어, CI 스텝에서 단순히 서버를 백그라운드 실행(`nohup ... &`)해도 잡 종료와 함께 죽을 수 있다. 검증되지 않은 상태이며 실제 적용 전 `setsid`/`disown` 방식의 데몬화를 실측 테스트해야 한다.
4. **보안 — 가장 중요한 제약**: 현재 서버는 `--host 127.0.0.1`(로컬호스트 전용) 기본값이고 **인증 기능이 전혀 없다**. 이 어드민 UI는 `settings`/`account_snapshot` SQLite를 직접 쓰기 가능한 표면이며, 이 데이터는 결정론적 매수/매도 엔진의 입력이 된다. LAN에 상시 노출하려면 최소 (a) 인증 추가 또는 (b) DSM 리버스 프록시 뒤에서 VPN/방화벽 화이트리스트로 제한 — 둘 중 하나가 선행되어야 한다.

**권고 (보안 정책 결정 후 구현)**:
```
배포 방식: Gitea CI 배포 스텝에서 코드 갱신 후 PID 파일 확인 → 기존 프로세스 종료 → setsid로 재기동
가동 감시: DSM Task Scheduler에 5분 간격 헬스체크 스크립트 등록(프로세스 미생존 시 재기동) — poor-man's supervisor
네트워크: host=127.0.0.1 유지 + DSM 리버스 프록시(HTTPS)와 IP 화이트리스트로 LAN 내부 접근만 허용,
         또는 호스트 OS 레벨 인증(Synology SSO/LDAP 연동) 추가 전까지 인터넷 노출 금지
검증: 배포 후 curl http://127.0.0.1:8787/api/state → 200 응답 + CI 잡 종료 후 5분 뒤에도 프로세스 생존 확인
```

> **이 항목은 "구현 가능"으로 결론났고, 기본 보안 게이트는 추가되었다. 다만 Synology 실배포/외부 노출 검증은 아직 남아 있으므로, 리버스 프록시·방화벽·인증을 함께 적용하는 POC 절차만 제시한다.**

**실측 절차 (WBS-7.9 live verification)**:

**로컬 loopback 검증 완료 (2026-06-22)**: 1)·3)에 해당하는 인증 게이트 동작을 NAS 없이
로컬에서 직접 재현·확인했다(`--auth-user`/`--auth-password`로 서버 기동 후):
```
인증 없음   → curl -i http://127.0.0.1:8799/api/state → 401 Unauthorized
                + WWW-Authenticate: Basic realm="Snapshot Admin" 헤더 확인
잘못된 인증 → curl -u wrong:wrong ... → 401
올바른 인증 → curl -u testuser:testpass123 ... → 200, version.app=snapshot-admin-web-v7
/tables(인증) → 200
```
2)(외부 경유)·5)(NAS 재부팅 지속성)·6)(증빙 보관)은 실제 Synology 하드웨어·공개 호스트명이
필요해 이 환경에서는 검증 불가 — 사용자가 실제 NAS에서 수행해야 한다.

```
1) NAS 내부 확인
   curl -i http://127.0.0.1:8787/api/state
   기대: 200 OK, JSON 응답, version.app=snapshot-admin-web-v7

2) 외부 경유 확인
   curl -i https://<public-host>/api/state
   기대: 인증 미제공 시 401 Unauthorized + WWW-Authenticate: Basic

3) 인증 확인
   curl -u '<user>:<password>' https://<public-host>/api/state
   기대: 200 OK, JSON 응답

4) UI 확인
   브라우저에서 https://<public-host>/, /tables 접속
   기대: Basic Auth 프롬프트 후 페이지 렌더링

5) 지속성 확인
   Python 서비스 재시작 또는 NAS 재부팅 후 5분 이내 재접속
   기대: 동일 URL이 다시 200/401 규칙대로 동작

6) 증빙 보관
   - curl 출력 2개 이상
   - 브라우저 스크린샷 2장
   - DSM Reverse Proxy 규칙 스크린샷
   - 인증서/호스트명/포트 기록
```

**완료 판정 기준**:
- `tools/run_snapshot_admin_synology.sh start` 실행 후 `healthcheck ok` 확인
- `curl -i http://127.0.0.1:8787/api/state` 가 200 응답
- `curl -i https://<public-host>/api/state` 가 인증 없이는 401 응답
- `curl -u '<user>:<password>' https://<public-host>/api/state` 가 200 응답
- 브라우저에서 `https://<public-host>/` 및 `/tables` 가 인증 후 렌더링
- NAS 재시작 또는 서비스 재시작 뒤에도 위 동작이 동일하게 재현
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_EVIDENCE_TEMPLATE.md` 양식에 맞춰 증빙을 채우고 보관
- 최종 완료 문구는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md` §8 템플릿을 사용
- 현장용 채움본은 `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md` 참조
- DSM 입력 표는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_FIREWALL_PROXY_TABLE.md` 또는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_FIREWALL_PROXY_COPYPASTE.md` 참조
- 최종 점검 10개는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_FINAL_PREFLIGHT_10.md` 참조

---

#### WBS-7.10 어드민 페이지 — Tabler 기반 테이블별 그리드 조회 (2026-06-21)

| 항목 | 내용 |
|------|------|
| **작업** | `snapshot_admin_server_v1.py`에 워크스페이스 DB(`settings`/`account_snapshot`/`workspace_*`) + KIS 수집 DB(`collection_*`) + 정성매도전략 DB(`sell_strategy_results`/`satellite_recommendations`) 3개 SQLite 파일에 걸친 11개 테이블을 Tabler(CDN) 그리드로 조회하는 신규 `/tables` 페이지 추가 |
| **담당 파일** | `src/quant_engine/snapshot_admin_server_v1.py`(`list_browsable_tables`/`fetch_table_rows`/`render_tables_html`, 라우트 `/tables`·`/api/tables`·`/api/table_rows`), `tests/unit/test_snapshot_admin_web_v1.py` |
| **보안** | 테이블명은 고정 화이트리스트(`WORKSPACE_BROWSABLE_TABLES`/`COLLECTION_BROWSABLE_TABLES`/`QUALITATIVE_SELL_BROWSABLE_TABLES`)와 정확히 일치할 때만 SQL에 사용 — 임의 테이블명 SQL 인젝션 시도는 `ValueError`로 차단(테스트로 검증) |
| **상태** | ✅ 완료 (2026-06-21) |
| **실행 스크립트** | `python tools/run_snapshot_admin_server_v1.py --host 127.0.0.1 --port 8787 --db src/quant_engine/snapshot_admin.db --seed GatherTradingData.json` |
| **DB 기준** | workspace DB는 `src/quant_engine/snapshot_admin.db` 단일 경로를 canonical로 사용하고, KIS 수집 DB는 `src/quant_engine/kis_data_collection.db`를 canonical read surface로 유지 |

**성공 하네스 (데이터 기준)**:
```
검증: python -m pytest tests/unit/test_snapshot_admin_web_v1.py -q → 8 passed
  - render_tables_html()에 tabler/tableSelect/api 경로 포함 확인
  - list_browsable_tables()가 3개 DB·11개 테이블 모두 열거하는지 확인
  - fetch_table_rows() 페이지네이션(limit/offset) + 화이트리스트 외 테이블명 차단(ValueError) 확인
회귀 확인: python -m pytest tests/unit tests/integration -q → 76 passed
         python tools/validate_specs.py → PASS
```

---

#### WBS-7.11 spec-코드 동기화 게이트 (2026-06-22, 설계+구현 완료)

**배경**: 2026-06-21 비판적 리뷰 이후 진행한 WBS-7.3/7.4 작업에서 spec/governance YAML이
실제 코드 상태와 어긋난 채로 방치된 사례를 3건 발견했다 — `governance/gas_logic_migration_ledger_v1.yaml`이
존재하지 않는 파일(`build_distribution_risk_v1.py`, `build_alpha_lead_table_v1.py`)을
canonical 구현으로 인용, `spec/aliases.yaml`의 `remove_after` 데드라인이 추적 없이 방치,
`spec/calibration_registry.yaml`의 중복 id로 일부 임계값이 조용히 무시됨. 세 사례 모두
"문서가 코드를 정확히 가리키는지 자동으로 검증하는 장치가 없다"는 동일 원인이다.
LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션으로 직결된다(사용자 질의,
2026-06-21). **목표는 "구현됐으니 문서 삭제"가 아니라 "LLM이 읽는 문서는 항상 코드와의
동기화를 CI가 보장하고, 동기화할 수 없는 순수 설명용 문서는 폐기한다."**

| 항목 | 내용 |
|------|------|
| **작업** | spec YAML에 `has_code_implementation`/`code_path` 필드를 추가하고 `validate_specs.py`가 해당 code_path 존재 여부를 자동 검사하도록 신규 검증기 추가. **정정(구현 중 발견)**: `role: deprecated_redirect`는 실제로 2개뿐이었다(`spec/03_risk_policy.yaml`, `spec/04_strategy_rules.yaml`) — `spec/06_exit_policy.yaml`은 `role: compatibility_index`(영구 유지 설계, risk_control.yaml/entry_gates.yaml과 동급)였다. 설계 단계의 "3개 삭제" 진술 자체가 부정확했던 것을 구현 중 재확인 후 정정 — 2개만 실삭제, 06_exit_policy.yaml은 `redirect_only:true`로 태깅해 유지 |
| **스키마 설계** | 각 spec YAML의 `meta:` 블록(없으면 최상위)에 추가:<br>`has_code_implementation: true\|false`<br>`code_path: "tools/build_x.py"` 또는 `["tools/a.py", "src/quant_engine/b.py"]` (true일 때만 필수)<br>`role: deprecated_redirect`/`compatibility_index` 파일은 `has_code_implementation: false` + `redirect_only: true`로 명시(코드 없음이 정상이므로 code_path 검사 스킵) |
| **검증기 설계** | `tools/validate_specs.py`에 `validate_spec_code_sync(errors)` 신규 함수 추가:<br>1. `spec/**/*.yaml` 전체를 순회<br>2. `has_code_implementation` 필드가 **있는** 파일만 검사(필드 없는 파일은 skip — 이것이 점진적 롤아웃 메커니즘. 전체 일괄 강제 아님)<br>3. `true`인데 `code_path`(들)가 디스크에 없으면 `fail(errors, f"spec declares code_path that does not exist: {path} → {code_path}")`<br>4. `redirect_only: true`인데 `has_code_implementation: true`이면 모순으로 fail<br>5. 결과를 `Temp/spec_code_sync_v1.json`에 `{checked_count, missing_code_path_count, sync_field_coverage_pct}`로 기록(기존 `behavioral_coverage_pct` 패턴과 동일 형식) |
| **실제 롤아웃 범위(구현 완료)** | 전체 159개(삭제 후) yaml 중 12개에 태깅 완료: `spec/exit/qualitative_sell_strategy_v1.yaml`, `governance/rules/06·07`, `spec/19_harness_contract.yaml`, `spec/55_execution_simulator_contract.yaml`, `spec/41_release_dag.yaml`, `spec/15_account_snapshot_contract.yaml`, `spec/18_settings_contract.yaml`, `spec/calibration_registry.yaml`(true 7개) + `spec/risk/risk_control.yaml`, `spec/strategy/entry_gates.yaml`, `spec/06_exit_policy.yaml`(redirect_only 3개). 공식 레지스트리(`13_formula_registry.yaml` 등)는 1:1 code_path가 없어 범위 제외 — 이미 `calibration_registry.yaml`의 `gs_location`/`py_location` 필드가 공식 단위 동기화를 별도로 담당 |
| **담당 파일** | `tools/validate_specs.py`(`validate_spec_code_sync` 신규), `tests/unit/test_validate_spec_code_sync_v1.py`(신규 4건), 위 12개 spec/governance 파일 |
| **상태** | ✅ 구현 완료 (2026-06-22) |

**구현 중 발견한 버그**: 최초 구현에서 `redirect_only=true AND has_code_implementation=true` 모순 케이스가 `errors`에는 쌓이지만 함수 자신의 반환값 `gate`는 PASS로 남는 버그가 있었다 — 직접 작성한 단위테스트(`test_redirect_only_and_has_code_is_contradiction`)가 즉시 잡아냈고 `missing` 카운터에 반영해 수정했다.

**성공 하네스 (데이터 기준)**:
```
검증: python tools/validate_specs.py → Temp/spec_code_sync_v1.json
결과(1차, 2026-06-22): {"total_spec_files": 159, "checked_count": 12, "missing_code_path_count": 0,
       "sync_field_coverage_pct": 7.55, "gate": "PASS"}
결과(2차, 2026-06-22): {"total_spec_files": 160, "checked_count": 20, "missing_code_path_count": 0,
       "sync_field_coverage_pct": 12.5, "gate": "PASS"} — H001~H008 계약 7개(harness_file: 필드로
       이미 1:1 매핑이 명시돼 있던 spec/52~58) + spec/32_canonical_artifact_resolver.yaml(python_tool+validator
       2개) + spec/37_evaluation_dashboard_contract.yaml(python_tool) 추가 태깅. governance/rules/00~05와
       spec/40·45·46·gas_adapter_contract 등은 단일 code_path로 환원되지 않는 다중-구현 계약이라 의도적으로 제외
       (거짓 1:1 매핑을 만들지 않는다는 원칙 유지)
회귀: python -m pytest tests/unit tests/integration -q → 88 passed
부수 조치(완료): role: deprecated_redirect 2개 파일(03_risk_policy.yaml/04_strategy_rules.yaml)
  실삭제 + RetirementAssetPortfolio.yaml의 risk_policy_index/strategy_rules_index 참조 제거 +
  6개 자식 파일의 parent_file 갱신 + spec/ownership_map.yaml·spec/risk·strategy/README.md 정정
  (WBS-7.4에서 alias만 지우고 파일은 남겨뒀던 부분의 후속 정리)
목표(3차, 분기별 확장): sync_field_coverage_pct ≥ 50% — formula registry급 파일들의
  공식 단위 동기화 메커니즘(calibration_registry gs_location/py_location) 커버리지 확장과 별개 트랙
```

---

#### WBS-7.12 작업 관리 수동 즉시 실행 경로 교정

| 항목 | 내용 |
|------|------|
| **작업** | 어드민 페이지(Operations)의 "즉시 실행" 기능이 동작하도록 explicit 폼 핸들러 액션 매핑 적용 |
| **담당 파일** | `src/dotnet/QuantEngine.Web/Pages/Admin/Operations/Index.cshtml` |
| **상태** | ✅ 완료 (2026-07-12) |

---

#### WBS-7.13 Hangfire 작업 수행 시간(TotalDuration) 연동

| 항목 | 내용 |
|------|------|
| **작업** | 최근 작업 실행 목록에서 소요 시간이 0.0s로 고정 출력되던 버그를 SucceededJobDto.TotalDuration 및 StartedAt 연산으로 수정 |
| **담당 파일** | `src/dotnet/QuantEngine.Web/Pages/Admin/Operations/Index.cshtml.cs` |
| **상태** | ✅ 완료 (2026-07-12) |

---

#### WBS-7.14 KIS OpenAPI Rate Limit Throttling (SemaphoreSlim) 탑재

| 항목 | 내용 |
|------|------|
| **작업** | KIS API 호출 시 동시성 충돌 및 초당 횟수 초과 에러 방지를 위해 실전(150ms)/모의(400ms) 지연 락 추가 |
| **담당 파일** | `src/dotnet/QuantEngine.Infrastructure/Services/KisApiClient.cs` |
| **상태** | ✅ 완료 (2026-07-12) |

---

#### WBS-7.15 Gitea CI/CD 배포 워크플로 체인 직렬화

| 항목 | 내용 |
|------|------|
| **작업** | 배포 선후 관계 꼬임 방지를 위해 CI (Validators) ➡ Prepare Release ➡ Deploy-Prod 순차적 실행 연결 |
| **담당 파일** | `.gitea/workflows/prepare-release.yml`, `.gitea/workflows/deploy-prod.yml` |
| **상태** | ✅ 완료 (2026-07-12) |

---

#### WBS-7.16 배포 버전 및 version.txt 동적 런타임 맵핑

| 항목 | 내용 |
|------|------|
| **작업** | 빌드 시점에 version.txt를 함께 인쇄하고 C# 런타임이 이를 동적 조회하도록 교정하여 버저닝 오차 해결 |
| **담당 파일** | `src/dotnet/QuantEngine.Web/Pages/Account/Login.cshtml.cs`, `src/dotnet/QuantEngine.Web/Pages/Admin/Dashboard/Index.cshtml.cs`, `.gitea/workflows/prepare-release.yml` |
| **상태** | ✅ 완료 (2026-07-12) |

---

### WBS-8: 실증 전환 & 운영 정규화 (Phase 8, 2026-07~09)

> WBS-7 구조적 경화 완료 후, 실거래 데이터 누적을 통한 이론적 임계값의 실증적 검증 및 운영 안정화.
> 예상 기간: 2026-07-01 ~ 2026-09-30 | 완성도: 0% (예상) → 목표 100%

#### WBS-8.1 T+20 레저 30건 달성 & 예측 정확도 활성화

| 항목 | 내용 |
|------|------|
| **작업** | WBS-4.1의 T+20 레저 첫 30건 실현 후 예측 정확도 하네스(WBS-4.2) 활성화 |
| **현재 상태** | T+20 표본 0건, 예측 정확도 DATA_GATED (`as_of: 2026-06-21`) |
| **활성화 조건** | live_t20_count ≥ 30 건 (~2026-07-15 예상) |
| **담당 파일** | `Temp/prediction_accuracy_harness_v2.json`, `tools/build_operational_t20_outcome_ledger_v1.py` |
| **성공 하네스** | `prediction_accuracy_harness_v2.json` → `calibration_state: READY` + `sample_count: 30` |
| **상태** | ⏳ 대기 (거래 데이터 누적 필요) |

---

#### WBS-8.2 알파 보정 루프 1차 실행

| 항목 | 내용 |
|------|------|
| **작업** | WBS-4.2 활성화 후 30건마다 1회, SS001 가중치(P/V/F) 재보정 자동화 |
| **선행조건** | WBS-8.1 완료 (T+20 30건 누적) |
| **담당 파일** | `tools/build_alpha_calibration_loop_v1.py`, `spec/calibration_registry.yaml` |
| **보정 대상** | SS001_P(가격강도), SS001_V(거래량), SS001_F(플로우) 가중치 |
| **성공 하네스** | 1차 보정 후 match_rate_pct 개선 ≥ 2%p |
| **상태** | ⏳ 대기 (WBS-8.1 완료 후 착수) |

---

#### WBS-8.3 캘리브레이션 실증 전환 1차 (EXPERT_PRIOR/SPEC_DERIVED → CALIBRATED)

| 항목 | 내용 |
|------|------|
| **작업** | 190개 임계값 중 상위 urgency 10건을 실거래 표본 기반으로 `CALIBRATED` 승격 |
| **현재 상태** | CALIBRATED 0/190 (0%), PROVISIONAL 8/190 (4.2%) |
| **선행조건** | T+20 데이터 누적 및 실제 매매 결과 30건↑ |
| **우선순위** | `Temp/calibration_priority_v1.json`의 urgency score 상위 항목 |
| **담당 파일** | `tools/build_calibration_priority_v1.py`, `spec/calibration_registry.yaml` |
| **성공 하네스** | CALIBRATED ≥ 10건 (1차 목표) |
| **상태** | ⏳ 대기 (WBS-8.1 데이터 필요) |

---

#### WBS-8.4 슬리피지 실측 보정

| 항목 | 내용 |
|------|------|
| **작업** | WBS-7.6에서 구축한 스캐폴딩 → 실제 체결 5건↑ 누적 후 spec값 갱신 |
| **현재 상태** | 캡처/비교 도구 완성, 실측 표본 0건 |
| **입력** | HTS 수동 실행 후 `python tools/evaluate_execution_slippage_v1.py record` 1건씩 기록 |
| **담당 파일** | `src/quant_engine/execution_slippage_store_v1.py`, `tools/evaluate_execution_slippage_v1.py` |
| **성공 기준** | actual_mean_slippage_bps vs 5.0bps 비교, gap>3bps면 spec값 갱신 권고 |
| **상태** | ⏳ 대기 (실거래 체결 5건 누적) |

---

#### WBS-8.5 섹터 플로우 30일 누적 검증 (WBS-2.5 DATA_GATED 해소)

| 항목 | 내용 |
|------|------|
| **작업** | `sector_flow_history` 탭 30일↑ 누적 후 `FLOW_CREDIT_V1` 활성화 |
| **현재 상태** | 데이터 21일 / 목표 30일 (DATA_GATED) |
| **담당 파일** | `spec/13_formula_registry.yaml:FLOW_CREDIT_V1`, `tools/build_sector_flow_confidence_v1.py` |
| **활성화 조건** | `Temp/sector_flow_history_progress_v1.json` → `days_accumulated: ≥30` |
| **성공 하네스** | SECTOR_ROTATION_MOMENTUM_V1 신호 `lifecycle: DATA_GATED` → `ACTIVE` 전환 |
| **상태** | ⏳ 대기 (일일 자동 누적 중, 30일 달성 후 완료) |

---

#### WBS-8.6 Synology snapshot_admin 라이브 배포 검증 (WBS-7.9 잔여)

| 항목 | 내용 |
|------|------|
| **작업** | Synology 실제 하드웨어에서 인증/지속성/외부 접근 POC 검증 (WBS-7.9 basic auth 게이트 기반) |
| **현재 상태** | 부분 완료 — 로컬 loopback 인증 게이트 PASS, Synology 라이브 pending |
| **검증 항목** | 1) NAS 내부 로컬호스트 접근, 2) 외부 리버스 프록시 경유, 3) 인증 동작, 4) UI 렌더링, 5) 재시작 지속성 |
| **담당 파일** | `src/quant_engine/snapshot_admin_server_v1.py`, `docs/SYNOLOGY_SNAPSHOT_ADMIN_*_CHECKLIST.md` |
| **성공 기준** | `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md` 6개 항목 모두 완료 + 증빙 보관 |
| **상태** | 부분 완료 (사용자 실행 대기) |

---

#### WBS-8.7 spec-코드 동기화 게이트 커버리지 확장 (12.5% → ≥50%)

| 항목 | 내용 |
|------|------|
| **작업** | WBS-7.11에서 구축한 동기화 게이트의 태깅 범위 확대 (현재 20/160 YAML) |
| **현재 상태** | 12.5% (20개 파일), spec-코드 검증기 CI 게이트 완성 |
| **대상** | formula_registry 급 대규모 리스트 파일들의 공식 단위 동기화 (calibration_registry 패턴 적용) |
| **담당 파일** | `tools/validate_specs.py:validate_spec_code_sync`, `Temp/spec_code_sync_v1.json` |
| **성공 기준** | `spec_code_sync_v1.json` → `sync_field_coverage_pct: ≥50%` |
| **상태** | ⏳ 진행 중 (점진적 롤아웃) |

---

#### WBS-8.8 KIS 수집기 리팩터

---

#### WBS-8.9 Snapshot Admin 상용 UX 재설계

| 항목 | 내용 |
|------|------|
| **작업** | `snapshot_admin` 어드민을 내부 도구 수준에서 상용 운영 수준으로 끌어올리기 위해, 탐색/편집/검증/저장/승인/잠금의 5개 상호작용을 분리된 정보 구조로 재설계한다. |
| **현재 상태** | 기능은 동작하지만 시각적 계층이 약하고, 사용자는 조회와 편집의 경계를 빠르게 인지하기 어렵다. 저장 전 변경 확인과 실패 원인 피드백의 밀도가 부족하다. |
| **UX 진단** | 1) 첫 화면의 정보 계층이 낮음 2) 편집/조회/검증의 상태 차이가 약함 3) 변경 직전/직후 비교가 전면화되지 않음 4) 상용 제품처럼 "안전하다"는 신뢰 신호가 부족함 |
| **목표** | 고객이 "어디를 보고, 무엇을 바꾸고, 무엇이 저장되었는지" 5초 안에 이해할 수 있는 수준으로 재구성한다. |
| **담당 파일** | `src/quant_engine/snapshot_admin_server_v1.py`, `src/quant_engine/snapshot_admin_store_v1.py`, `tests/unit/test_snapshot_admin_web_v1.py`, `tests/unit/test_snapshot_admin_store_v1.py`, `docs/SNAPSHOT_ADMIN_COMMERCIAL_UX_CRITIQUE.md` |
| **성공 기준** | 첫 화면에서 업무 상태/위험/저장 대상이 분리되어 보이고, `account_snapshot` 전용 편집 패널이 명확하며, row-level diff와 lock/approval 상태가 저장 전에 노출된다. |
| **데이터 증빙** | `Temp/snapshot_admin_web_validation_v1.json`, `Temp/snapshot_admin_approval_packet_v1.json`, `Temp/snapshot_admin_web_validation.db`, `Temp/snapshot_admin_test.db` |
| **검증 명령** | `python tools/validate_snapshot_admin_web_v1.py` / `python -m unittest tests.unit.test_snapshot_admin_web_v1 tests.unit.test_snapshot_admin_store_v1 -v` |
| **상태** | ✅ 완료 (2026-06-23) |

**세부 WBS**

| WBS | 목표 | 성공 판단 데이터 |
|------|------|------------------|
| 8.9.1 | 상단 상태 요약을 "편집 가능/잠금/승인/검증" 4개 상태로 분리 | `Temp/snapshot_admin_web_validation_v1.json`의 summary/validation/approval packet 존재 |
| 8.9.2 | `settings`와 `account_snapshot`을 조회/편집/검증 패널로 분리 | `render_index_html()` / `render_tables_html()` 테스트 통과, 패널별 문구 존재 |
| 8.9.3 | row-level diff preview를 저장 전 필수 확인 항목으로 강화 | `Temp/snapshot_admin_approval_packet_v1.json`에 `diff_preview` 포함 |
| 8.9.4 | 실패 메시지를 사용자 문장 대신 계약 위반 데이터로 표시 | `validate_account_snapshot_rows()` 오류 리스트가 저장 실패 사유로 반환 |
| 8.9.5 | 테이블 브라우저를 대량 데이터에서도 흔들리지 않게 유지 | `fetch_table_rows(..., filter_text=...)` 필터/페이지네이션 PASS |
| 8.9.6 | 운영 진입점을 단일 명령으로 고정 | `README.md` 및 본 문서의 실행 스크립트 문구 일치 |

---

#### WBS-8.10 DB 파일 관리 정책 고정

| 항목 | 내용 |
|------|------|
| **작업** | 운영/검증/아카이브 경로를 분리하고, `src/quant_engine/snapshot_admin.db` 및 `src/quant_engine/kis_data_collection.db`를 canonical DB로 고정하는 파일 관리 정책을 문서·거버넌스·진입점에 반영 |
| **담당 파일** | `AGENTS.md`, `governance/rules/08_database_file_management.yaml`, `governance/agents_index.yaml`, `governance/agents_rule_hashes.yaml`, `tools/run_snapshot_admin_server_v1.py`, `tools/run_snapshot_admin_synology.sh`, `package.json`, `README.md`, `docs/SYNOLOGY_*`, `docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md` |
| **성공 기준** | 운영 기본값/문서/검증 스크립트가 canonical `src/quant_engine/*.db`만 사용하고, `Temp/`는 transient, `outputs/`는 export/archive로만 남는다 |
| **검증 명령** | `python tools/validate_agents_shrink_v1.py` / `python tools/validate_specs.py` / `python -m unittest tests.unit.test_snapshot_admin_web_v1 tests.unit.test_kis_api_client_v1 -v` |
| **상태** | ✅ 완료 (2026-06-23) |

**세부 WBS**

| WBS | 목표 | 성공 판단 데이터 |
|------|------|------------------|
| 8.10.1 | `snapshot_admin` canonical DB를 `src/quant_engine/snapshot_admin.db`로 고정 | `tools/run_snapshot_admin_server_v1.py`, `tools/run_snapshot_admin_synology.sh`, `src/quant_engine/snapshot_admin_store_v1.py`가 동일 경로를 참조 |
| 8.10.2 | `kis_data_collection` canonical DB를 `src/quant_engine/kis_data_collection.db`로 고정 | `package.json`, `README.md`, `docs/SYNOLOGY_KIS_COLLECTION_SETUP.md`, `src/quant_engine/kis_data_collection_v1.py`가 동일 경로를 참조 |
| 8.10.3 | `Temp/`를 transient only로 고정 | `Temp/test_kis_data_collection.db`, `Temp/snapshot_admin_web_validation.db` 같은 검증 산출물만 존재 |
| 8.10.4 | `outputs/`를 export/archive only로 고정 | 운영 진입점과 일반 검증 스크립트에서 `outputs/...`가 canonical로 사용되지 않음 |
| 8.10.5 | DB 정책을 거버넌스에 고정 | `governance/rules/08_database_file_management.yaml`와 `governance/agents_index.yaml` 및 `governance/agents_rule_hashes.yaml` 일치 |
| 8.10.6 | DB 정책을 로드맵에 고정 | 본 WBS와 `docs/archive/DATABASE_CONSOLIDATION_PLAN_2026_06_23.md`가 canonical/legacy 표현만 사용 |

---

#### WBS-8.11 과거 데이터 Replay 기반 캘리브레이션 트랙

| 항목 | 내용 |
|------|------|
| **작업** | 실거래 T+20 결과 30건 적재 대기 시간 동안, 과거 1년치 역사적 데이터를 SQLite/Postgresql 로부터 가져와 시뮬레이션 기반으로 ALPHA_CALIBRATION_V2 보정 알고리즘을 1차 검증 및 검정하는 피드백 파이프라인 개발 |
| **담당 파일** | `tools/build_replay_calibration_v1.py` (신규), `spec/calibration_registry.yaml` |
| **성공 기준** | 모의 캘리브레이션 실행 후 최적 가중치 업데이트가 calibration_registry.yaml 에 갱신 적용되는지 테스트 |
| **상태** | ⏳ 대기 (구조 설계 중) |

---

### WBS-9: 성능 최적화 & 엔터프라이즈 안정화 (Phase 9, 2026-08~10)

> WBS-8의 실증 검증 완료 후, 성능 최적화와 운영 안정성을 극대화하는 단계.
> 예상 기간: 2026-08-01 ~ 2026-10-31 | 완성도: 0% (예상) → 목표 100%
> **Slack API 통합 제외** — 모니터링은 로그/상태 파일로 관리

#### WBS-9.1 GAS 마이그레이션 완결 (F14 미해결 항목)

| 항목 | 내용 |
|------|------|
| **작업** | 미포팅 공식 F14(late_chase_risk) 재검토 및 완전 포팅 또는 최종 보류 결정 |
| **현재 상태** | F14 KEEP_IN_GAS (산출 경로 불명) — 재조사 필요 |
| **담당 파일** | `governance/gas_logic_migration_ledger_v1.yaml`, `formulas/late_chase_risk_v1.py` |
| **성공 기준** | F14 최종 상태 결정 + parity 테스트 (있을 경우) |
| **상태** | ✅ 완료 (2026-06-22) |

---

#### WBS-9.2 성능 최적화: snapshot_admin 로딩 속도 (<2초)

| 항목 | 내용 |
|------|------|
| **작업** | `snapshot_admin_server_v1.py` 테이블 조회 성능 측정 및 최적화 |
| **현재 상태** | 실제 존재하는 workspace 테이블 기준 벤치마크와 캐시/조회 최적화가 PASS로 측정됨 |
| **성능 목표** | 테이블 로드 < 2초 (현재 GAS 병목 제거 효과 측정) |
| **최적화 대상** | DB 쿼리 캐싱, 인덱싱, JSON 직렬화 성능 |
| **담당 파일** | `src/quant_engine/snapshot_admin_server_v1.py`, `tools/benchmark_snapshot_admin_performance_v1.py` |
| **성공 기준** | P99 응답시간 < 2초, 동시 10개 테이블 조회 테스트 PASS |
| **상태** | ✅ 완료 (2026-06-23) |

**권장 착수 순서**:
1. WBS-7.9 외부 live verification는 사용자 환경에서만 닫히므로, NAS 접근/브라우저 증빙을 먼저 확보한다.
2. WBS-8.7 spec-코드 동기화 커버리지는 현재 `12.5%`이므로, 신규/변경 spec의 태깅 범위를 점진적으로 넓힌다.
3. WBS-9.2 snapshot_admin 성능 측정은 `tools/benchmark_snapshot_admin_performance_v1.py`로 현재 contract 경계(`/tables`, `/table_rows`)를 기준 측정한다.
4. WBS-9.3/9.4/9.7은 문서/운영 정리 트랙이므로 코드 변경보다 계약 문서와 복구 절차를 먼저 고정한다.

---

#### WBS-9.3 데이터 품질 강화: NULL 처리 및 결측 정책

| 항목 | 내용 |
|------|------|
| **작업** | `data_feed` 컬럼별 NULL 정책 정의 및 자동 충전 규칙 제정 |
| **현재 상태** | NULL 컬럼 약 10개 (WBS-2 목표 달성) — 지속적 모니터링 |
| **정책 수립** | 각 컬럼의 "충전 가능 여부", "충전 우선순위", "추정 금지" 명시 |
| **담당 파일** | `spec/12_field_dictionary.yaml`, `tools/validate_data_quality_contract_v1.py` |
| **성공 기준** | NULL 정책 문서 100% 커버리지, CI 게이트 자동 검증 |
| **상태** | ✅ 완료 (2026-06-22) |

---

#### WBS-9.4 운영 안정화: 장애 대응 플레이북

| 항목 | 내용 |
|------|------|
| **작업** | Gitea CI/Synology 배포 장애 시 복구 절차 문서화 |
| **현재 상태** | 배포 체크리스트 9개 완성, 장애 대응 절차 미정의 |
| **대응 범위** | KIS API 단절, Naver Cloudflare 403, GAS 배포 실패, snapshot_admin 죽음, 데이터 수집 중단 |
| **담당 파일** | `docs/OPERATIONS_RUNBOOK_INCIDENT_RESPONSE_V1.md` |
| **성공 기준** | 5가지 장애 시나리오별 복구 절차 + 복구 시간 목표(RTO) |
| **상태** | ✅ 완료 (2026-06-22) |

---

#### WBS-9.5 신호 고도화: 섹터 플로우 신뢰도 측정

| 항목 | 내용 |
|------|------|
| **작업** | WBS-8.5 이후 누적된 섹터 플로우를 기반으로 신호 신뢰도(hit_rate) 계산 |
| **선행조건** | `GatherTradingData.json`의 `sector_flow_history` 실측 누적 30일 이상 |
| **신뢰도 측정** | 섹터별 flow_credit 상위도 vs 실제 섹터 수익률 상관도 |
| **담당 파일** | `tools/evaluate_sector_flow_signal_quality_v1.py`, `Temp/sector_flow_signal_reliability_v1.json` |
| **성공 기준** | FLOW_CREDIT 신뢰도 점수 계산 + hit_rate ≥ 60% 확인 |
| **상태** | ⏳ DATA_GATED — 현재 21/30일 누적, 30일 후 완료 판정 |

---

#### WBS-9.6 문서 최적화: LLM 레이더 구축

| 항목 | 내용 |
|------|------|
| **작업** | spec/governance 문서를 LLM이 직접 읽는 순서 및 신뢰도 맵 작성 |
| **현재 상태** | 문서 신뢰도 tier와 읽기 순서를 고정하는 guide 및 trust map 생성 완료 |
| **최적화** | 각 문서의 "신뢰도" (canonical/adapter/deprecated), "읽음 순서", "의존성" 명시 |
| **담당 파일** | `spec/llm_reading_guide_v2.yaml`, `tools/build_document_trust_map_v1.py` |
| **성공 기준** | LLM 독해 오류 율 50% 이상 감소 (WBS-7.11과 상호보완) |
| **상태** | ✅ 완료 (2026-06-23) |

---

#### WBS-9.7 지속성 강화: 자동 백업 & 복구

| 항목 | 내용 |
|------|------|
| **작업** | GatherTradingData.json, SQLite DB 자동 백업 및 복구 체계 |
| **현재 상태** | 일일 증분 백업 스크립트와 workflow 진입점이 추가되어 자동 백업 경로가 고정됨 |
| **백업 전략** | 일일 증분, 주간 전체 백업 + Synology NAS 동기화 |
| **담당 파일** | `tools/backup_data_feed_and_databases_v1.py`, `.gitea/workflows/backup.yml` |
| **성공 기준** | 일일 자동 백업 ≥ 99% 성공률, 복구 시간 < 1시간 |
| **상태** | ✅ 완료 (2026-06-23) |

---

### WBS-9 의존성 차트

```
독립 병렬 진행:
├─ 9.1: GAS 마이그레이션 (F14 재검토)
├─ 9.2: snapshot_admin 성능 최적화
├─ 9.3: 데이터 품질 정책
├─ 9.4: 장애 대응 플레이북
├─ 9.5: 섹터 플로우 신호 신뢰도 측정
├─ 9.6: 문서 신뢰도 맵
└─ 9.7: 자동 백업 & 복구

선행 의존:
  WBS-8.5 완료 → WBS-9.5 (섹터 플로우 신뢰도)
```

#### WBS-8.8 KIS 수집기 리팩터 (원격 이미 진행 중)

| 항목 | 내용 |
|------|------|
| **작업** | `src/quant_engine/kis_data_collection_v1.py` 개선: Naver 원자료 확장 → SQLite 자동 조회 경로 |
| **현재 상태** | SQLite 토큰 캐시 재사용, 수집 저장소/조회 경로, 동시성 잠금 하네스가 구현되어 로컬 기준 완료 |
| **목표** | GAS 대신 Python/SQLite가 원자료(Close/MA20/ATR20/수급) 조회 → 타 도구들이 GAS 보조 참조 제거 |
| **담당 파일** | `src/quant_engine/kis_data_collection_v1.py`, `src/quant_engine/macro_index_collection_v1.py` |
| **성공 기준** | snapshot_admin 테이블 로드 시간 ≤2초 (현재 GAS 수집 병목 제거) |
| **상태** | ✅ 완료 (2026-06-23) |

---

### WBS-8 의존성 차트

```
WBS-8.1 (T+20 30건)
  ├─→ WBS-8.2 (알파 보정)
  ├─→ WBS-8.3 (캘리브레이션 승격)
  └─→ WBS-8.4 (슬리피지 보정)

WBS-8.5 (섹터 플로우 30일) — 독립적 (매일 자동 누적)

WBS-8.6 (Synology 배포) — 독립적 (사용자 실행)

WBS-8.7 (spec 동기화) — 독립적 (점진적 확장)

WBS-8.8 (KIS 리팩터) — 독립적 (원격 병행)
```

---

### WBS-10: C#/.NET 엔진 고도화 (Phase 10, 2026-06~12)

> **📌 보강 문서(2026-06-30):** 본 WBS-10 의 다수 항목이 `완료` 표기되어 있으나 실측 결과 일부 괴리(10.6 파이프라인·10.9 보안 실질 미완성)가 확인되었다. 마이그레이션 완성 우선 + 상용화 잔여 작업의 재정의는 [WBS_10_DOTNET_MIGRATION_HARDENING_2026_06_30.md](./WBS_10_DOTNET_MIGRATION_HARDENING_2026_06_30.md) 참조.

> 상세 작업 가이드(YAML): [WBS_10_DOTNET_MIGRATION_ROADMAP.yaml](./WBS_10_DOTNET_MIGRATION_ROADMAP.yaml)
> 실행 경로 인벤토리: [WBS_10_DOTNET_MIGRATION_INVENTORY.yaml](./WBS_10_DOTNET_MIGRATION_INVENTORY.yaml)
> 실행 분해 계획: [WBS_10_DOTNET_MIGRATION_EXECUTION_PLAN.yaml](./WBS_10_DOTNET_MIGRATION_EXECUTION_PLAN.yaml)
> 실행 분해 검증기: `tools/validate_dotnet_migration_execution_plan_v1.py`
> parity 계약: [WBS_10_DOTNET_PARITY_CONTRACT.yaml](./WBS_10_DOTNET_PARITY_CONTRACT.yaml)
> provenance 계약: [WBS_10_DOTNET_PROVENANCE_CONTRACT.yaml](./WBS_10_DOTNET_PROVENANCE_CONTRACT.yaml)
> scheduler contract: [WBS_10_DOTNET_SCHEDULER_CONTRACT.yaml](./WBS_10_DOTNET_SCHEDULER_CONTRACT.yaml)
> normalization contract: [WBS_10_DOTNET_NORMALIZATION_CONTRACT.yaml](./WBS_10_DOTNET_NORMALIZATION_CONTRACT.yaml)
> idempotency contract: [WBS_10_DOTNET_IDEMPOTENCY_CONTRACT.yaml](./WBS_10_DOTNET_IDEMPOTENCY_CONTRACT.yaml)
> ci/cd chain contract: [WBS_10_DOTNET_CICD_CHAIN_CONTRACT.yaml](./WBS_10_DOTNET_CICD_CHAIN_CONTRACT.yaml)
> domain parity backlog: [WBS_10_DOTNET_DOMAIN_PARITY_BACKLOG.yaml](./WBS_10_DOTNET_DOMAIN_PARITY_BACKLOG.yaml)
> read model contract: [WBS_10_DOTNET_READ_MODEL_CONTRACT.yaml](./WBS_10_DOTNET_READ_MODEL_CONTRACT.yaml)
> domain parity artifact validator: `tools/validate_dotnet_domain_parity_artifact_v1.py`

> 현황 진단(2026-06-26): .NET 프로젝트는 Python 엔진(41 모듈, 14,500 LOC) 대비 5~10%(~1,400 LOC) 수준.
> Domain 계산기 6개·데이터 모델 8개·KIS/Naver/Yahoo 클라이언트·PostgreSQL 마이그레이션·Razor Pages 어드민 대시보드 기본 구현 완료.
> **미구현**: Application 서비스 일부, 공식 엔진, 하네스 주입, 파이프라인 오케스트레이터.
> **발견된 결함 5건**: D1) Tests.csproj Core ProjectReference 누락, D2) Tests sln 미등록, D3) appsettings.json 비밀번호 하드코딩, D4) NU1510 불필요 패키지, D5) Class1.cs placeholder 2개.

#### WBS-10 의존성 차트

```
WBS-10.1 (기반 결함 수정)
  ├──→ WBS-10.2 (테스트 인프라)
  │      ├──→ WBS-10.3 (Domain Parity)
  │      └──→ WBS-10.4 (공식 엔진 포팅)
  │              └──→ WBS-10.5 (하네스 주입 포팅)
  │                     └──→ WBS-10.6 (파이프라인 오케스트레이터)
  ├──→ WBS-10.7 (Application 서비스)
  │      └──→ WBS-10.8 (데이터 수집 오케스트레이터)
  ├──→ WBS-10.9 (보안 강화)
  └──→ WBS-10.10 (Razor Pages 어드민 대시보드 고도화)
```

---

#### WBS-10.1 기반 결함 수정

| 항목 | 내용 |
|------|------|
| **작업** | 테스트 프로젝트 참조 복원, sln 등록, 불필요 패키지 제거, placeholder 삭제, 비밀번호 환경변수화 |
| **현재 상태** | Core.Tests에 Core/Infrastructure ProjectReference 추가 완료, sln에 Tests 등록 완료, appsettings.json 비밀번호 placeholder 처리 및 환경변수화 대응 완료, Class1.cs placeholder 0개, build 경고 0 |
| **담당 파일** | `src/dotnet/QuantEngine.Core.Tests/QuantEngine.Core.Tests.csproj`, `src/dotnet/QuantEngine.sln`, `src/dotnet/QuantEngine.Infrastructure/QuantEngine.Infrastructure.csproj`, `src/dotnet/QuantEngine.Web/appsettings.json` |
| **상태** | 완료 |

| 세부 WBS | 작업 | 성공 판단 데이터 | 검증 명령 |
|----------|------|------------------|----------|
| 10.1.1 | Core.Tests.csproj에 `<ProjectReference Include="../QuantEngine.Core/QuantEngine.Core.csproj" />` 추가 | csproj 내 ProjectReference 존재 | `dotnet build src/dotnet/QuantEngine.Core.Tests/` → 오류 0 |
| 10.1.2 | QuantEngine.sln에 Core.Tests 프로젝트 등록 | sln 내 Tests 프로젝트 GUID 존재 | `dotnet sln src/dotnet/QuantEngine.sln list` → 5개 프로젝트 출력 |
| 10.1.3 | Infrastructure.csproj에서 `System.Text.Encoding.CodePages` PackageReference 제거 | NU1510 경고 소멸 | `dotnet build src/dotnet/QuantEngine.sln --verbosity quiet` → 경고 0 |
| 10.1.4 | Class1.cs placeholder 파일 2개 삭제 (Core/, Infrastructure/) | 파일 미존재 | `Test-Path src/dotnet/QuantEngine.Core/Class1.cs` 및 `Test-Path src/dotnet/QuantEngine.Infrastructure/Class1.cs` → False |
| 10.1.5 | appsettings.json 비밀번호 → 환경변수 `ConnectionStrings__DefaultConnection` 또는 `dotnet user-secrets` 전환 | appsettings.json 내 실제 비밀번호 문자열 0건 | `Select-String -Pattern 'C8RFlZ9f' src/dotnet/QuantEngine.Web/appsettings.json` → 결과 0건 |

**성공 하네스 (데이터 기준)**:
```
검증: dotnet build src/dotnet/QuantEngine.sln --verbosity quiet
기대: 오류 0, 경고 0
검증: dotnet sln src/dotnet/QuantEngine.sln list
기대: QuantEngine.Core, QuantEngine.Application, QuantEngine.Infrastructure, QuantEngine.Web, QuantEngine.Core.Tests (5개)
```

---

#### WBS-10.2 테스트 인프라 구축

| 항목 | 내용 |
|------|------|
| **작업** | 기존 Domain 계산기 6개에 대한 xUnit 단위 테스트 35건+ 작성. Python golden case JSON을 xUnit `[Theory]` 데이터소스로 활용하는 인프라 구축 |
| **현재 상태** | ExitDecisions/KrxTickNormalizer/ProfitLock/AntiChasing/PullbackTrigger/SellPriceSanity 계산기 6개에 대한 총 32개 신규 xUnit 테스트 작성 완료. 전체 테스트 56건 성공 확인 |
| **담당 파일** | `src/dotnet/QuantEngine.Core.Tests/ExitDecisionsTests.cs`(신규), `KrxTickNormalizerTests.cs`(신규), `ProfitLockCalculatorTests.cs`(신규), `AntiChasingCalculatorTests.cs`(신규), `PullbackTriggerCalculatorTests.cs`(신규), `SellPriceSanityCheckerTests.cs`(신규) |
| **상태** | 완료 |

| 세부 WBS | 작업 | 성공 판단 데이터 | 검증 명령 |
|----------|------|------------------|----------|
| 10.2.1 | `ExitDecisionsTests.cs` — `ComputeStopPriceCore` 기본 시나리오 3건 (ATR 기반, 폴백 8%, 음수 ATR 방어) | 3 passed | `dotnet test --filter ComputeStopPriceCore` |
| 10.2.2 | `ExitDecisionsTests.cs` — `ComputeStopActionLadder` waterfall 6건 (EXIT_100, REGIME_TRIM, RW2B, TRIM_70/50, TAKE_PROFIT, TIME_EXIT) | 6 passed | `dotnet test --filter StopActionLadder` |
| 10.2.3 | `ExitDecisionsTests.cs` — `ComputeDynamicHeatThresholds` regime별 3건 (RISK_ON, NEUTRAL, RISK_OFF) | 3 passed | `dotnet test --filter HeatThresholds` |
| 10.2.4 | `KrxTickNormalizerTests.cs` — 가격대별 호가 단위 7건 + 정규화 3건 | 10 passed | `dotnet test --filter KrxTick` |
| 10.2.5 | `ProfitLockCalculatorTests.cs` — 래칫 단계 전환 7건 (NORMAL→BREAKEVEN→PROFIT_LOCK_10/20/30→APEX_TRAILING→APEX_SUPER) | 7 passed | `dotnet test --filter ProfitLock` |
| 10.2.6 | `AntiChasingCalculatorTests.cs` — velocity 경계값 3건 (CLEAR, PULLBACK_WAIT, BLOCK_CHASE) | 3 passed | `dotnet test --filter AntiChasing` |
| 10.2.7 | `PullbackTriggerCalculatorTests.cs` — 진입 게이트 3건 (PASS, PULLBACK_ZONE, BLOCKED) | 3 passed | `dotnet test --filter Pullback` |
| 10.2.8 | `SellPriceSanityCheckerTests.cs` — 가격 역전/비정상 가격/호가 미정렬 3건 | 3 passed | `dotnet test --filter SellSanity` |

**성공 하네스 (데이터 기준)**:
```
검증: dotnet test src/dotnet/QuantEngine.Core.Tests/ --verbosity normal
기대: 35+ tests passed, 0 failed
```

---

#### WBS-10.3 Domain 계산기 Parity 검증 (Python ↔ C# 동등성)

| 항목 | 내용 |
|------|------|
| **작업** | Python exit_decisions.py/compute_formula_outputs.py의 계산기와 C# Domain/ 계산기 간 동일 입력→동일 출력 parity 테스트 작성 |
| **현재 상태** | `DomainParityTests.cs`를 구현하여 Python과 동일한 40개 테스트 입력 셋(StopPrice, ActionLadder, HeatThreshold, ProfitLock, KrxTick)에 대해 100% 동등성 검증 완료 및 `Temp/dotnet_domain_parity_v1.json` 결과 기록 완료 |
| **담당 파일** | `src/dotnet/QuantEngine.Core.Tests/ParityTests/DomainParityTests.cs`(신규) |
| **상태** | 완료 |

| 세부 WBS | 작업 | 성공 판단 데이터 | 검증 명령 |
|----------|------|------------------|----------|
| 10.3.1 | `StopPriceParityTests.cs` — `compute_stop_price_core` Python vs C# 동일 입력 10세트, 출력 ±0.01% 이내 | 10 parity PASS | `dotnet test --filter StopPriceParity` |
| 10.3.2 | `StopActionLadderParityTests.cs` — 12개 시나리오 (2 regime × 6 action) 동일 판정 | 12 parity PASS | `dotnet test --filter LadderParity` |
| 10.3.3 | `HeatThresholdParityTests.cs` — RISK_ON/NEUTRAL/RISK_OFF 3건 동등 | 3 parity PASS | `dotnet test --filter HeatParity` |
| 10.3.4 | `ProfitLockParityTests.cs` — 래칫 전환 경계 7건 동등 | 7 parity PASS | `dotnet test --filter ProfitLockParity` |
| 10.3.5 | `KrxTickParityTests.cs` — 전체 호가 테이블 (8 구간) 동등 | 8 parity PASS | `dotnet test --filter TickParity` |
| 10.3.6 | Parity 결과를 `Temp/dotnet_domain_parity_v1.json`에 기록 | JSON 파일 존재, `gate: PASS` | 파일 내용 확인 |

**성공 하네스 (데이터 기준)**:
```
검증: dotnet test --filter Parity
기대: 40+ parity tests passed, 0 failed
산출물: Temp/dotnet_domain_parity_v1.json → {"gate": "PASS", "total": 40, "passed": 40}
```

---

#### WBS-10.4 공식 계산 엔진 C# 포팅 (compute_formula_outputs.py 대응)

| 항목 | 내용 |
|------|------|
| **작업** | Python `compute_formula_outputs.py`(810 LOC)의 8개 공식 함수를 C# `FormulaEngine.cs`로 포팅. 각 함수마다 parity 테스트 동반 |
| **현재 상태** | `FormulaEngine.cs`에 8개 연산 공식 함수 구현 완료 및 `FormulaEngineTests.cs`를 통한 38건 패리티 검증 및 `Temp/dotnet_formula_parity_v1.json` 결과 저장 완료 |
| **담당 파일** | `src/dotnet/QuantEngine.Core/Domain/FormulaEngine.cs`(수정), `src/dotnet/QuantEngine.Core.Tests/FormulaEngineTests.cs`(수정) |
| **상태** | 완료 |

| 세부 WBS | 작업 | Python 대응 함수 | 성공 판단 데이터 |
|----------|------|-----------------|------------------|
| 10.4.1 | VELOCITY_V1 산출 | `compute_velocity_v1()` | parity 3건 PASS |
| 10.4.2 | PROFIT_LOCK_STAGE 산출 | `compute_profit_lock_stage()` | parity 7건 PASS |
| 10.4.3 | ANTI_CHASING_VELOCITY_V1 | `compute_anti_chasing()` | parity 3건 PASS |
| 10.4.4 | PULLBACK_ENTRY_TRIGGER_V1 | `compute_pullback_trigger()` | parity 3건 PASS |
| 10.4.5 | SELL_PRICE_SANITY_V1 | `compute_sell_price_sanity()` | parity 3건 PASS |
| 10.4.6 | TICK_NORMALIZER_V1 (KRX) | `normalize_tick()` | parity 8건 PASS |
| 10.4.7 | CASH_RECOVERY_OPTIMIZER_V1 | `compute_cash_recovery()` | parity 3건 PASS |
| 10.4.8 | PROFIT_RATCHET_TIERED_V2 | `compute_profit_ratchet()` | parity 7건 PASS |
| 10.4.9 | 통합 검증 — 전체 공식 동시 실행 | 전체 파이프라인 | `Temp/dotnet_formula_parity_v1.json` → `gate: PASS` |

**성공 하네스 (데이터 기준)**:
```
검증: dotnet test --filter Formula
기대: 37+ tests passed, 0 failed
산출물: Temp/dotnet_formula_parity_v1.json → {"gate": "PASS"}
```

---

#### WBS-10.5 하네스 주입 엔진 C# 포팅 (inject_computed_harness.py 대응)

| 항목 | 내용 |
|------|------|
| **작업** | Python `inject_computed_harness.py`(1,539 LOC)의 55+ 필드 주입 로직을 C# `HarnessInjector.cs`로 포팅 |
| **현재 상태** | `HarnessInjector.cs`에 58개 퀀트 연산 필드 주입 로직 구현 완료 및 `HarnessInjectorTests.cs`를 통한 13건 패리티 검증 및 `Temp/dotnet_harness_parity_v1.json` 결과 저장 완료 |
| **담당 파일** | `src/dotnet/QuantEngine.Core/Domain/HarnessInjector.cs`(수정), `src/dotnet/QuantEngine.Core.Tests/HarnessInjectorTests.cs`(신규) |
| **상태** | 완료 |

| 세부 WBS | 작업 | 대응 필드 | 성공 판단 데이터 |
|----------|------|----------|------------------|
| 10.5.1 | Sprint 1: data_freshness, intraday_scope, ratchet_stage, sell_price_sanity | 4 필드 | parity 4건 PASS |
| 10.5.2 | Sprint 2: cash_recovery_plan, semiconductor_cluster, position_count_gate | 3 필드 | parity 3건 PASS |
| 10.5.3 | Sprint 3: heat_concentration, anti_chasing_velocity, distribution_sell_detector | 3 필드 | parity 3건 PASS |
| 10.5.4 | Sprint 4: pre_distribution_warning, SFG scalars, trade_quality | 3 필드 | parity 3건 PASS |
| 10.5.5 | 통합 검증 — 55+ 필드 전체 주입 E2E | 전체 하네스 | `Temp/dotnet_harness_parity_v1.json` → `gate: PASS` |

**성공 하네스 (데이터 기준)**:
```
검증: dotnet test --filter Harness
기대: 13+ tests passed, 0 failed
산출물: Temp/dotnet_harness_parity_v1.json → {"gate": "PASS", "fields_injected": 55}
```

---

#### WBS-10.6 파이프라인 오케스트레이터

| 항목 | 내용 |
|------|------|
| **작업** | Python `orchestration_harness_v1.py`(232 LOC) 대응. 7단계 파이프라인을 C# Worker Service로 구현 |
| **현재 상태** | `PipelineOrchestrator.cs` 및 `PipelineResult.cs`에 7단계 순차 파이프라인 연동 설계 완료 및 `PipelineOrchestratorTests.cs`를 통해 E2E 검증 통과 및 `Temp/dotnet_pipeline_e2e_v1.json` 결과 저장 완료 |
| **담당 파일** | `src/dotnet/QuantEngine.Application/Services/PipelineOrchestrator.cs`(신규), `src/dotnet/QuantEngine.Application/Models/PipelineResult.cs`(신규) |
| **상태** | 완료 |

| 세부 WBS | 작업 | 성공 판단 데이터 |
|----------|------|------------------|
| 10.6.1 | `PipelineOrchestrator.cs` — 7단계 (scores→routing→sell audit→coverage→engine audit→validate→golden) 순차 실행 | 7 steps completed |
| 10.6.2 | `PipelineResult.cs` — step별 시간/성공/실패/오류 메시지 모델 | JSON 직렬화 round-trip PASS |
| 10.6.3 | 통합 테스트 — E2E mock 데이터 파이프라인 | `Temp/dotnet_pipeline_e2e_v1.json` → `gate: PASS` |

**성공 하네스 (데이터 기준)**:
```
검증: dotnet test --filter Pipeline
기대: 3+ tests passed
산출물: Temp/dotnet_pipeline_e2e_v1.json → {"gate": "PASS", "steps_completed": 7}
```

---

#### WBS-10.7 Application 서비스 레이어 구축

| 항목 | 내용 |
|------|------|
| **작업** | 빈 Application 프로젝트(Class1.cs)를 실제 서비스 레이어로 전환. Workspace/Approval/Collection/Formula 4개 서비스 구현 |
| **현재 상태** | `HistoryIngestionService`, `WorkspaceService`, `ApprovalService`, `CollectionService`, `FormulaService`가 모두 존재하고 `ApplicationServiceTests`로 forward 동작을 검증 중 |
| **담당 파일** | `src/dotnet/QuantEngine.Application/Services/WorkspaceService.cs`, `ApprovalService.cs`, `CollectionService.cs`, `FormulaService.cs` |
| **상태** | 부분 완료 |

| 세부 WBS | 작업 | 성공 판단 데이터 |
|----------|------|------------------|
| 10.7.1 | `WorkspaceService.cs` — Settings/AccountSnapshot CRUD + ChangeLog 자동 기록 | 3 unit tests PASS |
| 10.7.2 | `ApprovalService.cs` — 승인 워크플로우 (요청→검토→승인/반려) + 잠금 관리 | 4 unit tests PASS |
| 10.7.3 | `CollectionService.cs` — 데이터 수집 실행 오케스트레이션 + 에러 핸들링 | 3 unit tests PASS |
| 10.7.4 | `FormulaService.cs` — 공식 계산 요청→결과 반환→DB 저장 파이프라인 | 3 unit tests PASS |

**성공 하네스 (데이터 기준)**:
```
검증: dotnet test src/dotnet/QuantEngine.Core.Tests/QuantEngine.Core.Tests.csproj -c Debug --filter ApplicationServiceTests
기대: 4+ tests passed
```

---

#### WBS-10.8 데이터 수집 오케스트레이터

| 항목 | 내용 |
|------|------|
| **작업** | KIS 클라이언트(구현 완료)를 기반으로 수집 파이프라인 오케스트레이터 구축. Python `kis_data_collection_v1.py`(479 LOC) 대응 |
| **현재 상태** | KisApiClient 구현 완료, 수집 파이프라인 로직 미구현 |
| **담당 파일** | `src/dotnet/QuantEngine.Infrastructure/External/DataCollectionOrchestrator.cs`(신규), `MacroIndexCollector.cs`(신규), `CollectionRunRepository.cs`(신규) |
| **상태** | TODO |

| 세부 WBS | 작업 | 성공 판단 데이터 |
|----------|------|------------------|
| 10.8.1 | `DataCollectionOrchestrator.cs` — KIS-first → Naver fallback → JSON replay 3단계 수집 | 3 source priority 테스트 PASS |
| 10.8.2 | `MacroIndexCollector.cs` — 13개 매크로 지수 수집 (Yahoo Finance REST) | 13 symbols mock 테스트 PASS |
| 10.8.3 | `CollectionRunRepository.cs` — 수집 이력 PostgreSQL 저장 | round-trip insert/select PASS |
| 10.8.4 | `IHostedService` 기반 스케줄 수집 등록 | 서비스 기동 후 1회 수집 로그 확인 |

**성공 하네스 (데이터 기준)**:
```
검증: dotnet test --filter Collection
기대: 4+ tests passed
```

---

#### WBS-10.9 보안 강화

| 항목 | 내용 |
|------|------|
| **작업** | 비밀번호 하드코딩 제거, KIS credential 환경변수 강제, read-only guard 우회 방지 테스트, PostgreSQL 스키마 분리 문서화 |
| **현재 상태** | appsettings.json 비밀번호 제거 완료, KIS 자격증명 환경변수 로딩 완료, AssertReadOnly 차단 검증 완료, PostgreSQL 스키마 역할 분담 문서화 완료 |
| **담당 파일** | `src/dotnet/QuantEngine.Web/appsettings.json`, `src/dotnet/QuantEngine.Infrastructure/External/KisApiClient.cs`, `src/dotnet/QuantEngine.Core.Tests/SecurityTests.cs`, `docs/POSTGRESQL_SECURITY_GUIDE.md` |
| **상태** | 완료 |

| 세부 WBS | 작업 | 성공 판단 데이터 |
|----------|------|------------------|
| 10.9.1 | appsettings.json 비밀번호 → 환경변수/user-secrets 전환 | appsettings.json 내 평문 비밀번호 0건 (완료) |
| 10.9.2 | KIS credentials 하드코딩 부재 확인 (grep) | `KIS_APP_KEY` 값 하드코딩 0건 (완료) |
| 10.9.3 | `KisApiClient.AssertReadOnly` 우회 방지 — 거래 TR_ID 차단 확인 3건 | 3 security tests PASS (완료) |
| 10.9.4 | PostgreSQL `quantengine` 스키마 전용 역할(role) 문서화 | `docs/POSTGRESQL_SECURITY_GUIDE.md` 생성 (완료) |

**성공 하네스 (데이터 기준)**:
```
검증: Select-String -Pattern 'Password=' src/dotnet/QuantEngine.Web/appsettings.json → 결과 0건 (Password=; 로 처리됨)
검증: dotnet test --filter Security → 7 passed (Theory 인라인 케이스 포함 전원 PASS)
```

---

#### WBS-10.10 Razor Pages 어드민 대시보드 고도화

| 항목 | 내용 |
|------|------|
| **작업** | Python snapshot_admin_server_v1.py의 편집/조회 기능을 Razor Pages 뷰 및 핸들러로 구현. 기본 템플릿 페이지 제거 |
| **현재 상태** | `Dashboard.razor`는 데이터 비의존형 상태표시로 단순화되었고, `Operations.razor`가 `Temp/operational_report.json` 고정 렌더 경로를 제공하며, Counter/Weather 기본 페이지는 삭제됨. 공개 배포본은 아직 이전 빌드가 남아 있을 수 있으므로 CI/CD 동기화가 필요함 |
| **담당 파일** | `src/dotnet/QuantEngine.Web/Components/Pages/Dashboard.razor`, `Operations.razor`, `NavMenu.razor` |
| **상태** | 완료 |

| 세부 WBS | 작업 | 성공 판단 데이터 |
|----------|------|------------------|
| 10.10.1 | Operational Report 페이지 — `Temp/operational_report.json` 고정 렌더 | 38 sections 인식 + PASS/DATA_MISSING 표시 (완료) |
| 10.10.2 | Dashboard 상태 페이지 — 데이터 비의존형 요약으로 단순화 | DB 실패 시에도 200 응답 (완료) |
| 10.10.3 | Counter.razor / Weather.razor 기본 페이지 삭제, NavMenu 정비 | 불필요 페이지 0건, NavMenu에 Dashboard/Operations만 표시 (완료) |
| 10.10.4 | 다크 모드 + 반응형 레이아웃 적용 | 브라우저 렌더링 정상 확인 (완료) |
| 10.10.5 | 배포 동기화 | `deploy-prod.yml`가 공개 라우트를 배포 후 검증하도록 구성됨 (완료) |

**성공 하네스 (데이터 기준)**:
```
검증: dotnet build src/dotnet/QuantEngine.Web/ → 오류 0
검증: Counter.razor, Weather.razor 파일 미존재
검증: 브라우저 접근 http://127.0.0.1:5080/operations → operational_report.json 기반 렌더링
검증: 배포 URL http://178.104.200.7/quant/ 에서 `/`와 `/operations`가 200 응답 + 로컬과 동일한 UI 기준을 만족
```

---

#### WBS-10.11 Razor Pages 개발 가이드라인 수립
| 항목 | 내용 |
|------|------|
| **작업** | [Temp/CLAUDE.md](file:///C:/Temp/data_feed/Temp/CLAUDE.md)의 API-First 아키텍처, 이중 토큰 인증, SignalR, Tabler UX 및 CSRF 방어 등 Razor Pages 관련 핵심 개발 지침을 [AGENTS.md](file:///C:/Temp/data_feed/AGENTS.md)에 차용/반영 |
| **현재 상태** | [Temp/CLAUDE.md](file:///C:/Temp/data_feed/Temp/CLAUDE.md) 분석 후 [AGENTS.md](file:///C:/Temp/data_feed/AGENTS.md)의 Section 5b로 이식 완료 |
| **담당 파일** | [docs/ROADMAP_WBS.md](file:///C:/Temp/data_feed/docs/ROADMAP_WBS.md), [AGENTS.md](file:///C:/Temp/data_feed/AGENTS.md) |
| **상태** | 완료 |

| 세부 WBS | 작업 | 성공 판단 데이터 |
|----------|------|------------------|
| 10.11.1 | CLAUDE.md 및 실제 적용된 Razor Pages 지침 핵심사항 추출 및 공식화 | [Temp/CLAUDE.md](file:///C:/Temp/data_feed/Temp/CLAUDE.md) 분석 내역 도출 |
| 10.11.2 | AGENTS.md에 Razor Pages 개발 규칙 5b 섹션 신설 및 적용 | [AGENTS.md](file:///C:/Temp/data_feed/AGENTS.md) 내 5b 섹션 코드 삽입 완료 |
| 10.11.3 | 스펙 검증 스크립트 실행을 통한 구성 유효성 검증 | `validate_specs.py` 무오류 통과 |

**성공 하네스 (데이터 기준)**:
```
검증: python tools/validate_specs.py → EXIT 0
검증: C:\Temp\data_feed\AGENTS.md 내에 '5b. Razor Pages 개발 규칙' 및 'IXxxBrowserClient', 'TokenRefreshHandler' 키워드 존재
```

---

#### WBS-10.12 Playwright 기반 Razor Pages 어드민 UI E2E 자동화

| 항목 | 내용 |
|------|------|
| **작업** | 대시보드 로그인, 데이터 수집 상세 조회, DB 테이블 변경 및 저장 폼 제출 등 핵심 UI 시나리오에 대해 Playwright를 이용한 무인 검증 스크립트 작성 및 CI 파이프라인 탑재 |
| **담당 파일** | `tests/e2e/RazorPagesSmokeTests.cs` (신규), `.gitea/workflows/ci.yml` |
| **성공 기준** | CI 파이프라인 실행 시 Playwright 테스트 스위트가 에러 없이 모두 PASS 완료 |
| **상태** | ⏳ 대기 (구조 설계 중) |

---

## 3. 완성도 로드맵 매트릭스

| WBS | 우선순위 | 난이도 | 선행조건 | 예상 기간 | 현재 완성도 |
|-----|---------|------|---------|---------|-----------|
| 1.1 소수주 병합 | 🔴 Critical | 낮음 | GAS 배포 | 완료 | **100%** ✅ |
| 1.2 총자산 재계산 | 🔴 Critical | 중간 | 없음 | 완료 | **100%** ✅ |
| 1.3 Time_Stop_Date | 🟠 High | 낮음 | 없음 | 완료 | **100%** ✅ |
| 1.4 Rule_Sell_Qty | 🟠 High | 중간 | 없음 | 완료 | **100%** ✅ |
| 1.5 Lifecycle 레지스트리 | 🟡 Medium | 낮음 | 없음 | 완료 | **100%** ✅ |
| 2.1 펀더멘털 피드 | 🔴 Critical | 높음 | yfinance | 완료 | **100%** ✅ |
| 2.2 US 주식 가격 | 🟠 High | 중간 | Yahoo API | 완료 | **100%** ✅ |
| 2.3 RS 신호 V2 | 🟠 High | 중간 | 없음 | 완료 | **100%** ✅ |
| 2.4 PEG_SCORE | 🟡 Medium | 낮음 | 2.1 완료 | 완료 | **100%** ✅ |
| 2.5 섹터 플로우 | 🟡 Medium | 중간 | 30일 데이터 | DATA_GATED | DATA_GATED |
| 3.1 리밸런싱 V1 배포 | 🔴 Critical | 낮음 | GAS 배포 | 완료 | **100%** ✅ |
| 3.2 리밸런싱 V2 | 🟡 Medium | 높음 | 3.1 안정화 | 완료 | **100%** ✅ |
| 3.3 주문 시뮬레이터 | 🟠 High | 중간 | 3.1 완료 | 완료 | **100%** ✅ |
| 3.4 MDD 가드 | 🟠 High | 중간 | 일별 기록 | 완료 | **100%** ✅ |
| 4.1 T+20 레저 | 🟡 Medium | 중간 | 30건 대기 | 2026-07-15 | DATA_GATED (0/30건) |
| 4.2 예측 정확도 | 🟡 Medium | 중간 | 4.1 완료 | 2026-08 | DATA_GATED |
| 4.3 알파 보정 | 🟢 Low | 높음 | 4.2 완료 | 2026-09 | DATA_GATED |
| 4.4 대시보드 | 🟡 Medium | 낮음 | 4.1 완료 | 완료 | **100%** ✅ |
| 5.1 CI/CD | 🟡 Medium | 중간 | Gitea 연결 | 완료 | **100%** ✅ |
| 5.2 GAS 자동 배포 | 🟢 Low | 낮음 | 5.1 완료 | 완료 | **100%** ✅ |
| 5.3 자율 실행 | 🟢 Low | 중간 | 5.1+5.2 완료 | 완료 | **100%** ✅ |
| 6 비기계적 매도전략·위성추천 (엔진+데이터+KIS+SQLite+자체평가) | 🔴 Critical | 높음 | 없음 | 완료 | **100%** ✅ (잔류위험: 0c절·WBS-7.7) |
| 6-잔여 공매도 잔고율 | 🟢 Low | 높음 | KRX 정책 | 차단 확정 | USER_ACTION 대기 |
| 7.1 캘리브레이션 실증 전환 | 🔴 Critical | 높음 | 30건↑ 표본 | 도구완료, 승격은 DATA_GATED | 0/191 CALIBRATED (도구 자동집계 + 중복id 버그 수정) |
| 7.2 T+5 지표 정합성 통일 | 🔴 Critical | 낮음 | 없음 | 완료 | **100%** ✅ (2026-06-21) |
| 7.3 GAS→Python 마이그레이션 | 🟠 High | 중간 | parity 테스트 | 완료 | 14/15 DONE, 1 KEEP_IN_GAS |
| 7.4 Deprecated 정리 | 🟠 High | 낮음 | 없음 | 완료 | **100%** ✅ (2026-06-21, alias 17건 제거) |
| 7.5 임시 폴백 비례화 | 🟡 Medium | 중간 | 없음 | 완료(OVERHANG만) | **100%** ✅ (2026-06-21, 나머지 2건은 정책결정 분리) |
| 7.6 슬리피지 실측 보정 | 🟡 Medium | 낮음 | 체결 5건↑ | 스캐폴딩완료, 비교는 DATA_GATED | **100%** ✅ (캡처 도구, 비교는 표본 대기) |
| 7.7 E2E 통합테스트 | 🟠 High | 중간 | 없음 | 완료 | **100%** ✅ (2026-06-21, 3 passed) |
| 7.8 ETF NAV 수집경로 확정 | 🟡 Medium | 높음 | KRX/KIND 정책 | 완료(재검토주기 설정) | **100%** ✅ (next_review: 2026-09-30) |
| 7.9 Synology 배포 검토 | 🟡 Medium | 중간 | 보안정책 결정 | 부분완료 | **부분완료** (외부 접근 POC 가이드 + Basic Auth 게이트 추가, live verification pending) |
| 7.10 어드민 테이블 그리드(Tabler) | 🟢 Low | 낮음 | 없음 | 완료 | **100%** ✅ (2026-06-21, 8 passed) |
| 7.11 spec-코드 동기화 게이트 | 🔴 Critical | 중간 | 없음 | 완료(2차 확장) | **100%** ✅ (2026-06-22, 20/160 태깅 12.5%, 88 passed) |
| 10.1 기반 결함 수정 | 🔴 Critical | 낮음 | 없음 | 30분 | **100%** ✅ (2026-06-29) |
| 10.2 테스트 인프라 | 🔴 Critical | 중간 | 10.1 | 2시간 | **100%** ✅ (2026-06-29) |
| 10.3 Domain Parity | 🔴 Critical | 중간 | 10.2 | 3시간 | **100%** ✅ (2026-06-29) |
| 10.4 공식 엔진 포팅 | 🔴 Critical | 높음 | 10.3 | 8시간 | **100%** ✅ (2026-06-29) |
| 10.5 하네스 주입 포팅 | 🟠 High | 높음 | 10.4 | 6시간 | **100%** ✅ (2026-06-29) |
| 10.6 파이프라인 오케스트레이터 | 🟠 High | 중간 | 10.5 | 4시간 | **100%** ✅ (2026-06-29) |
| 10.7 Application 서비스 | 🟠 High | 중간 | 10.1 | 3시간 | 0% |
| 10.8 데이터 수집 오케스트레이터 | 🟡 Medium | 중간 | 10.7 | 4시간 | 0% |
| 10.9 보안 강화 | 🟠 High | 낮음 | 10.1 | 1시간 | 0% |
| 10.10 Razor Pages 어드민 대시보드 고도화 | 🟡 Medium | 중간 | 10.7 | 4시간 | 0% |
| 10.11 Razor Pages 개발 지침 수립 | 🟢 Low | 낮음 | 없음 | 1시간 | **100%** ✅ (2026-06-29) |

---

## 4. 엔진 완성도 KPI (데이터 기반 측정)

```yaml
# 현재 상태 (2026-06-15 기준) vs 목표

데이터 품질:
  NULL 컬럼 수:        ≤10개 →  목표: 10개 이하 ✅ (WBS-2.1~2.4 완료)
  Weight_Pct 정확도:  99%   →  목표: 99% ✅ (소수주 병합 완료)
  총자산 오차:         0.0%  →  목표: 2.0% 이하 ✅ (실시간 재계산 완료)
  sector_universe 갱신: 13섹터 112행 ✅ (Naver ETF 스크래핑, source_url/asof 완비)

공식 레지스트리:
  lifecycle 등록률:    100%  →  목표: 100% ✅ (269개 등록)
  황금 테스트 커버리지: 100% →  목표: 100% ✅
  공식 버전 충돌:      없음   →  유지 ✅

신호 품질:
  RS 신호 커버리지:    100%  →  목표: 100% ✅ (WBS-2.3 완료)
  Flow_Credit 커버리지: 100% (data_feed 11/11) → 목표: 100% (WBS-2.5 DATA_GATED)
  PEG_Gate 커버리지:   75%   →  목표: 80% (WBS-2.4 완료, 음수성장 2종목 제외)
  섹터 유니버스 갱신 gate: PASS ✅ (naver_rows=100, representative_rows=12)

리밸런싱 엔진:
  레짐 소스 정확도:    100%  →  유지 ✅ (macro.REGIME_PRELIM 최우선)
  밴드 내 유지 여부:   PASS  →  유지 ✅
  FORCE 주문 자동화:   100%  →  유지 ✅

성과:
  T+20 레저 건수:      0건   →  목표: 30건 DATA_GATED
  예측 적중률(T+1):    52.94% (sample=68, decisive=67.92%) — as_of 2026-06-21
  예측 적중률(T+5):    DATA_GATED (sample=0, as_of 2026-06-21) — 0c절 참조, 과거 54.76%/35.86% 캐시값 모두 폐기
  알파 (vs KOSPI):     미측정 →  목표: >0%p/분기
  honest_proof_score: 50.95 →  목표: ≥70 (T+20 30건 → 70.95 자동 달성 예상)

캘리브레이션 품질 (신규, WBS-7.1):
  calibrated_threshold_count: 0/190 (0%)   →  목표: ≥10건 (1차), ≥30건 (2차)
  provisional_threshold_count: 8/190 (4.2%) →  목표: ≥30건
  expert_prior_unvalidated_pct: 95.8% (SPEC_DERIVED+EXPERT_PRIOR) → 목표: ≤70%

보완·고도화 (신규, Phase 7):
  gas_python_migration_pct: 14/14 완료 (100%, KEEP_IN_GAS 1건 제외)
  deprecated_alias_remaining: 0건 (데드라인 2026-06-30) → 목표: 0건
  e2e_integration_test_count: 3건 → 목표: ≥1건 (KIS수집→스냅샷→정성매도 체인)

자동화:
  run_all 성공률:      98단계 DAG PASS →  목표: ≥95% ✅ (step_count=98, wave_0~9)
  CI/CD 커버리지:      100%   →  목표: 100% ✅ (Synology act_runner 온라인, 4게이트 PASS)
  수동 개입 횟수:      매일   →  목표: ≤1회/주 (setupDailyRunAllTrigger 설정 후)
```

---

## 5. 다음 스프린트 실행 목록 (즉시 착수 가능)

### Sprint-1 (이번 주): 기반 경화 완결 (완료)

```
[x] WBS-1.1: GAS 배포 후 Weight_Pct 검증 (005930 ≥ 40%)
[x] WBS-1.2: totalAssetKrw_ 2-pass 재계산 구현
[x] WBS-1.3: Time_Stop_Date 자동 산출 (entry_date + 60일)
[x] WBS-1.4: Rule_Sell_Qty = floor(qty × Sell_Ratio_Pct / 100) 구현
[x] WBS-3.1: rebalance 시트 GAS 실행 확인 (timestamp 확인)
```

### Sprint-2 (2주): 신호 완성 (완료)

```
[x] WBS-2.3: RS_Verdict_V1_Raw, RS_Line_20D_Slope 로직 구현
[x] WBS-2.2: US 주식 가격 settings 수동입력 → 자동 Weight_Pct 연동
[x] WBS-3.3: 주문 시뮬레이터 tick 정규화 완성
[x] WBS-1.5: lifecycle 레지스트리 149개 중 상위 50개 이관
```

### Sprint-3 (4주): 펀더멘털 + 성과 기반 구축 (완료)

```
[x] WBS-2.1: DART 재무데이터 수집 파이프라인 구현 (tools/ingest_fundamental_raw.py yfinance 개편)
[x] WBS-3.2: 리밸런싱 V2 신호 가중 목표배분 (signal_weighted_ss001_v1 PASS)
[x] WBS-3.4: MDD 일별 기록 테이블 생성 (logDailyAssetHistory_ daily_history 시트 자동 생성)
[x] WBS-4.1: T+20 레저 구조 구축 (tools/build_realized_performance_v1.py 스키마 완성; 데이터 누적 중)
[x] WBS-5.1: Gitea CI/CD 기본 파이프라인 (.gitea/workflows/ci.yml 구축)
```

### Sprint-4 (DATA_GATED): 성과 인텔리전스 + 자동화 완결

```
[ ] WBS-4.1: T+20 레저 첫 30건 달성 (2026-07-15) — 거래 데이터 누적 필요
[ ] WBS-4.2: 예측 정확도 하네스 (WBS-4.1 완료 후)
[ ] WBS-4.3: 알파 보정 루프 (WBS-4.2 완료 후)
[x] WBS-2.4: PEG_SCORE_V1 실데이터 검증 완료 (ingest_fundamental_raw.py peg_ratio/peg_gate 추가, 비ETF 75% 커버)
[x] WBS-4.4: 성과 모니터링 대시보드 완성 (updateEvaluationDashboard_() GAS 함수 + run_all Step-8)
[x] WBS-5.2: GAS 자동 배포 스크립트 (tools/deploy_gas.py -- dry-run PASS 17 files)
[x] WBS-5.3: 타이머 트리거 설정 (gdf_06_rebalance.gs setupDailyRunAllTrigger() 추가)
```

### Sprint-5 (2026-06-15): 섹터 유니버스 월간 갱신 파이프라인 (완료 — PR #62)

```
[x] 섹터 유니버스 Naver 스크래핑 도구 구현
    tools/update_sector_universe_from_naver.py (616줄)
    → Naver ETF 페이지 스크래핑 → Source_URL/Source_AsOf 자동 채움
    → --apply 플래그로 GatherTradingData.xlsx 원본 반영
    → 결과: 13섹터 112행, naver_rows=100, representative_rows=12

[x] 섹터 유니버스 갱신 하네스 구현
    tools/validate_sector_universe_monthly_refresh_v1.py (173줄)
    → gate=PASS/WARN/FAIL 판정 (PASS 확인)
    → missing_source_url=0, stale_rows=0, template_rows=0

[x] 섹터 유니버스 리프레시 감사 모듈
    src/quant_engine/sector_universe_refresh.py (296줄)

[x] GAS 라이브러리 강화 (src/gas/core/gas_lib.gs +429줄)

[x] 섹터 리포트 & 대표종목 모니터 고도화
    etf_representative_monitor.py, src/dotnet/QuantEngine.Tools
    update_workbook_sector_insights.py (sector_universe_refresh_audit 시트 포함)

[x] JSON 직렬화 안정화 (convert_xlsx_to_json.py — datetime/NaN 예외 처리)

[x] sector_universe GatherTradingData.xlsx --apply 반영 완료 (2026-06-15)
```

**월간 운영 절차 (매월 1회):**
```bash
python tools/update_sector_universe_from_naver.py --limit 10          # 드라이런
python tools/validate_sector_universe_monthly_refresh_v1.py \
  --xlsx outputs/sector_universe_refresh/GatherTradingData_sector_universe.xlsx
python tools/update_sector_universe_from_naver.py --limit 10 --apply  # 원본 반영
```

---

### Sprint-6 (비판적 보완 스프린트, 2026-06-21 비판적 리뷰 대응)

```
[x] WBS-7.2: T+5/예측정확도 지표 단일 진실원천 통일 (2026-06-21 완료)
[x] WBS-7.4: Deprecated 별칭 17건 정리 — 2026-06-30 데드라인 (2026-06-21 완료, validate_specs.py PASS)
[x] WBS-7.1: 캘리브레이션 레지스트리 건강도 자동집계 도구 + 중복id 버그 수정 (2026-06-21, PROVISIONAL 전환 자체는 실데이터 대기)
[x] WBS-7.3: GAS→Python 마이그레이션 재검토 완료(14건 DONE, 1건 KEEP_IN_GAS, TODO 0건, 2026-06-22) — renderer-only 예외만 유지
[x] WBS-7.7: KIS수집→스냅샷→정성매도 E2E 통합 테스트 작성 (2026-06-21 완료, 3 passed)
[x] WBS-7.5: OVERHANG_PRESSURE_V1 폴백 비례화 (2026-06-21 완료, avg_volume_5d 비례식 + EXPERT_PRIOR 등록)
[x] WBS-7.6: 슬리피지 실측 캡처 스캐폴딩 구축 완료 (2026-06-21, 비교 자체는 체결 5건 누적 대기)
[x] WBS-7.8: ETF NAV 수집경로 재검토 + 공매도 잔고율 운영절차 문서화 (2026-06-21 완료)
[x] WBS-7.9: KIS 수집 예외 처리 & Fallback 고도화 (2026-06-22 완료, KIS 실패 시 Naver/Seed JSON 폴백 복원력 적용)
[x] WBS-7.10: GAS 배포 전 Thin Adapter 오염 사전 검출 연동 (2026-06-22 완료, deploy_gas.py에 audit/validate pre-deploy hook 탑재)
[x] WBS-7.11: PostgreSQL 다형적 스토어 계약 레이어 구현 (2026-06-22 완료, sqlite/psycopg2 쿼리 플레이스홀더 분기 및 트랜잭션 동적 처리 반영)
[x] WBS-7.12: 스톱로스 정책(stop_loss_gate) Parity 단위 테스트 구축 (2026-06-22 완료, ATR 변동성 배수 및 상대약세 트리거 동등성 실증 완료)
[x] WBS-7.13: 추격매수 리스크(late_chase_risk_score) Parity 단위 테스트 구축 (2026-06-22 완료, 이평선 이격도 및 거래량 미확인 돌파 동등성 실증 완료)
[x] WBS-7.14: 결정 라우팅(routing_decision_v1) Parity 단위 테스트 구축 (2026-06-22 완료, 장중 락 다운그레이드 및 MRG 이격 차단 동등성 실증 완료)
[x] P3 adoption plan validator: `tools/validate_v8_9_p3_adoption_plan_v1.py` (2026-06-22 완료, P3-A~P3-E + decision_flow + manifest 배선 검증 PASS)
[x] HONEST-V1 source-of-truth cleanup: `tools/build_honest_performance_guard_v1.py` (2026-06-22 완료, T+5 stale hardcode 제거 및 `prediction_accuracy_harness_v2.json` 우선 참조)
[x] WBS-4.1/WBS-7.1 status snapshot: `tools/build_wbs_4_1_7_1_status_v1.py` (2026-06-22 완료, live_t20=0/30, calibrated=0/190, top provisional candidates captured)
[x] Packaging reference repair: `tools/build_packaged_artifact_placeholders_v1.py` + `tools/validate_packaged_artifact_references_v1.py` (2026-06-22 완료, active manifest Temp refs 14건 DATA_MISSING 계약 생성 및 strict PASS)
[x] Release/package stabilization: `src/quant_engine/prepare_upload_zip.py`, `src/quant_engine/orchestration_harness_v1.py`, `src/quant_engine/generate_models_from_schema.py` (2026-06-22 완료, Python 3.13 런처 고정 + schema/model parity + upload ZIP 정책 PASS)

### Repo Cleanup Notes

- Commit set: `docs/ROADMAP_WBS.md`, `spec/calibration_registry.yaml`, `src/quant_engine/generate_models_from_schema.py`, `src/quant_engine/orchestration_harness_v1.py`, `src/quant_engine/prepare_upload_zip.py`, `tools/build_honest_performance_guard_v1.py`, `tools/validate_packaged_artifact_references_v1.py`, `tools/build_packaged_artifact_placeholders_v1.py`, `tools/build_wbs_4_1_7_1_status_v1.py`, `tools/validate_v8_9_p3_adoption_plan_v1.py`
- Archive candidates: `suggest/quant_engine_*.yaml` and other planning drafts already superseded by the active roadmap
- Keep as active assets: `gas_*` runtime sources, `tests/parity/test_routing_decision_parity.py`
- GS cleanup status: `gas_lib.gs`, `gas_apex_alpha_watch.gs`, `gas_apex_runtime_core.gs`, `gas_harness_rows.gs`, `gas_report.gs`, `gas_event_calendar.gs` remain active deployment assets; no deletion scheduled for current release train.
- Document search exclusion: `tools/build_document_search_index_v1.py` + `tools/validate_document_search_exclusion_v1.py` (2026-06-22 완료, `docs/archive/`, `suggest/`, `artifacts/archive/` 색인 제외 PASS)
```

### WBS-8.6 잔여 finding inventory

`governance/gas_logic_migration_ledger_v1.yaml` 기준 현재 잔여는 1건이다.

| status | count | ids | 해석 |
|--------|------:|-----|------|
| `DONE` | 14 | F01, F02, F03, F04, F05, F06, F07, F09, F10, F11, F12, F13, F14, F15 | parity 또는 레지스트리 정정이 끝난 finding |
| `KEEP_IN_GAS` | 1 | F08 | display/rendering 책임으로 GAS에 남김 (`spec/56_renderer_copy_only_contract.yaml`, `spec/40_final_decision_packet_contract.yaml`) |
| `TODO` | 0 | - | 현 시점 기준 미착수 finding 없음 |

#### 잔여 의미

- `.gs → Python`은 숫자로 보면 거의 끝났지만, 완료는 “파일 수 감소”가 아니라 “남은 1건이 렌더링 전용인지 검증된 상태”다.
- `KEEP_IN_GAS`가 남아 있으므로, GAS 파일이 존재한다는 사실만으로는 미완료를 뜻하지 않는다.
- F08은 renderer copy-only 계약(`spec/56_renderer_copy_only_contract.yaml`)과 final packet contract(`spec/40_final_decision_packet_contract.yaml`)에 의해 렌더링 문자열로만 취급된다.
- 반대로 `TODO=0`이므로, 현재 미해결 작업은 구현 미착수가 아니라 정책 확정과 증빙 정합성이다.

---

## 6. 원본 변환 트랙 WBS

### 실행 요약

| 트랙 | 판정 | 핵심 근거 |
|------|------|----------|
| `.gs → Python` | 완료 ✅ | F08만 `KEEP_IN_GAS`, 나머지 14건 `DONE`, parity 및 thin-adapter 게이트 PASS |
| `xlsx → sqlite` | 완료 ✅ | 수집/스냅샷 검증 PASS, `8.2.11` 종료 선언 완료 |
| `KIS Open API` 전환 | 완료 ✅ | KIS 우선 경로 및 credentials 검증 PASS, `8.8.6` 종료 선언 완료 |
| 플랫폼 전환 검증 | PASS | `python tools/validate_platform_transition_wbs_v1.py` PASS |

### 남은 blocker

| 항목 | blocker |
|------|---------|
| `.gs → Python` | 없음 (종료됨) |
| `xlsx → sqlite` | 없음 (종료됨) |
| `KIS Open API` 전환 | 없음 (종료됨) |

### 현황 요약

| 트랙 | 현재 상태 | 병목 | 완료 조건 |
|------|-----------|------|----------|
| `.gs → Python` | 완료 ✅ | 없음 | `TODO` finding 0, parity PASS, rendering-only 잔여만 허용 |
| `xlsx → sqlite` | 완료 ✅ | 없음 | workflow/validator가 SQLite/JSON 우선 사용, xlsx는 seed-prep 보조 |

### WBS-8.1 `.gs → Python` 변환 트랙

#### parity / finding 1:1 매핑

| parity test | coverage finding | 판정 기준 |
|-------------|------------------|----------|
| `tests/parity/test_stop_loss_policy_parity.py` | F02, F03, F04, F05, F06, F07, F11, F15 | legacy parity harness. price basis, action routing, score, late-chase gate parity PASS |
| `tests/parity/test_distribution_risk_parity.py` | F12, F13 | distribution risk score / formula mapping parity PASS |
| `tests/parity/test_late_chase_risk_parity.py` | F14 | late-chase risk scoring parity PASS |
| `tests/parity/test_routing_decision_parity.py` | F10, F11 | legacy routing harness. stop-breach / heat / cash-floor regression PASS |
| `tests/parity/test_score_parity_v1.py` | F07 | entry/exit timing score and action parity PASS |
| `tests/parity/test_routing_gate_parity_v1.py` | F10, F11, F15 | stop-breach, heat, cash-floor gate parity PASS |
| `tests/parity/test_price_qty_parity_v1.py` | F02, F03, F04, F05, F06 | price/qty parity PASS |

#### finding 판정표

| finding | status | 완료 판정 근거 |
|---------|--------|----------------|
| F01 | `DONE` | `spec/calibration_registry.yaml`에 id=SP_TAKE_PROFIT(gs_location=gas_data_feed.gs:186, 'P5-T01 wave1'에서 등록)으로 등록되어 있음을 재확인. |
| F02 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py::test_price_basis_f02_f06_parity` PASS; `tests/parity/test_price_qty_parity_v1.py::TestPriceQtyParityV1::test_take_profit_tier1_and_tier2_price_basis_parity` PASS |
| F03 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py::test_price_basis_f02_f06_parity` PASS; `tests/parity/test_price_qty_parity_v1.py::TestPriceQtyParityV1::test_take_profit_tier1_and_tier2_price_basis_parity` PASS |
| F04 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py::test_price_basis_f02_f06_parity` PASS; `tests/parity/test_price_qty_parity_v1.py::TestPriceQtyParityV1::test_take_profit_tier1_and_tier2_price_basis_parity` PASS |
| F05 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py::test_action_routing_f05_parity` PASS; `tests/parity/test_price_qty_parity_v1.py::TestPriceQtyParityV1::test_take_profit_tier1_and_tier2_price_basis_parity` PASS |
| F06 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py::test_price_basis_f02_f06_parity` PASS; `tests/parity/test_price_qty_parity_v1.py::TestPriceQtyParityV1::test_take_profit_tier1_and_tier2_price_basis_parity` PASS |
| F07 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py::test_score_calculation_f07_parity` PASS; `tests/parity/test_score_parity_v1.py` PASS |
| F08 | `KEEP_IN_GAS` | `spec/56_renderer_copy_only_contract.yaml` + `spec/40_final_decision_packet_contract.yaml`로 렌더링 전용 유지 |
| F09 | `DONE` | `spec/calibration_registry.yaml`에 id=TAKE_PROFIT_BASE(gs_location=gas_data_feed.gs:2164)로 등록되어 있음을 재확인. |
| F10 | `DONE` | `tests/parity/test_routing_decision_parity.py::test_heat_gate_and_mr_gating` PASS; `tests/parity/test_routing_gate_parity_v1.py` PASS |
| F11 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py::test_stop_loss_gate_decision_routing_f11_parity` PASS; `tests/parity/test_routing_gate_parity_v1.py` PASS |
| F12 | `DONE` | `tests/parity/test_distribution_risk_parity.py::test_distribution_risk_parity_scenarios` PASS |
| F13 | `DONE` | `tests/parity/test_distribution_risk_parity.py::test_distribution_risk_parity_scenarios` PASS |
| F14 | `DONE` | `tests/parity/test_late_chase_risk_parity.py::test_close_vs_ma20_ranges_parity` PASS |
| F15 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py::test_late_chase_gate_f15_parity` PASS; `tests/parity/test_routing_gate_parity_v1.py` PASS |

#### finding 종료 규칙

- `DONE`: parity test 또는 registry 정정이 있고, 해당 finding이 더 이상 GAS 삭제/이관의 blocker가 아니다.
- `KEEP_IN_GAS`: rendering 또는 platform stub처럼 GAS adapter 책임에 남는 경우만 허용한다.
- `TODO`: 현재 ledger 기준 0건이어야 한다.
- `BLOCKER`: 전용 parity 테스트가 없는 상태에서 migration_action을 삭제/이관으로 승격할 수 없다.

#### migration_action 기준 BLOCKER 연결

| migration_action | 관련 finding | 현재 판정 | 완료 조건 | 증빙 |
|------------------|--------------|-----------|----------|------|
| `REGISTER_SP_TAKE_PROFIT` | F01 | `DONE` | registry stale 정정만 남음 | `spec/calibration_registry.yaml` |
| `REGISTER_TAKE_PROFIT_BASE` | F09 | `DONE` | registry stale 정정만 남음 | `spec/calibration_registry.yaml` |
| `MIGRATE_PRICEBASIS_TO_PYTHON` | F02, F03, F04, F06 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py`에 `test_price_basis_f02_f06_parity`를 추가해 가격 기준 및 가격 산출 로직에 대해 GAS와의 동등성을 입증 및 포팅 종결함 | `tests/parity/test_stop_loss_policy_parity.py::test_price_basis_f02_f06_parity` |
| `MIGRATE_SCORE_CALCULATION` | F07 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py`에 `test_score_calculation_f07_parity`를 추가해 익절 조건 만족 시 매도 순위 점수 가산 로직의 동등성을 입증 및 포팅 종결함 | `tests/parity/test_stop_loss_policy_parity.py::test_score_calculation_f07_parity` |
| `MIGRATE_DECISIONS_ROUTING` | F05, F10 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py`와 `tests/parity/test_routing_decision_parity.py`로 stop/heat/cash-floor 중심의 routing 동등성을 검증 완료함 | `tests/parity/test_stop_loss_policy_parity.py::test_action_routing_f05_parity`, `tests/parity/test_routing_decision_parity.py::test_heat_gate_and_mr_gating` |
| `MIGRATE_STOP_BREACH_DECISION` | F11 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py`를 확장하여 F11 stop_loss_gate 의사결정의 Python 동등성을 검증하고 Parity 테스트를 통과함 | `tests/parity/test_stop_loss_policy_parity.py::test_stop_loss_gate_decision_routing_f11_parity` |
| `DELETE_DISTRIBUTION_RISK_GAS` | F12, F13 | `DONE` | `tests/parity/test_distribution_risk_parity.py`를 작성하여 GAS calcDistributionRiskRow_의 10가지 세부 팩터 조건과 Python build_distribution_risk_score_v2.py의 계산 일치를 검증 완료함. parity가 완벽히 입증되었으므로 DONE 처리 | `tests/parity/test_distribution_risk_parity.py::test_distribution_risk_parity_scenarios` |
| `DELETE_LATE_CHASE_RISK_GAS` | F14 | `DONE` | `tests/parity/test_late_chase_risk_parity.py`를 신규 구축하여 이평선 괴리도/DART 공시/분산 차단/거래량 미확인 돌파 등 6가지 late chase 가산 규칙에 대한 Python 계산 정합성 검증 완료 | `tests/parity/test_late_chase_risk_parity.py::test_close_vs_ma20_ranges_parity` |
| `MIGRATE_LATE_CHASE_GATE` | F15 | `DONE` | `tests/parity/test_stop_loss_policy_parity.py`를 확장하여 F15 late_chase_gate 의사결정의 Python 동등성을 검증하고 Parity 테스트를 통과함 | `tests/parity/test_stop_loss_policy_parity.py::test_late_chase_gate_f15_parity` |
| `DISPLAY_TEXT_PASSTHROUGH` | F08 | `KEEP_IN_GAS` | display_text는 pure narrative/rendering output이므로 GAS adapter에 렌더링 책임으로 남김 | `spec/56_renderer_copy_only_contract.yaml`, `spec/40_final_decision_packet_contract.yaml` |

| 세부 WBS | 작업 | 데이터 기반 완료 정의 | 현재 상태 |
|----------|------|-------------------|----------|
| 8.1.1 | 잔여 GAS finding 재분류 | `governance/gas_logic_migration_ledger_v1.yaml`에서 `status: TODO` = 0, `KEEP_IN_GAS`는 렌더링/플랫폼 스텁만 허용 | 완료 |
| 8.1.2 | parity 테스트 맵핑 | `tests/parity/test_stop_loss_policy_parity.py`, `tests/parity/test_distribution_risk_parity.py`, `tests/parity/test_late_chase_risk_parity.py`, `tests/parity/test_routing_decision_parity.py`가 ledger finding과 대응 | 완료 |
| 8.1.3 | parity PASS 증빙 | 위 parity 테스트가 로컬 검증에서 PASS이고 `Temp/gas_thin_adapter_validation_v1.json`이 `gate=PASS`를 유지 | 완료 |
| 8.1.4 | thin-adapter 정제 | `tools/validate_gas_thin_adapter_v1.py`의 `forbidden_gas_business_logic_count`가 정책 임계치 이내 | 완료 |
| 8.1.5 | GAS 배포 경로 정리 | `tools/deploy_gas.py`가 업로드/배포/검증만 수행하고 투자 판단 로직을 포함하지 않음 | 완료 |
| 8.1.6 | rendering-only 잔여 고정 | `F08`이 `spec/56_renderer_copy_only_contract.yaml`과 `spec/40_final_decision_packet_contract.yaml`로만 설명됨 | 완료 |
| 8.1.7 | 종료 선언 | `gas_*` 중 렌더링/배포 스텁 외의 결정 로직이 Python canonical로 귀속 | 완료 |

### WBS-8.2 `xlsx → sqlite` 변환 트랙

#### 현재 판정

| 항목 | 판정 | 근거 |
|------|------|------|
| `.gs → Python` | 부분 완료 | `WBS-8.1`의 `TODO`는 0, `KEEP_IN_GAS`는 F08 בלבד |
| `xlsx → sqlite` | 부분 완료 | `WBS-8.2.11` 종료 선언이 아직 진행 중 |
| `KIS Open API` 전환 | 진행 중 | `WBS-8.8.6` 전환 종료 선언이 미착수 |
| 플랫폼 전환 검증 | PASS | `python tools/validate_platform_transition_wbs_v1.py` PASS |

#### 목표 요약

- 최우선 핵심 키워드: **마이그레이션 완료 후 코드가 문제 없음을 데이터로 증빙**
- 그 다음 핵심 작업: **기존 Naver 스크래핑을 KIS Open API 우선 경로로 전환**
- 완료 판정은 구현 감상이 아니라 `YAML + 코드 + 데이터 실체 + 검증`의 동시 충족으로만 한다.

#### 재생성 명령

```powershell
python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db Temp/test_kis_data_collection.db --output-json Temp/test_kis_data_collection.json --kis-account real --no-live-kis --no-naver
python tools/validate_platform_transition_wbs_v1.py
python tools/validate_snapshot_admin_web_v1.py
```

#### 증빙 파일 체크리스트

| 산출물 | 필수 필드/테이블 | 완료 기준 |
|--------|------------------|----------|
| `Temp/test_kis_data_collection.json` | `status`, `row_count`, `source_counts`, `started_at`, `finished_at`, `input_json`, `sqlite_db`, `rows[]` | `status=PASS`, `row_count>0`, `source_counts.gathertradingdata_json>0` |
| `Temp/test_kis_data_collection.db` | `collection_runs`, `collection_snapshots`, `collection_source_errors` | 3개 테이블 모두 존재하고 `collection_runs>0`, `collection_snapshots>0`, `collection_source_errors=0` |
| `Temp/snapshot_admin_web_validation.db` | `account_snapshot`, `settings`, `workspace_approval_v2`, `workspace_change_log`, `workspace_lock` | 테이블 5개가 존재하고 `single_workspace_sqlite=true`, `settings_and_snapshot_share_db=true` |
| `Temp/snapshot_admin_approval_packet_v1.json` | `approval_packet_path`, `settings_rows`, `account_snapshot_rows`, `summary`, `version` | approval packet 검증 산출물로 존재하고 snapshot admin smoke validator PASS |
| `GatherTradingData.json` | seed input | runbook과 workflow가 이 파일을 1차 seed로 사용 |

#### 재생성 판정

- `Temp/test_kis_data_collection.json`는 `status=PASS`와 `row_count>0`를 만족해야 한다.
- `Temp/test_kis_data_collection.json`는 `source_counts.gathertradingdata_json>0`를 만족해야 한다.
- `Temp/test_kis_data_collection.db`는 `collection_runs>0`, `collection_snapshots>0`, `collection_source_errors=0`를 만족해야 한다.
- `Temp/snapshot_admin_web_validation.db`는 5개 핵심 테이블이 모두 존재해야 한다.
- `Temp/snapshot_admin_approval_packet_v1.json`은 snapshot admin 검증의 승인 패킷으로 함께 존재해야 한다.
- 위 세 산출물은 `python tools/validate_platform_transition_wbs_v1.py`와 `python tools/validate_snapshot_admin_web_v1.py` PASS로 함께 판정한다.

#### WBS-8.2 성공 목표

| 목표 | 성공 판정 기준 | 기대 결과값 | 데이터 증빙 |
|------|----------------|-------------|-------------|
| M1 | `xlsx`가 직접 1차 입력이 아님 | `GatherTradingData.json` 우선, `GatherTradingData.xlsx` 보조 | `docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md`, `.gitea/workflows/kis_data_collection.yml` |
| M2 | 수집 결과가 SQLite에 적재됨 | `collection_runs>=1`, `collection_snapshots>=1`, `collection_source_errors=0` | `Temp/test_kis_data_collection.db`, `Temp/test_kis_data_collection.json` |
| M3 | snapshot admin이 SQLite 단일 워크스페이스를 사용함 | `single_workspace_sqlite=true`, `settings_and_snapshot_share_db=true`, `collector_separate_db=true` | `Temp/snapshot_admin_web_validation.db`, `tools/validate_snapshot_admin_web_v1.py` |
| M4 | 전환 검증이 재현 가능함 | `python tools/validate_platform_transition_wbs_v1.py` PASS | `Temp/platform_transition_wbs_v1.json` |
| M5 | 검증 후 코드 무결성이 유지됨 | `gas_thin_adapter_gate=PASS`, `sqlite_schema_parity=PASS` | `Temp/gas_thin_adapter_validation_v1.json`, `tools/validate_gas_thin_adapter_v1.py` |

| 세부 WBS | 작업 | 데이터 기반 완료 정의 | 현재 상태 |
|----------|------|-------------------|----------|
| 8.2.1 | 수집 파이프라인 SQLite 1차화 | `.gitea/workflows/kis_data_collection.yml`이 xlsx를 직접 1차 입력으로 요구하지 않고 SQLite 적재를 수행 | 완료 |
| 8.2.2 | 어드민 편집기 SQLite 1차화 | `tools/validate_snapshot_admin_web_v1.py`가 `single_workspace_sqlite=true`, `settings_and_snapshot_share_db=true`를 PASS | 완료 |
| 8.2.3 | JSON 재생성성 | `Temp/test_kis_data_collection.json`이 `GatherTradingData.json` seed로 재생성되고 `status=PASS`를 유지 | 완료 |
| 8.2.4 | DB 재생성성 | `Temp/test_kis_data_collection.db`가 동일 seed 계열로 재생성되고 핵심 테이블 3개를 유지 | 완료 |
| 8.2.5 | snapshot DB 재생성성 | `Temp/snapshot_admin_web_validation.db`가 `GatherTradingData.json` seed로 재현되고 5개 핵심 테이블을 유지 | 완료 |
| 8.2.6 | approval packet 재현성 | `Temp/snapshot_admin_approval_packet_v1.json`이 snapshot admin validator와 함께 재생성 가능 | 완료 |
| 8.2.7 | xlsx 역할 축소 | `GatherTradingData.xlsx`는 `docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md`에 적힌 보조 자산 역할만 수행 | 완료 |
| 8.2.8 | xlsx 직접 의존 제거 | workflow와 validator에서 `GatherTradingData.xlsx`를 직접 1차 입력으로 요구하지 않음 | 완료 |
| 8.2.9 | 증빙 파일 세트 고정 | 위 4개 Temp 산출물과 `python tools/validate_platform_transition_wbs_v1.py` PASS가 함께 존재 | 완료 |
| 8.2.10 | 재생성 절차 고정 | runbook에 `GatherTradingData.json` 우선, 이후 `tools/convert_xlsx_to_json.py`와 `tools/run_kis_data_collection_v1.py` 순서가 명시됨 | 완료 |
| 8.2.11 | 종료 선언 | operator guide와 workflow가 SQLite/JSON 우선을 유지 | 완료 |

#### 항목별 재생성 명령

| 항목 | 명령 |
|------|------|
| 8.2.3 JSON 재생성성 | `python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db Temp/test_kis_data_collection.db --output-json Temp/test_kis_data_collection.json --kis-account real --no-live-kis` |
| 8.2.4 DB 재생성성 | `python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db Temp/test_kis_data_collection.db --output-json Temp/test_kis_data_collection.json --kis-account real --no-live-kis` |
| 8.2.5 snapshot DB 재생성성 | `python tools/validate_snapshot_admin_web_v1.py` |
| 8.2.6 approval packet 재현성 | `python tools/validate_snapshot_admin_web_v1.py` |

#### 파일별 해석

- `GatherTradingData.json`: 수집 seed 입력이다.
- `Temp/test_kis_data_collection.json`: `run_kis_data_collection_v1.py`의 출력 요약이다.
- `Temp/test_kis_data_collection.db`: 같은 실행에서 생성되는 SQLite 수집 DB다.
- `Temp/snapshot_admin_web_validation.db`: snapshot admin web 검증이 사용하는 SQLite 워크스페이스 DB다.
- `Temp/snapshot_admin_approval_packet_v1.json`: snapshot admin 승인 패킷이다.

### WBS-8.8 Naver 스크래핑 → KIS Open API 전환 트랙

#### 목표 요약

- 핵심 키워드: **Naver 스크래핑 의존 축소 후 KIS Open API 우선화**
- 이 트랙은 시세/수급/호가 계열의 read-only 수집 경로를 KIS로 이동시키는 작업이다.
- Naver는 폴백 또는 보조 탐색으로만 남기고, 주요 운영 경로는 KIS API 결과로 판정한다.
- 운영 우선순위: `KIS 우선` > `Naver 폴백` > `seed JSON replay`.
- 저장 우선순위: `SQLite 우선` > `Temp JSON` > `xlsx archive`.

#### 성공 목표

| 목표 | 성공 판정 기준 | 기대 결과값 | 데이터 증빙 |
|------|----------------|-------------|-------------|
| K1 | KIS read-only 경로가 기본 경로임 | `KIS_APP_KEY`, `KIS_APP_SECRET` 기반 수집이 먼저 시도되고, KIS 성공 시 source_priority 선두에 위치함 | `.gitea/workflows/kis_data_collection.yml`, `tools/validate_kis_api_credentials_v1.py`, `Temp/test_kis_data_collection.json` |
| K2 | Naver 의존 축소 | 핵심 운영 입력에서 Naver가 보조/폴백으로만 남고, KIS 실패 시에만 선택됨 | `tools/build_qualitative_sell_inputs_v1.py`, `tools/fetch_naver_market_data_v1.py` |
| K3 | 결과값이 SQLite에 기록됨 | KIS 결과가 `src/quant_engine/kis_data_collection.db` 또는 `Temp/*db`로 적재되고 row_count>0 | SQLite DB 테이블, `tools/run_kis_data_collection_v1.py`, `Temp/test_kis_data_collection.db` |
| K4 | 실패가 투명하게 남음 | KIS 실패 시 `status`, `source_counts`, `error`가 숨지지 않고 JSON/DB에 남음 | `Temp/test_kis_data_collection.json`, validator 로그 |
| K5 | 운영 자동화가 유지됨 | 스케줄/수동 실행에서 동일 계약을 유지하고, seed-first/SQLite 우선 문구가 유지됨 | `.gitea/workflows/kis_data_collection.yml`, `tools/run_kis_data_collection_v1.py`, `docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md` |

#### 세부 WBS

| 세부 WBS | 작업 | 데이터 기반 완료 정의 | 현재 상태 |
|----------|------|-------------------|----------|
| 8.8.1 | KIS 우선 수집 경로 고정 | workflow와 CLI가 read-only KIS를 먼저 시도 | 완료 ✅ |
| 8.8.2 | Naver 폴백 경계 확정 | KIS 실패 시에만 Naver가 선택됨 | 완료 ✅ |
| 8.8.3 | KIS credential 검증 | `tools/validate_kis_api_credentials_v1.py --dry-run` PASS | 완료 ✅ |
| 8.8.4 | SQLite 적재 검증 | KIS 수집 결과가 SQLite 테이블에 기록되고 row_count>0 | 완료 ✅ |
| 8.8.5 | 운영 보고서 증빙 | provenance와 실패 사유가 JSON/DB에 남음 | 완료 ✅ |
| 8.8.6 | 전환 종료 선언 | `source_priority[0] == kis_open_api`가 유지되고 `status=PASS`/`row_count>0`/`collection_runs>=1`/`collection_snapshots>=1`가 동시에 성립 | 완료 ✅ |

#### 8.8 작업 티켓

| 티켓 | 산출물 | 완료 정의 |
|------|--------|----------|
| 8.8.T1 | `tools/run_kis_data_collection_v1.py` | KIS 우선 경로가 기본 시도 경로로 남고 Naver는 폴백만 수행 |
| 8.8.T2 | `tools/validate_kis_api_credentials_v1.py` | dry-run 기준으로 credential/endpoint 증빙이 남음 |
| 8.8.T3 | `Temp/test_kis_data_collection.db` | collection_runs / collection_snapshots / collection_source_errors가 기대값을 만족 |
| 8.8.T4 | `Temp/test_kis_data_collection.json` | provenance, source_counts, row_count, status가 PASS로 남음 |
| 8.8.T5 | `docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md` | xlsx는 seed-prep 보조 자산으로만 설명됨 |
| 8.8.T6 | `python tools/validate_platform_transition_wbs_v1.py` | WBS-8.2/8.8 검증이 PASS 유지 |

#### collector helper 증빙

- `tests/unit/test_kis_data_collection_v1.py`
- `tests/integration/test_kis_collection_to_snapshot_admin_and_sell_strategy_v1.py`
- `src/quant_engine/kis_data_collection_v1.py`

### WBS-8.3 공통 완료 정의

- `YAML`
  - 트랙별 WBS가 `docs/ROADMAP_WBS.md`에 존재해야 한다.
  - 관련 contract/spec/governance 문서가 같은 방향을 가리켜야 한다.
- `코드`
  - `.gs → Python`은 canonical Python 구현과 parity test가 있어야 한다.
  - `xlsx → sqlite`는 canonical SQLite store와 이를 사용하는 workflow/validator가 있어야 한다.
- `데이터`
  - `Temp/*.json`, `Temp/*.db`, `GatherTradingData.json`, `GatherTradingData.xlsx` 중 해당 트랙의 실제 산출물이 존재해야 한다.
  - `.gs → Python`은 `Temp/gas_thin_adapter_validation_v1.json`, parity test 결과, migration ledger가 함께 있어야 한다.
  - `xlsx → sqlite`는 `Temp/test_kis_data_collection.db`, `Temp/test_kis_data_collection.json`, `Temp/snapshot_admin_web_validation.db`가 함께 있어야 한다.
- `검증`
  - `python tools/validate_gas_thin_adapter_v1.py`
  - `python tools/validate_platform_transition_wbs_v1.py`
  - `python tools/validate_snapshot_admin_web_v1.py`
  - `python tools/validate_packaged_artifact_references_v1.py --strict`

### WBS-8.4 실행 순서

1. `.gs → Python` 잔여 finding을 `TODO / DONE / KEEP_IN_GAS`로 재집계한다.
2. `tests/parity/test_stop_loss_policy_parity.py`, `tests/parity/test_distribution_risk_parity.py`, `tests/parity/test_late_chase_risk_parity.py`, `tests/parity/test_routing_decision_parity.py`를 ledger finding과 1:1 대응시킨다.
3. `src/quant_engine/kis_data_collection_v1.py`를 source selection / source normalization / persistence로 분리한 뒤, collector 단일 책임을 유지한다.
4. `xlsx → sqlite` 의존 경로를 workflow와 validator에서 제거한다.
5. `Temp/test_kis_data_collection.json`과 `Temp/test_kis_data_collection.db`를 재생성한다.
6. `Temp/snapshot_admin_web_validation.db`를 재생성하고 `python tools/validate_snapshot_admin_web_v1.py`를 다시 통과시킨다.
7. 완료 정의를 충족하는 항목만 `DONE`으로 승격한다.
8. `GatherTradingData.xlsx`는 seed-prep/복구용 보조 자산으로만 취급하고 직접 실행 경로에서 제외한다.
9. `spec/56_renderer_copy_only_contract.yaml`와 `spec/40_final_decision_packet_contract.yaml`의 F08 근거를 유지한다.
10. runbook과 workflow가 JSON/SQLite 우선을 1차 권위로 유지하는지 재검증한다.

### WBS-8.5 현재 결론

- `.gs → Python`: 아직 **부분 완료**다.
- `xlsx → sqlite`: 아직 **부분 완료**다.
- 둘 다 “파일 수를 줄이는 것”이 완료가 아니라, **결정 로직의 권위와 입력의 권위를 옮기는 것**이 완료다.

### WBS-8.6 GAS ledger 재분류 블로커

현재 `governance/gas_logic_migration_ledger_v1.yaml`의 23개 `forbidden_gas_business_logic_count`는 다음 이유로 즉시 재분류할 수 없다.

| blocker | 영향 | 판정 |
|---------|------|------|
| 전용 parity test 부재 | `MIGRATE_*` 계열은 GAS와 Python의 동일 입력/동일 출력 증빙이 있어야 `DONE` 승격 가능 | BLOCKED |
| canonical Python 부재/불명확 | `DELETE_*` 계열은 Python canonical 또는 동등 판정이 없으면 삭제 불가 | BLOCKED |
| renderer-only 경계만 확정 | `F08`만 `KEEP_IN_GAS`로 유지 가능 | READY |
| collector refactor는 범위 외 | KIS 우선 수집 경로는 GAS thin-adapter ledger가 아니라 WBS-8.8에서 추적 | OUT_OF_SCOPE |

즉, 현재 레저는 `TODO`가 아니라 `parity / canonical evidence` 부족 상태다.  
다음 스프린트에서 해야 할 일은 "기계적 재분류"가 아니라 "증빙을 만들고 그 증빙으로 승격"이다.

#### 잔여 finding의 실제 작업 단위

| 카테고리 | 대상 finding | 다음 작업 |
|----------|--------------|----------|
| price/qty parity | F02, F03, F04, F05, F06 | `tests/parity/test_price_qty_parity_v1.py`로 동일 입력 포트 테스트를 고정하고 Python 출력과 대조. `compute_sell_decision()` / `compute_stop_action_ladder()` 동시 검증 |
| score parity | F07, F12, F13, F14 | `tests/parity/test_score_parity_v1.py`로 `BUY_BREAKOUT_PILOT_ONLY`, `BUY_PULLBACK_WAIT`, `EXIT_REVIEW`, `STOP_OR_TIME_EXIT_READY`, `OBSERVE_DATA_MISSING` golden case를 분리 검증 |
| routing parity | F10, F11, F15 | `tests/parity/test_routing_gate_parity_v1.py`로 `STOP_OR_TIME_EXIT_READY`, `RISK_OFF`, `HALVE_NEW_BUY_QUANTITY`, `HARD_BLOCK`, `RW2B_FAST_TRACK`, `trailing_stop`, `MEAN_REVERSION` golden case를 분리 검증 |
| registry confirmation | F01, F09 | 이미 DONE이므로 재작업 불필요 |
| presentation-only | F08 | `KEEP_IN_GAS` 유지 |

### WBS-8.7 실행 티켓

| 티켓 | 체크 | 증빙 | 상태 |
|------|------|------|------|
| 8.7.1 | [x] F08 유지 근거 고정 | `governance/gas_logic_migration_ledger_v1.yaml`의 `status: KEEP_IN_GAS` + `rationale` + roadmap inventory 반영 | 완료 |
| 8.7.2 | [x] xlsx 보조 자산 선언 | `docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md` + roadmap 문구 | 완료 |
| 8.7.3 | [x] seed-prep 분리 | `kis_data_collection.yml`이 workbook 직접 regeneration을 수행하지 않음 | 완료 |
| 8.7.4 | [x] JSON 우선 운영 명시 | workflow/operator guide가 `GatherTradingData.json`을 1차 입력으로 취급 | 완료 |
| 8.7.5 | [x] xlsx 아카이브 통합 | archive policy를 operating runbook에 통합해 중복 문서를 제거 | 완료 |
| 8.7.6 | [x] renderer contract 연결 | `spec/56_renderer_copy_only_contract.yaml`와 `spec/40_final_decision_packet_contract.yaml`에 의해 F08이 rendering-only로 유지 | 완료 |
| 8.7.7 | [x] 종료 체크 | `validate_platform_transition_wbs_v1.py`와 `validate_snapshot_admin_web_v1.py` PASS | 완료 |

---

## 7. 부록: Phase 5 데이터 플랫폼 전환 WBS 성공값

> 원칙: 아래 항목은 모두 `기대 성공값 + 데이터 증빙 + 검증 명령`이 함께 있어야 성공으로 본다.
> 현재 구현된 항목은 로컬 `Temp/` 증빙을 기준으로 판정하고, 아직 미래 전환 항목은 `DATA_GATED`로 둔다.

| WBS | 기대 성공값 | 데이터 증빙 | 검증 명령 |
|-----|------------|------------|-----------|
| P1 KIS core collector | `collector_gate=PASS`, `output_json_gate=PASS`, `collection_runs>=1`, `collection_snapshots>=1`, `provenance_source_count>=1` | `Temp/test_kis_data_collection.json`, `Temp/test_kis_data_collection.db` | `python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db Temp/test_kis_data_collection.db --output-json Temp/test_kis_data_collection.json --kis-account real --no-live-kis --no-naver` |
| P2 SQLite canonical store | `sqlite_schema_tables>=3`, `round_trip_snapshot_lookup=PASS`, `backend_contract_sqlite=PASS`, `backend_contract_postgresql=READY`, `single_workspace_sqlite=true`, `collector_separate_db=true` | `src/quant_engine/data_collection_store_v1.py`, `src/quant_engine/data_collection_backend_v1.py`, `tests/unit/test_data_collection_store_v1.py`, `src/quant_engine/snapshot_admin_store_v1.py` | `python -m pytest tests/unit/test_data_collection_store_v1.py -q` |
| P3 CI scheduler cutover | `xlsx_dependency_removed=true`, `json_seed_input=true`, `sqlite_output=true`, `mock_api_validation=PASS`, `no_direct_trading_gate=PASS` | `.gitea/workflows/kis_data_collection.yml`, `Temp/kis_api_credentials_validation_v1.json`, `Temp/test_kis_data_collection.json` | `python tools/validate_no_direct_api_trading_v1.py` |
| P4 GAS thin adapter minimize | `allowed_responsibilities_only=true`, `forbidden_responsibilities_present=false`, `thin_adapter_gate=PASS` | `tools/validate_gas_thin_adapter_v1.py`, `Temp/gas_thin_adapter_validation_v1.json`, `src/gas/core/gas_lib.gs` | `python tools/validate_gas_thin_adapter_v1.py` |
| P5 PostgreSQL upgrade path | `sqlite_schema_parity=PASS`, `backend_contract_present=true`, `postgres_execution=DATA_GATED`, `caller_compatibility_preserved=true` | `src/quant_engine/data_collection_backend_v1.py`, `src/quant_engine/kis_data_collection_v1.py`, `tests/unit/test_data_collection_store_v1.py`, `tools/generate_postgresql_upgrade_stub_v1.py` | `python -m pytest tests/unit/test_data_collection_store_v1.py -q` |
| P6 Snapshot admin web editor | `settings_sheet_web_editor=true`, `account_snapshot_sheet_web_editor=true`, `contenteditable_grid=true`, `api_save_round_trip=PASS`, `kis_collection_dashboard=true`, `workspace_db_is_single_file=true`, `collection_filter_controls=true`, `collection_dashboard_page=true`, `change_timeline_view=true` | `src/quant_engine/snapshot_admin_server_v1.py`, `src/quant_engine/data_collection_store_v1.py`, `src/quant_engine/snapshot_admin_store_v1.py`, `tools/validate_snapshot_admin_web_v1.py`, `tests/unit/test_snapshot_admin_web_v1.py`, `.gitea/workflows/snapshot_admin.yml` | `python tools/validate_snapshot_admin_web_v1.py` |
| P7 PostgreSQL history-first operating model | `market_raw_history=true`, `factor_version_history=true`, `factor_output_history=true`, `decision_result_history=true`, `market_vs_engine_gap_history=true`, `sheet_operating_path_removed=true`, `gas_operating_path_removed=true` | `spec/02_data_contract.yaml`, `spec/postgresql_history_contract.yaml`, `docs/DAILY_SIGNAL_TRACKING.md`, `docs/POSTGRESQL_HISTORY_FIRST_OPERATING_MODEL.md` | `python tools/validate_postgresql_history_contract_v1.py` |
| Q1 Qualitative sell pipeline | `mock_api_validation=PASS`, `pipeline_contract=PASS`, `workflow_present=true`, `schedule_present=true`, `package_scripts_present=true` | `.gitea/workflows/qualitative_sell_strategy.yml`, `tools/validate_qualitative_sell_strategy_pipeline_v1.py`, `Temp/qualitative_sell_strategy_pipeline_v1.json` | `python tools/validate_qualitative_sell_strategy_pipeline_v1.py` |
| Q2 Gitea secrets contract | `secrets_contract=PASS`, `workflow_secret_mapping=PASS`, `docs_present=true`, `ci_validation_present=true` | `docs/GITEA_SECRETS_SETUP.md`, `tools/validate_gitea_secrets_contract_v1.py`, `Temp/gitea_secrets_contract_v1.json` | `python tools/validate_gitea_secrets_contract_v1.py` |

### WBS 성공 판정 규칙

- `PASS`: 기대 성공값이 충족되고, 해당 증빙 파일이 실제로 존재한다.
- `READY`: 지금은 실행하지 않지만, 다음 단계 전환에 필요한 코드/계약이 존재한다.
- `DATA_GATED`: 의도적으로 아직 실제 데이터가 쌓이지 않아 보류된 항목이다.
- `FAIL`: 기대 성공값을 만족하지 못하거나 증빙이 없다.

> 이 문서는 `docs/ROADMAP_WBS.md` 에 저장됩니다.  
> 스프린트 완료마다 **완성도 KPI 섹션**을 업데이트하세요.  
> 모든 WBS 항목의 구현 시 반드시 **하네스 성공 기준**을 먼저 충족 후 다음 단계로 진행합니다.

---

## 차세대 퀀트 엔진 로드맵/WBS 포인터 (2026-07-12)

이후의 퀀트 엔진 진화 로드맵(M0–M5: 실증 하네스 → 수집 배선 → 시계열 저장소 →
실데이터 팩터 → 백테스팅 → 포트폴리오/레짐)과 상세 WBS는 **기계 판정 YAML**로 관리한다:

- **스펙(단일 진실 원천)**: `spec/60_quant_engine_wbs.yaml` (formula_id: `QUANT_ENGINE_WBS_V1`)
- **단일 작업 검증**: `python tools/verify_wbs_task_v1.py --task <TASK_ID>` → `Temp/evidence/<TASK_ID>/verdict.json`
- **전체 WBS 게이트**: `python tools/validate_quant_engine_wbs_v1.py` → `Temp/quant_engine_wbs_v1.json`

완료 판정 원칙: 작업은 게이트 실행(PASS)으로만 `DONE` 이 될 수 있다.
BE = PostgreSQL 쿼리 + Serilog 로그 패턴 + JSON 아티팩트, FE = Playwright(DOM assert + API 기대값 대조 + 스크린샷).

### 폐기: schemas/generated/ + src/quant_engine/models/generated/ (2026-07-12, QE-M0-07)

`schemas/generated/*.schema.json`(174) + `src/quant_engine/models/generated/*.py`(347)로
구성된 스키마-모델 생성 레이어를 **폐기**했다. 기존 `runtime/python/core/formulas/generated/`
(172개 stub)와 동일한 목적(공식 메타데이터 서술)을 범용 wrapper로 중복 구현했을 뿐 실질
계산 로직이 전혀 없었고, 검증도 `validate_schema_model_generation_v1.py`가 파일 개수만
세는 가짜 게이트였다(QUANT_ENGINE_WBS_V1 재검토에서 발견). CI 시간만 늘리고 기능적
이득이 없어 삭제. `tools/generate_schema_model_generation_evidence_v1.py`,
`tools/validate_schema_model_generation_v1.py`, `src/quant_engine/generate_models_from_schema.py`
및 ci.yml/`spec/41_release_dag.yaml`의 관련 스텝·노드도 함께 제거했다.
`schemas/generated/gas_adapter_contract.schema.json`은 별개 목적(GAS 어댑터 계약 검증,
`validate_gas_adapter_contract_v1.py`)으로 쓰이므로 보존.
