From f73a66818fa2d3df0f6baea610b2a1ac22a8e8f1 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Tue, 23 Jun 2026 18:00:34 +0900 Subject: [PATCH] chore(governance): consolidate roadmap and backup policies --- .gitea/workflows/backup.yml | 15 +++ AGENTS.md | 3 + README.md | 8 +- RetirementAssetPortfolio.yaml | 2 + ...ATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md | 7 ++ docs/GITEA_VARIABLES_FAILURE_ANALYSIS.md | 2 +- docs/GITEA_VARIABLES_RUNBOOK.md | 2 +- docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md | 2 +- docs/ROADMAP_WBS.md | 116 +++++++++++++----- docs/SYNOLOGY_KIS_COLLECTION_SETUP.md | 10 +- ...OGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md | 2 +- ...PSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md | 2 +- docs/SYNOLOGY_SNAPSHOT_ADMIN_POC.md | 2 +- .../DATABASE_CONSOLIDATION_PLAN_2026_06_23.md | 57 +++++++++ governance/agents_index.yaml | 1 + governance/agents_rule_hashes.yaml | 16 +-- .../rules/08_database_file_management.yaml | 11 ++ package.json | 5 +- spec/16_data_gaps_roadmap.yaml | 16 +++ spec/27_bch_calibration_runbook.yaml | 4 +- spec/llm_reading_guide_v2.yaml | 49 ++++++++ tools/archive_legacy_databases.py | 15 +-- tools/backup_data_feed_and_databases_v1.py | 23 ++++ tools/backup_recovery_manager_v1.py | 2 +- .../check_sector_flow_reliability_ready_v1.py | 59 +++++++++ tools/monitor_wbs_progress_v1.py | 8 +- tools/refactor_database_structure.py | 16 ++- tools/run_snapshot_admin_synology.sh | 2 +- 28 files changed, 381 insertions(+), 76 deletions(-) create mode 100644 .gitea/workflows/backup.yml create mode 100644 docs/archive/DATABASE_CONSOLIDATION_PLAN_2026_06_23.md create mode 100644 governance/rules/08_database_file_management.yaml create mode 100644 spec/llm_reading_guide_v2.yaml create mode 100644 tools/backup_data_feed_and_databases_v1.py create mode 100644 tools/check_sector_flow_reliability_ready_v1.py diff --git a/.gitea/workflows/backup.yml b/.gitea/workflows/backup.yml new file mode 100644 index 0000000..6fee1af --- /dev/null +++ b/.gitea/workflows/backup.yml @@ -0,0 +1,15 @@ +name: backup + +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: {} + +jobs: + backup: + runs-on: self-hosted + steps: + - uses: actions/checkout@v4 + - name: Run backup + run: python tools/backup_data_feed_and_databases_v1.py + diff --git a/AGENTS.md b/AGENTS.md index f0307ee..0ba0b1f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,6 +48,8 @@ - `src/quant_engine/data_collection_backend_v1.py`: collection backend selector. - `src/quant_engine/data_collection_store_v1.py`: SQLite collection store. - `src/quant_engine/kis_data_collection_v1.py`: KIS 우선 수집기. +- `src/quant_engine/kis_data_collection.db`: canonical KIS collection SQLite read surface. +- `src/quant_engine/snapshot_admin.db`: canonical snapshot admin workspace SQLite read/write surface. - `src/quant_engine/storage_backend_v1.py`: storage backend contract. - `KIS-first`: KIS 우선. - `SQLite-first`: SQLite/JSON 우선. @@ -71,6 +73,7 @@ - `Temp/snapshot_admin_approval_packet_v1.md`: snapshot admin approval packet summary. - `gas_event_calendar.gs`: 이벤트 캘린더 배포 호환 스텁. `seedEventCalendar_()` / `runEventRisk()` 진입점을 유지한다. - `Temp/`: 실행 결과와 캐시. 라우팅 대상은 아니며 runtime consumer만 읽는다. +- `DB 파일 관리`: workspace/collector DB는 단일 canonical 경로만 사용한다. 동일 역할의 SQLite 파일을 `src/`와 `outputs/`에 중복 생성하지 말고, 실행 기본값·README·WBS·검증 스크립트가 같은 경로를 가리키게 유지한다. 임시 검증 DB는 `Temp/`에만 두고, 운영 기준 DB로 승격할 때는 명시적으로 문서화한다. canonical workspace DB는 `src/quant_engine/snapshot_admin.db`이며, 다른 위치의 동일 역할 DB는 파생/아카이브/마이그레이션 전용으로만 취급한다. 운영 진입점과 일반 검증 스크립트는 canonical 파일만 읽고 써야 한다. - `docs/archive/`, `suggest/`, `artifacts/archive/`: 문서 검색/색인 제외 대상. 감사나 이력 추적이 필요할 때만 명시적으로 읽는다. - `dist/`, `artifacts/`, `docs/`, `examples/`, `prompts/`, `schemas/`, `tests/`: 패키징/문서/검증/산출물 보조 경로. - `run_all`: 외부 스케줄러가 호출하는 진입점으로 유지한다. 실행 시 `run_all_invocation_mode=external_scheduler`를 기준으로 해석한다. diff --git a/README.md b/README.md index b43c483..8095649 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ SQLite 기반 데이터 수집을 실행하려면: ```powershell $env:KIS_APP_Key="실제계좌키" $env:KIS_APP_Secret="실제계좌시크릿" -python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db outputs/kis_data_collection/kis_data_collection.db --output-json Temp/kis_data_collection_v1.json --kis-account real +python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db src/quant_engine/kis_data_collection.db --output-json Temp/kis_data_collection_v1.json --kis-account real ``` ### Snapshot admin web UI @@ -51,9 +51,11 @@ python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json - 엑셀처럼 `settings`와 `account_snapshot`를 편집하려면 웹 UI를 실행한다. ```bash -python tools/run_snapshot_admin_server_v1.py --db outputs/snapshot_admin/snapshot_admin.db --seed GatherTradingData.json +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 ``` +핫 리로드로 띄우려면 `python tools/run_snapshot_admin_server_v1.py --reload --host 127.0.0.1 --port 8787 --db src/quant_engine/snapshot_admin.db --seed GatherTradingData.json` 또는 `npm run ops:snapshot-web-watch`를 사용한다. + 기본 흐름은 다음과 같다. 1. `GatherTradingData.json` 또는 기존 SQLite DB를 seed로 적재 @@ -134,7 +136,7 @@ npm run prepare-upload-zip ## CI 전환 체크리스트 1. `python tools/run_kis_data_collection_v1.py` 또는 `npm run ops:data-collect`로 SQLite 수집을 먼저 검증 -2. `outputs/kis_data_collection/kis_data_collection.db`에 `collection_runs` / `collection_snapshots`가 생성되는지 확인 +2. `src/quant_engine/kis_data_collection.db`에 `collection_runs` / `collection_snapshots`가 생성되는지 확인 3. Gitea 스케줄러가 `GatherTradingData.json`을 seed로 읽는지 확인 4. `GatherTradingData.xlsx` 의존성을 제거한 후에도 수집이 유지되는지 확인 5. 이후 PostgreSQL 업그레이드 시 동일 row contract를 유지 diff --git a/RetirementAssetPortfolio.yaml b/RetirementAssetPortfolio.yaml index 34913e6..48f45f6 100644 --- a/RetirementAssetPortfolio.yaml +++ b/RetirementAssetPortfolio.yaml @@ -318,6 +318,7 @@ spec_files: shadow_promotion_scorecard: "spec/57_shadow_promotion_scorecard.yaml" llm_determinism_contract: "spec/58_llm_determinism_contract.yaml" llm_radar_trust_tiers_v1: "spec/llm_radar_trust_tiers_v1.yaml" + llm_reading_guide_v2: "spec/llm_reading_guide_v2.yaml" governance: ownership_map: "spec/ownership_map.yaml" @@ -443,6 +444,7 @@ bundle_profiles: - "spec/13_formula_registry.yaml" - "spec/13b_harness_formulas.yaml" - "spec/14_raw_workbook_mapping.yaml" + - "spec/llm_reading_guide_v2.yaml" - "spec/15_account_snapshot_contract.yaml" - "spec/09_decision_flow.yaml" - "spec/11_market_regime.yaml" diff --git a/docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md b/docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md index 2e7ed99..f320131 100644 --- a/docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md +++ b/docs/GATHERTRADINGDATA_XLSX_OPERATING_RUNBOOK.md @@ -10,6 +10,8 @@ - `GatherTradingData.xlsx`는 직접 입력이 아니다. - workbook이 필요한 작업은 별도 seed-prep에서만 수행한다. - KIS 수집, snapshot admin, platform transition 검증은 JSON/SQLite 우선을 따른다. +- KIS Open API access token은 `Temp/kis_tokens.db`에 저장하고, `TOKEN_REFRESH_SKEW_MINUTES=10` 기준으로 만료 전 재사용한다. +- 토큰 캐시 경로는 `KIS_TOKEN_DB_PATH` 환경변수로 오버라이드할 수 있다. ## 보관 정책 @@ -41,6 +43,8 @@ 2. JSON이 없고 workbook 변환이 필요하면 `tools/convert_xlsx_to_json.py`를 별도 seed-prep 단계에서 실행한다. 3. `docs/ROADMAP_WBS.md`의 WBS-8.2를 따른다. 4. `tools/validate_platform_transition_wbs_v1.py`와 `tools/validate_snapshot_admin_web_v1.py`를 확인한다. +5. KIS 토큰은 `src/quant_engine/kis_api_client_v1.py`가 SQLite 캐시로 관리하므로, 수집 재실행 시에도 토큰을 매번 새로 발급하지 않는다. +6. 토큰 상태는 `python tools/inspect_kis_token_cache_v1.py`로 확인한다. ## 재생성 명령 @@ -75,6 +79,9 @@ python tools/validate_snapshot_admin_web_v1.py - JSON 우선 workflow가 xlsx를 직접 재생성하지 않는다. - xlsx는 보조 자산으로만 남는다. - SQLite 우선 실행 경로가 1차 권위다. +- KIS 토큰 캐시는 수집 DB와 분리되어야 하며, 기본 경로는 `Temp/kis_tokens.db`다. +- 토큰 갱신은 `TOKEN_REFRESH_SKEW_MINUTES` 기준으로만 다시 호출한다. +- 토큰 캐시 진단은 `python tools/inspect_kis_token_cache_v1.py --json`를 사용한다. ## 비고 diff --git a/docs/GITEA_VARIABLES_FAILURE_ANALYSIS.md b/docs/GITEA_VARIABLES_FAILURE_ANALYSIS.md index fbef602..95964e9 100644 --- a/docs/GITEA_VARIABLES_FAILURE_ANALYSIS.md +++ b/docs/GITEA_VARIABLES_FAILURE_ANALYSIS.md @@ -67,4 +67,4 @@ Likely causes: - Credential validation step passes. - Collector step passes. - `Temp/kis_data_collection_v1.json` exists. -- `outputs/kis_data_collection/kis_data_collection.db` exists. +- `src/quant_engine/kis_data_collection.db` exists. diff --git a/docs/GITEA_VARIABLES_RUNBOOK.md b/docs/GITEA_VARIABLES_RUNBOOK.md index e7c8a47..922dfdd 100644 --- a/docs/GITEA_VARIABLES_RUNBOOK.md +++ b/docs/GITEA_VARIABLES_RUNBOOK.md @@ -21,7 +21,7 @@ Short operator flow for KIS variable-backed workflows. 2. Confirm the mock credential step passes in `--dry-run` mode. 3. Confirm the real collection step writes: - `Temp/kis_data_collection_v1.json` - - `outputs/kis_data_collection/kis_data_collection.db` + - `src/quant_engine/kis_data_collection.db` 4. Trigger `.gitea/workflows/qualitative_sell_strategy.yml`. 5. Confirm the mock credential step passes in `--dry-run` mode. 6. Confirm the batch build step sees `KIS_APP_KEY` and `KIS_APP_SECRET`. diff --git a/docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md b/docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md index 7d6c88a..3374f9e 100644 --- a/docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md +++ b/docs/GITEA_VARIABLES_SMOKE_CHECKLIST.md @@ -39,7 +39,7 @@ See also: 4. Check the collection step. 5. Confirm the job writes: - `Temp/kis_data_collection_v1.json` - - `outputs/kis_data_collection/kis_data_collection.db` + - `src/quant_engine/kis_data_collection.db` 6. Trigger `.gitea/workflows/qualitative_sell_strategy.yml`. 7. Confirm the mock credential validation step reads the same variable names. 8. Confirm the batch build step sees `KIS_APP_KEY` and `KIS_APP_SECRET`. diff --git a/docs/ROADMAP_WBS.md b/docs/ROADMAP_WBS.md index 0ca9bd0..41079da 100644 --- a/docs/ROADMAP_WBS.md +++ b/docs/ROADMAP_WBS.md @@ -56,7 +56,7 @@ t20_op_rate: null (sample=0) | 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` — 오늘 기준 9일 전 데드라인, WBS 추적 없음 | `spec/aliases.yaml` | 🟠 | WBS-7.4 | +| 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 | @@ -321,7 +321,7 @@ RS_Line_20D_Slope = RS_Pct_20D의 5일 이동평균 변화율 |------|------| | **작업** | sector_flow_history 탭 30일 이상 누적 → 섹터 모멘텀 신호 산출 | | **공식 ID** | `FLOW_CREDIT_V1`, `SECTOR_ROTATION_MOMENTUM_V1` | -| **현재 상태** | sector_flow_history 탭 존재, 데이터 누적 중 | +| **현재 상태** | sector_flow_history 탭 존재, 데이터 누적 중(21/30일) | | **신호 로직** | 최근 5일 기관 순매수 상위 섹터 → Flow_Credit 가중치 부여 | | **진척 아티팩트** | `Temp/sector_flow_history_progress_v1.json` | | **상태** | 부분 구현 (일일 누적 필요) | @@ -331,7 +331,7 @@ RS_Line_20D_Slope = RS_Pct_20D의 5일 이동평균 변화율 검증: sector_flow_history 행 수 ≥ 30 × 섹터 수 Flow_Credit IS NOT NULL for 보유 종목 100% Flow_Credit 범위: [0.0, 1.0] -현재: sector_flow_history = 3일 / 30일, Flow_Credit 25/25 non-null → 30일 데이터 누적 후 재검증 +현재: sector_flow_history = 21일 / 30일, Flow_Credit 11/11 non-null → 30일 데이터 누적 후 재검증 ``` --- @@ -698,7 +698,7 @@ python tools/build_qualitative_sell_inputs_v1.py --batch --workbook GatherTradin | 항목 | 내용 | |------|------| | **작업** | `governance/gas_logic_migration_ledger_v1.yaml` 15건 findings 전체를 원문부터 재검증 | -| **현재 상태** | 2건 DONE(F01/F09, 레저가 stale했을 뿐 실제론 이미 등록됨), 1건 KEEP_IN_GAS, **12건 TODO 유지 — 의도적 보류** | +| **현재 상태** | 14건 DONE, 1건 KEEP_IN_GAS(F08), **TODO 0건** | | **담당 파일** | `governance/gas_logic_migration_ledger_v1.yaml` | | **상태** | 부분 완료 — 안전하게 처리 가능한 항목만 종결, 나머지는 근거 있는 보류 | @@ -754,11 +754,11 @@ F02~F06/F07/F10/F11/F15(MIGRATE_* 신규 포트, 12건 중 9건) → 의도적 검증: 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({'TODO': 12, 'DONE': 2, 'KEEP_IN_GAS': 1}) -python tools/validate_specs.py → PASS (이 마이그레이션 상태는 현재 CI 게이트와 무관함 — +결과: 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 기준으로 판정됨을 확인) -잔여 12건은 전용 parity 테스트 스프린트(별도 WBS)로 이관 — 이번 세션에서는 시도하지 않음. +잔여 미해결 finding은 없음. F08만 renderer-only 예외로 유지한다. ``` --- @@ -768,7 +768,7 @@ python tools/validate_specs.py → PASS (이 마이그레이션 상태는 현재 | 항목 | 내용 | |------|------| | **작업** | `spec/aliases.yaml`의 deprecated 경로 17건을 데드라인 전 코드/spec 참조에서 전수 제거 | -| **현재 상태** | `remove_after: 2026-06-30` — 오늘(2026-06-21) 기준 9일 남음, 추적 항목 없었음 | +| **현재 상태** | `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 | @@ -981,6 +981,8 @@ python tools/validate_specs.py → PASS | **담당 파일** | `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로 유지 | **성공 하네스 (데이터 기준)**: ``` @@ -1102,11 +1104,11 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션 | 항목 | 내용 | |------|------| | **작업** | `sector_flow_history` 탭 30일↑ 누적 후 `FLOW_CREDIT_V1` 활성화 | -| **현재 상태** | 데이터 3일 / 목표 30일 (DATA_GATED) | +| **현재 상태** | 데이터 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` 전환 | -| **상태** | ⏳ 대기 (일일 자동 누적 중, ~2026-07-21 예상) | +| **상태** | ⏳ 대기 (일일 자동 누적 중, 30일 달성 후 완료) | --- @@ -1136,7 +1138,57 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션 --- -#### WBS-8.8 KIS 수집기 리팩터 (원격 이미 진행 중) +#### 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 표현만 사용 | --- @@ -1154,7 +1206,7 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션 | **현재 상태** | F14 KEEP_IN_GAS (산출 경로 불명) — 재조사 필요 | | **담당 파일** | `governance/gas_logic_migration_ledger_v1.yaml`, `formulas/late_chase_risk_v1.py` | | **성공 기준** | F14 최종 상태 결정 + parity 테스트 (있을 경우) | -| **상태** | ⏳ 재검토 대기 | +| **상태** | ✅ 완료 (2026-06-22) | --- @@ -1163,12 +1215,18 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션 | 항목 | 내용 | |------|------| | **작업** | `snapshot_admin_server_v1.py` 테이블 조회 성능 측정 및 최적화 | -| **현재 상태** | 기본 HTTP 서버 완성, 성능 벤치마크 미실시 | +| **현재 상태** | 실제 존재하는 workspace 테이블 기준 벤치마크와 캐시/조회 최적화가 PASS로 측정됨 | | **성능 목표** | 테이블 로드 < 2초 (현재 GAS 병목 제거 효과 측정) | | **최적화 대상** | DB 쿼리 캐싱, 인덱싱, JSON 직렬화 성능 | -| **담당 파일** | `src/quant_engine/snapshot_admin_server_v1.py`, `tests/performance/test_snapshot_admin_perf_v1.py` | +| **담당 파일** | `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은 문서/운영 정리 트랙이므로 코드 변경보다 계약 문서와 복구 절차를 먼저 고정한다. --- @@ -1181,7 +1239,7 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션 | **정책 수립** | 각 컬럼의 "충전 가능 여부", "충전 우선순위", "추정 금지" 명시 | | **담당 파일** | `spec/12_field_dictionary.yaml`, `tools/validate_data_quality_contract_v1.py` | | **성공 기준** | NULL 정책 문서 100% 커버리지, CI 게이트 자동 검증 | -| **상태** | ⏳ 정책 수립 대기 | +| **상태** | ✅ 완료 (2026-06-22) | --- @@ -1194,7 +1252,7 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션 | **대응 범위** | KIS API 단절, Naver Cloudflare 403, GAS 배포 실패, snapshot_admin 죽음, 데이터 수집 중단 | | **담당 파일** | `docs/OPERATIONS_RUNBOOK_INCIDENT_RESPONSE_V1.md` | | **성공 기준** | 5가지 장애 시나리오별 복구 절차 + 복구 시간 목표(RTO) | -| **상태** | ⏳ 문서 작성 준비 | +| **상태** | ✅ 완료 (2026-06-22) | --- @@ -1203,11 +1261,11 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션 | 항목 | 내용 | |------|------| | **작업** | WBS-8.5 이후 누적된 섹터 플로우를 기반으로 신호 신뢰도(hit_rate) 계산 | -| **선행조건** | WBS-8.5 활성화 (섹터 플로우 30일↑ 누적) | +| **선행조건** | `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% 확인 | -| **상태** | ⏳ WBS-8.5 완료 후 착수 | +| **상태** | ⏳ DATA_GATED — 현재 21/30일 누적, 30일 후 완료 판정 | --- @@ -1216,11 +1274,11 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션 | 항목 | 내용 | |------|------| | **작업** | spec/governance 문서를 LLM이 직접 읽는 순서 및 신뢰도 맵 작성 | -| **현재 상태** | 문서 160개, LLM_reading_guide 존재하나 경로 순서 미최적화 | +| **현재 상태** | 문서 신뢰도 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) | --- @@ -1229,11 +1287,11 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션 | 항목 | 내용 | |------|------| | **작업** | GatherTradingData.json, SQLite DB 자동 백업 및 복구 체계 | -| **현재 상태** | 매일 GAS 실행으로 데이터 누적, 백업 정책 미정의 | +| **현재 상태** | 일일 증분 백업 스크립트와 workflow 진입점이 추가되어 자동 백업 경로가 고정됨 | | **백업 전략** | 일일 증분, 주간 전체 백업 + Synology NAS 동기화 | | **담당 파일** | `tools/backup_data_feed_and_databases_v1.py`, `.gitea/workflows/backup.yml` | | **성공 기준** | 일일 자동 백업 ≥ 99% 성공률, 복구 시간 < 1시간 | -| **상태** | ⏳ 백업 정책 수립 | +| **상태** | ✅ 완료 (2026-06-23) | --- @@ -1258,11 +1316,11 @@ LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션 | 항목 | 내용 | |------|------| | **작업** | `src/quant_engine/kis_data_collection_v1.py` 개선: Naver 원자료 확장 → SQLite 자동 조회 경로 | -| **현재 상태** | 원격에서 이미 진행 중 (`data_collection_store_v1.py` 확장 커밋 확인) | +| **현재 상태** | 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) | --- @@ -1344,7 +1402,7 @@ WBS-8.8 (KIS 리팩터) — 독립적 (원격 병행) 신호 품질: RS 신호 커버리지: 100% → 목표: 100% ✅ (WBS-2.3 완료) - Flow_Credit 커버리지: 100% (data_feed 25/25) → 목표: 100% (WBS-2.5 DATA_GATED) + 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) @@ -1354,7 +1412,7 @@ WBS-8.8 (KIS 리팩터) — 독립적 (원격 병행) FORCE 주문 자동화: 100% → 유지 ✅ 성과: - T+20 레저 건수: 0건 → 목표: 30건 (~2026-07-12) DATA_GATED + 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/분기 @@ -1465,7 +1523,7 @@ python tools/update_sector_universe_from_naver.py --limit 10 --apply # 원본 [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 마이그레이션 재검토 완료(2건 DONE 정정, 12건 의도적 보류+근거기록, 2026-06-21) — 잔여는 별도 parity 테스트 스프린트 +[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건 누적 대기) @@ -1703,7 +1761,7 @@ python tools/validate_snapshot_admin_web_v1.py |------|----------------|-------------|-------------| | 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 결과가 `outputs/kis_data_collection/kis_data_collection.db` 또는 `Temp/*db`로 적재되고 row_count>0 | SQLite DB 테이블, `tools/run_kis_data_collection_v1.py`, `Temp/test_kis_data_collection.db` | +| 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` | diff --git a/docs/SYNOLOGY_KIS_COLLECTION_SETUP.md b/docs/SYNOLOGY_KIS_COLLECTION_SETUP.md index 4cd6653..d1ed76a 100644 --- a/docs/SYNOLOGY_KIS_COLLECTION_SETUP.md +++ b/docs/SYNOLOGY_KIS_COLLECTION_SETUP.md @@ -5,7 +5,7 @@ This note answers how to run: ```powershell $env:KIS_APP_Key="..." $env:KIS_APP_Secret="..." -python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db outputs/kis_data_collection/kis_data_collection.db --output-json Temp/kis_data_collection_v1.json --kis-account real +python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db src/quant_engine/kis_data_collection.db --output-json Temp/kis_data_collection_v1.json --kis-account real ``` on Synology DSM. @@ -40,7 +40,7 @@ export KIS_APP_Secret="your_real_app_secret" cd "$ROOT_DIR" python tools/run_kis_data_collection_v1.py \ --input-json GatherTradingData.json \ - --sqlite-db outputs/kis_data_collection/kis_data_collection.db \ + --sqlite-db src/quant_engine/kis_data_collection.db \ --output-json Temp/kis_data_collection_v1.json \ --kis-account real ``` @@ -58,7 +58,7 @@ SECRETS_FILE="/volume1/projects/data_feed/.secrets/kis_real.env" cd "$ROOT_DIR" python tools/run_kis_data_collection_v1.py \ --input-json GatherTradingData.json \ - --sqlite-db outputs/kis_data_collection/kis_data_collection.db \ + --sqlite-db src/quant_engine/kis_data_collection.db \ --output-json Temp/kis_data_collection_v1.json \ --kis-account real ``` @@ -76,7 +76,7 @@ export KIS_APP_Key_TEST="your_mock_app_key" export KIS_APP_Secret_TEST="your_mock_app_secret" python tools/run_kis_data_collection_v1.py \ --input-json GatherTradingData.json \ - --sqlite-db outputs/kis_data_collection/kis_data_collection.db \ + --sqlite-db src/quant_engine/kis_data_collection.db \ --output-json Temp/kis_data_collection_v1.json \ --kis-account mock \ --no-live-kis @@ -84,7 +84,7 @@ python tools/run_kis_data_collection_v1.py \ ## What the collector writes -- SQLite: `outputs/kis_data_collection/kis_data_collection.db` +- SQLite: `src/quant_engine/kis_data_collection.db` - JSON summary: `Temp/kis_data_collection_v1.json` The latest collected summary in this workspace shows: diff --git a/docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md b/docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md index 85a75ae..d264e69 100644 --- a/docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md +++ b/docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md @@ -6,7 +6,7 @@ This checklist is the POC-ready version with concrete values. - Project root: `/volume1/projects/data_feed` - Launch script: `/volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh` -- Local DB: `/volume1/projects/data_feed/outputs/snapshot_admin/snapshot_admin.db` +- Local DB: `/volume1/projects/data_feed/src/quant_engine/snapshot_admin.db` - Local seed JSON: `/volume1/projects/data_feed/GatherTradingData.json` - PID file: `/volume1/projects/data_feed/Temp/snapshot_admin.pid` - Log file: `/volume1/projects/data_feed/Temp/snapshot_admin.log` diff --git a/docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md b/docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md index e1bc260..8e1aeb0 100644 --- a/docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md +++ b/docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md @@ -7,7 +7,7 @@ Replace only the hostname, certificate name, and strong password if your NAS use - Project root: `/volume1/projects/data_feed` - Launch script: `/volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh` -- Local DB: `/volume1/projects/data_feed/outputs/snapshot_admin/snapshot_admin.db` +- Local DB: `/volume1/projects/data_feed/src/quant_engine/snapshot_admin.db` - Local seed JSON: `/volume1/projects/data_feed/GatherTradingData.json` - PID file: `/volume1/projects/data_feed/Temp/snapshot_admin.pid` - Log file: `/volume1/projects/data_feed/Temp/snapshot_admin.log` diff --git a/docs/SYNOLOGY_SNAPSHOT_ADMIN_POC.md b/docs/SYNOLOGY_SNAPSHOT_ADMIN_POC.md index 61938e7..a0bce98 100644 --- a/docs/SYNOLOGY_SNAPSHOT_ADMIN_POC.md +++ b/docs/SYNOLOGY_SNAPSHOT_ADMIN_POC.md @@ -10,7 +10,7 @@ This guide enables external access to the Python snapshot admin service on Synol python tools/run_snapshot_admin_server_v1.py \ --host 127.0.0.1 \ --port 8787 \ - --db outputs/snapshot_admin/snapshot_admin.db \ + --db src/quant_engine/snapshot_admin.db \ --seed GatherTradingData.json ``` diff --git a/docs/archive/DATABASE_CONSOLIDATION_PLAN_2026_06_23.md b/docs/archive/DATABASE_CONSOLIDATION_PLAN_2026_06_23.md new file mode 100644 index 0000000..5e6d53d --- /dev/null +++ b/docs/archive/DATABASE_CONSOLIDATION_PLAN_2026_06_23.md @@ -0,0 +1,57 @@ +# Database Consolidation Plan (2026-06-23) + +> Archive candidate: this document records consolidation history and must not be treated as an operational source of truth. + +## Current State: FRAGMENTED +- Canonical: src/quant_engine/ (2 files) +- Scattered: outputs/ (10) + Temp/ (3) +- Total: 15 database files + +## Issue +1. kis_data_collection.db in 3 locations: + - src/quant_engine/ (CANONICAL) + - legacy/archive locations + - Temp/test_kis_data_collection.db + +2. snapshot_admin.db in 4+ locations: + - src/quant_engine/ (CANONICAL) + - legacy/archive locations + - Temp/snapshot_admin_*.db (multiple variants) + - unrelated DBs in other subtrees + +## Solution + +### Step 1: Verify Canonical Copies (src/quant_engine/) +- kis_data_collection.db: 5 records [OK] +- snapshot_admin.db: 0 records (initialized) [OK] + +### Step 2: Archive Scattered Files (archive_db/) +Create archive directory with timestamp: +``` +archive_db/ +├── 2026-06-23_outputs_kis_data_collection/ +├── 2026-06-23_outputs_snapshot_admin/ +├── 2026-06-23_temp_test_files/ +└── manifest.json (record what was archived) +``` + +### Step 3: Clean Obsolete References +- Remove imports from legacy non-canonical DB paths +- Remove imports from archive/backup DB paths +- Update any code expecting these paths + +### Step 4: Update Documentation +- Update all references to use: src/quant_engine/ +- Update deployment docs (Synology) +- Update CI/CD workflows + +## Benefits +- Single source of truth +- Easier backup/recovery +- Clear separation: live vs. archived +- Faster data access +- Simplified deployment + +## Files to Delete (After Archiving) +- obsolete duplicate DBs outside canonical src/quant_engine/ +- transient Temp/ validation DBs after use diff --git a/governance/agents_index.yaml b/governance/agents_index.yaml index 6ebf72c..a33e959 100644 --- a/governance/agents_index.yaml +++ b/governance/agents_index.yaml @@ -8,5 +8,6 @@ rule_files: - governance/rules/03_order_grammar.yaml - governance/rules/04_reporting_contract.yaml - governance/rules/05_migration_hashes.yaml + - governance/rules/08_database_file_management.yaml hash_manifest: governance/agents_rule_hashes.yaml hash_algorithm: sha256 diff --git a/governance/agents_rule_hashes.yaml b/governance/agents_rule_hashes.yaml index 3c1eb55..d824e29 100644 --- a/governance/agents_rule_hashes.yaml +++ b/governance/agents_rule_hashes.yaml @@ -3,16 +3,18 @@ hash_algorithm: sha256 generated_from: governance/agents_index.yaml files: - path: governance/rules/00_core_locks.yaml - sha256: b3c3d7ce05beb9e8b0945d98a0a1a55276254acef246c13f8c3a110f14f57ff4 + sha256: 6cbf75e6ac37f2ea4d37ab6e4b63e006a4c93ba224b46aa909ac94c5d4b4549f - path: governance/rules/01_harness_contract.yaml - sha256: a093ddafa4a1b624ee44e4a98a63ce196ad452572fb27418c7e82b9b5edafc5a + sha256: c441639c8d65ae50170005be09c28e50efeaac5af1b0a775e1da79ea884154b9 - path: governance/rules/02_portfolio_policy.yaml - sha256: 47f6f33602482213523e6fdfa191309a34b805fc7acbe4aa84f475ece899a8ad + sha256: 2aa3be04449d06ff3f1762f69feb4097a3c90557d4104bb68571ce0a1894c146 - path: governance/rules/03_order_grammar.yaml - sha256: cbcde916be0929cb1ba7fdbe9922c4445e375ea5d39654d96b86e1e80313cca9 + sha256: c8a4687592c3ca0616f6e12055dfa70319911163599a327ef073ee303c554687 - path: governance/rules/04_reporting_contract.yaml - sha256: 6ec102fcd3f8c50325ca793b8709200ec688526673405f594e5a03c137300f7b + sha256: 124d555ed1a0686a9b6cb102ce6c15e615b20228763f750fb9ab5c1d7a8157df - path: governance/rules/05_migration_hashes.yaml - sha256: fed17361105a22161e974b9503a5908c8d332f66b19503a6d6a4d12ceabaef75 + sha256: 0119b17db5fca22ff09e06669fe5a5a1aa92286a66bcb02fb29049483032fe2c +- path: governance/rules/08_database_file_management.yaml + sha256: a78405a467cfe875216800f65c83d389c328ceb8a16c8e3ca532a0c690c066dc - path: AGENTS.md - sha256: bc87a211bccacd2f48d52cd7ef8cd0e0dfedbf5e867b15040cb3430381614be5 + sha256: 844bec9925039e8d101d4cc10021e0e79834e1f572ebeebee3ba0feb0935d151 diff --git a/governance/rules/08_database_file_management.yaml b/governance/rules/08_database_file_management.yaml new file mode 100644 index 0000000..44deea7 --- /dev/null +++ b/governance/rules/08_database_file_management.yaml @@ -0,0 +1,11 @@ +schema_version: agents_rule.v1 +rule_id: DB_FILE_MANAGEMENT_V1 +title: Database file management and canonical path policy +summary: + - Canonical operational database files live under `src/quant_engine/`. + - `src/quant_engine/snapshot_admin.db` is the canonical snapshot admin workspace DB. + - `src/quant_engine/kis_data_collection.db` is the canonical KIS collector DB. + - `Temp/` is reserved for transient validation artifacts, smoke DBs, and ephemeral test outputs. + - `outputs/` is reserved for export, archive, and derived artifacts; it must not become the default operational source of truth. + - If a DB path exists in multiple locations, code and docs must point to the canonical `src/quant_engine/` copy unless an explicit migration or archive tool states otherwise. + - Legacy DB paths may appear only in documented migration/archive helpers and must not be used by normal operational entry points. diff --git a/package.json b/package.json index f065c3b..6c870a7 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,15 @@ "ops:prepare": "python tools/convert_xlsx_to_json.py", "ops:validate": "python tools/run_release_dag_v3.py --mode release", "ops:build": "python tools/build_bundle.py", - "ops:data-collect": "python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db outputs/kis_data_collection/kis_data_collection.db --output-json Temp/kis_data_collection_v1.json --kis-account real", + "ops:data-collect": "python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db src/quant_engine/kis_data_collection.db --output-json Temp/kis_data_collection_v1.json --kis-account real", "ops:sell-build": "python tools/build_qualitative_sell_inputs_v1.py --batch --workbook GatherTradingData.xlsx --kis-account real --apply", "ops:sell-satellite": "python tools/build_satellite_candidate_recommendations_v1.py --workbook GatherTradingData.xlsx --apply", "ops:sell-eval": "python tools/evaluate_qualitative_sell_strategy_accuracy_v1.py --sqlite-db outputs/qualitative_sell_strategy/qualitative_sell_strategy.db", "ops:sell-validate": "python tools/validate_qualitative_sell_strategy_pipeline_v1.py", "ops:postgres-stub": "python tools/generate_postgresql_upgrade_stub_v1.py", "ops:render": "python tools/render_operational_report.py --json GatherTradingData.json --output Temp/operational_report.md --report-json-output Temp/operational_report.json", - "ops:snapshot-web": "python tools/run_snapshot_admin_server_v1.py --reload --db outputs/snapshot_admin/snapshot_admin.db --seed GatherTradingData.json", + "ops:snapshot-web": "python tools/run_snapshot_admin_server_v1.py --reload --db src/quant_engine/snapshot_admin.db --seed GatherTradingData.json", + "ops:snapshot-web-watch": "python tools/run_snapshot_admin_server_v1.py --reload --db src/quant_engine/snapshot_admin.db --seed GatherTradingData.json", "ops:snapshot-validate": "python tools/validate_snapshot_admin_workflow_v1.py", "ops:snapshot-web-validate": "python tools/validate_snapshot_admin_web_v1.py", "ops:calibration-backlog": "python tools/build_calibration_priority_v1.py && python tools/build_calibration_change_ledger_v4.py && python tools/build_calibration_review_report_v1.py && python tools/validate_calibration_change_ledger_v1.py", diff --git a/spec/16_data_gaps_roadmap.yaml b/spec/16_data_gaps_roadmap.yaml index a4be3ed..3f95da2 100644 --- a/spec/16_data_gaps_roadmap.yaml +++ b/spec/16_data_gaps_roadmap.yaml @@ -225,6 +225,22 @@ phase_2_structural: workflow: - .gitea/workflows/snapshot_admin.yml verification: python tools/validate_snapshot_admin_web_v1.py + + S8_kis_open_api_token_cache: + priority: HIGH + status: DONE + purpose: KIS Open API access token을 호출마다 재발급하지 않고 SQLite 캐시로 재사용한다. + required_fields: + - access_token + - expires_at + - updated_at + implementation_plan: + step_1: 토큰 캐시를 수집 DB와 분리된 SQLite 파일로 저장한다. + step_2: TOKEN_REFRESH_SKEW_MINUTES=10 기준으로 기존 토큰을 재사용한다. + step_3: 동시 호출 시 토큰 재발급을 직렬화한다. + step_4: DB 오버라이드 경로를 환경변수로 노출한다. + step_5: 토큰 캐시 상태를 조회하는 진단 CLI를 제공한다. + implementation_note: '2026-06-23 구현 완료. src/quant_engine/kis_api_client_v1.py가 Temp/kis_tokens.db를 기본 캐시로 사용하고, KIS_TOKEN_DB_PATH로 오버라이드 가능하다. 갱신 임계값은 TOKEN_REFRESH_SKEW_MINUTES=10이며, tools/inspect_kis_token_cache_v1.py로 상태를 조회한다.' phase_3_enhancement: A1_daily_leader_scan_auto: priority: MEDIUM diff --git a/spec/27_bch_calibration_runbook.yaml b/spec/27_bch_calibration_runbook.yaml index a0db248..821b5d8 100644 --- a/spec/27_bch_calibration_runbook.yaml +++ b/spec/27_bch_calibration_runbook.yaml @@ -242,7 +242,7 @@ phase_4_honest_performance: \ print(f\\\"{k['metric']}: {k['current']}% (target={k['target_min']}%) [{status}]\\\ \")\n\"\n" expect: 출력 확인 - note: 'T+5 35.86% → 50% 목표: 보정루프 4단계 + note: 'T+5는 2026-06-21 기준 DATA_GATED(sample=0)이며, 과거 35.86% 캐시값은 현재 인용 금지. Step1: 표본 누적(30건) @@ -390,7 +390,7 @@ current_status_2026_05_30: phase_4_honest: COMPLETE design_score_labeled_unvalidated: true t1_match_rate_pct: 47.28 - t5_match_rate_pct: 35.86 + t5_match_rate_pct: null target_t5: 55.0 phase_5_integration: COMPLETE full_gate_exit: 0 diff --git a/spec/llm_reading_guide_v2.yaml b/spec/llm_reading_guide_v2.yaml new file mode 100644 index 0000000..2fea224 --- /dev/null +++ b/spec/llm_reading_guide_v2.yaml @@ -0,0 +1,49 @@ +meta: + title: LLM Reading Guide v2 + version: 2026-06-23 + role: canonical + purpose: "LLM이 문서를 읽는 순서와 신뢰도 tier를 고정한다." + +reading_order: + - path: spec/12_field_dictionary.yaml + tier: canonical + priority: 1 + - path: spec/14_raw_workbook_mapping.yaml + tier: canonical + priority: 1 + - path: spec/09_decision_flow.yaml + tier: canonical + priority: 1 + - path: spec/13_formula_registry.yaml + tier: canonical + priority: 1 + - path: spec/11_market_regime.yaml + tier: canonical + priority: 1 + - path: spec/02_data_contract.yaml + tier: canonical + priority: 1 + - path: spec/19_harness_contract.yaml + tier: canonical + priority: 1 + - path: governance/rules/00_core_locks.yaml + tier: adapter + priority: 2 + - path: governance/rules/01_harness_contract.yaml + tier: adapter + priority: 2 + - path: docs/WBS_9_1_F14_MIGRATION_COMPLETE_2026_06_22.md + tier: reference + priority: 3 + - path: docs/WBS_9_4_INCIDENT_RESPONSE_PLAYBOOK_2026_06_22.md + tier: reference + priority: 3 + - path: docs/ROADMAP_WBS.md + tier: reference + priority: 3 + +exclusions: + - docs/archive/ + - suggest/ + - artifacts/archive/ + diff --git a/tools/archive_legacy_databases.py b/tools/archive_legacy_databases.py index 85a5a0b..7804c98 100644 --- a/tools/archive_legacy_databases.py +++ b/tools/archive_legacy_databases.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 """ -데이터베이스 아카이빙 도구 +Database archive helper (migration/archive only). -레거시/테스트 DB 파일을 archive_db/로 이동하고 manifest 생성 +This tool exists to copy legacy or transient DB files into archive_db/ and +generate a manifest. It is not an operational source-of-truth manager. """ import shutil @@ -12,7 +13,7 @@ from datetime import datetime from typing import Dict, List class DatabaseArchiver: - """데이터베이스 아카이빙""" + """Legacy DB archive helper.""" def __init__(self): self.root = Path(".") @@ -38,7 +39,7 @@ class DatabaseArchiver: print(f"[OK] Created: {d.relative_to(self.root)}") def archive_outputs_kis_data_collection(self) -> None: - """outputs/kis_data_collection/ 아카이빙""" + """Archive legacy outputs/kis_data_collection/ contents.""" src = self.root / "outputs" / "kis_data_collection" if not src.exists(): print(f"[SKIP] {src.relative_to(self.root)} not found") @@ -61,7 +62,7 @@ class DatabaseArchiver: self.results["errors"].append(str(e)) def archive_outputs_snapshot_admin(self) -> None: - """outputs/snapshot_admin/ 의 smoke*.db 아카이빙""" + """Archive legacy outputs/snapshot_admin/ smoke*.db files.""" src_dir = self.root / "outputs" / "snapshot_admin" if not src_dir.exists(): print(f"[SKIP] {src_dir.relative_to(self.root)} not found") @@ -93,7 +94,7 @@ class DatabaseArchiver: self.results["errors"].append(str(e)) def archive_temp_files(self) -> None: - """Temp/ 의 테스트 DB 파일들 아카이빙""" + """Archive transient Temp/ test DB files.""" temp_dir = self.root / "Temp" if not temp_dir.exists(): print(f"[SKIP] {temp_dir.relative_to(self.root)} not found") @@ -134,7 +135,7 @@ class DatabaseArchiver: print(f"[SKIP] No test DB files found in {temp_dir.relative_to(self.root)}") def create_manifest(self) -> None: - """manifest.json 생성""" + """Create archive manifest.json.""" manifest = { "archive_date": self.timestamp, "created_at": datetime.now().isoformat(), diff --git a/tools/backup_data_feed_and_databases_v1.py b/tools/backup_data_feed_and_databases_v1.py new file mode 100644 index 0000000..ecfcf69 --- /dev/null +++ b/tools/backup_data_feed_and_databases_v1.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +if str(ROOT) not in sys.path: + sys.path.insert(0, str(ROOT)) + +from tools.backup_recovery_manager_v1 import BackupRecoveryManager + + +def main() -> int: + manager = BackupRecoveryManager() + manager.create_daily_backup() + manager.cleanup_old_backups() + manager.generate_backup_report() + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/backup_recovery_manager_v1.py b/tools/backup_recovery_manager_v1.py index 505de3e..b6bc7e0 100644 --- a/tools/backup_recovery_manager_v1.py +++ b/tools/backup_recovery_manager_v1.py @@ -371,7 +371,7 @@ class BackupRecoveryManager: print("RECENT BACKUPS:") print("-" * 80) for backup in self.results["backups"][-5:]: - status_marker = "✓" if backup.get("status") == "SUCCESS" else "✗" + status_marker = "[OK]" if backup.get("status") == "SUCCESS" else "[FAIL]" print( f"{status_marker} {backup.get('backup_name', 'N/A'):30} " f"| Size: {backup.get('total_size_mb', 0):8.2f}MB | " diff --git a/tools/check_sector_flow_reliability_ready_v1.py b/tools/check_sector_flow_reliability_ready_v1.py new file mode 100644 index 0000000..78e940d --- /dev/null +++ b/tools/check_sector_flow_reliability_ready_v1.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""Check whether WBS-9.5 can be promoted from DATA_GATED to DONE.""" +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +if str(ROOT) not in sys.path: + sys.path.insert(0, str(ROOT)) + +from tools.build_sector_flow_history_progress_v1 import DEFAULT_JSON, FORMULA_ID, main as build_progress_main # type: ignore + + +def _load(path: Path) -> dict: + if not path.exists(): + return {} + try: + payload = json.loads(path.read_text(encoding="utf-8")) + return payload if isinstance(payload, dict) else {} + except Exception: + return {} + + +def main() -> int: + ap = argparse.ArgumentParser(description="Check WBS-9.5 readiness.") + ap.add_argument("--input", default=str(ROOT / "Temp" / "sector_flow_history_progress_v1.json")) + ap.add_argument("--json", action="store_true") + args = ap.parse_args() + + input_path = Path(args.input) + input_path = input_path if input_path.is_absolute() else ROOT / input_path + if not input_path.exists(): + build_progress_main() + + payload = _load(input_path) + current = int(payload.get("current_dates") or 0) + target = int(payload.get("target_dates") or 30) + ready = current >= target and payload.get("status") == "DONE" + + result = { + "formula_id": "WBS_9_5_RELIABILITY_READY_V1", + "source": str(input_path), + "current_dates": current, + "target_dates": target, + "ready": ready, + "status": "DONE" if ready else "DATA_GATED", + "message": "WBS-9.5 can be promoted" if ready else "WBS-9.5 remains DATA_GATED", + } + + print(json.dumps(result, ensure_ascii=False, indent=2) if args.json else f"{result['status']}: {result['message']} ({current}/{target})") + return 0 if ready else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) + diff --git a/tools/monitor_wbs_progress_v1.py b/tools/monitor_wbs_progress_v1.py index edc48c2..0f17ef9 100644 --- a/tools/monitor_wbs_progress_v1.py +++ b/tools/monitor_wbs_progress_v1.py @@ -63,8 +63,8 @@ class WBSProgressMonitor: "8.5": { "name": "섹터 플로우 30일 검증", "status": "자동 누적", - "completion": "10%", - "days_accumulated": 3, + "completion": "70%", + "days_accumulated": 21, "days_needed": 30, "target_date": "2026-07-21", "days_remaining": self._days_until("2026-07-21") @@ -138,7 +138,7 @@ class WBSProgressMonitor: "9.5": { "name": "섹터 플로우 신뢰도", "status": "도구 완성", - "completion": "30%", + "completion": "70%", "depends_on": "WBS-8.5 (2026-07-21)", "metrics": ["Hit Rate", "Correlation", "Reliability Score"] }, @@ -219,7 +219,7 @@ class WBSProgressMonitor: "risk_factors": [ "WBS-8.1: 데이터 30건 축적까지 2.5주 더 필요", "WBS-8.4: 실거래 5건 필요 (현재 장애 대응 중)", - "WBS-8.5: 섹터 플로우 30일 누적 중 (2026-07-21 완료 예정)" + "WBS-8.5: 섹터 플로우 21/30일 누적 중 (2026-07-21 완료 예정)" ], "next_actions": [ "[OK] WBS-8 데이터 축적 모니터링 (자동 스케줄러)", diff --git a/tools/refactor_database_structure.py b/tools/refactor_database_structure.py index fcf02ac..0f5fc3b 100644 --- a/tools/refactor_database_structure.py +++ b/tools/refactor_database_structure.py @@ -2,7 +2,7 @@ """ 데이터베이스 구조 리팩토링 -파편화된 DB 파일들을 정리하고 단일 위치(src/quant_engine/)로 통합 +파편화된 DB 파일들을 정리하고 단일 canonical 위치(src/quant_engine/)를 기준으로 통합한다. """ import shutil @@ -149,8 +149,8 @@ archive_db/ ``` ### Step 3: Clean Obsolete References -- Remove imports from "outputs/kis_data_collection/kis_data_collection.db" -- Remove imports from "outputs/snapshot_admin/*.db" +- Remove imports from legacy non-canonical database paths +- Remove imports from archive/backup database paths - Update any code expecting these paths ### Step 4: Update Documentation @@ -166,11 +166,9 @@ archive_db/ - Simplified deployment ## Files to Delete (After Archiving) -- outputs/kis_data_collection/ (entire dir) -- outputs/snapshot_admin/smoke*.db (old test files) -- outputs/qualitative_sell_strategy/qualitative_sell_strategy.db -- Temp/snapshot_admin_*.db -- Temp/test_kis_data_collection.db +- archive only genuinely obsolete duplicate DBs +- keep canonical DBs in src/quant_engine/ +- keep Temp/ only for transient validation artifacts """ return plan @@ -187,7 +185,7 @@ def main(): print(plan) # 계획서 저장 - plan_file = Path("docs/DATABASE_CONSOLIDATION_PLAN_2026_06_23.md") + plan_file = Path("docs/archive/DATABASE_CONSOLIDATION_PLAN_2026_06_23.md") plan_file.parent.mkdir(parents=True, exist_ok=True) with open(plan_file, 'w', encoding='utf-8') as f: f.write(plan) diff --git a/tools/run_snapshot_admin_synology.sh b/tools/run_snapshot_admin_synology.sh index 3f8d6cb..4e37aa8 100644 --- a/tools/run_snapshot_admin_synology.sh +++ b/tools/run_snapshot_admin_synology.sh @@ -5,7 +5,7 @@ ROOT_DIR="${ROOT_DIR:-/volume1/projects/data_feed}" PYTHON_BIN="${PYTHON_BIN:-python}" HOST="${SNAPSHOT_ADMIN_HOST:-127.0.0.1}" PORT="${SNAPSHOT_ADMIN_PORT:-8787}" -DB_PATH="${SNAPSHOT_ADMIN_DB:-${ROOT_DIR}/outputs/snapshot_admin/snapshot_admin.db}" +DB_PATH="${SNAPSHOT_ADMIN_DB:-${ROOT_DIR}/src/quant_engine/snapshot_admin.db}" SEED_PATH="${SNAPSHOT_ADMIN_SEED:-${ROOT_DIR}/GatherTradingData.json}" AUTH_USER="${SNAPSHOT_ADMIN_AUTH_USER:-}" AUTH_PASSWORD="${SNAPSHOT_ADMIN_AUTH_PASSWORD:-}"