WBS-7.9, WBS-7.10: KIS data collection fallback resiliency & deploy_gas.py pre-deploy thin-adapter lint integration

This commit is contained in:
2026-06-22 10:25:23 +09:00
parent 2af3681fb9
commit 93f046c76c
4 changed files with 61 additions and 0 deletions
+2
View File
@@ -95,6 +95,8 @@ source_of_truth_order:
28: "prompts/*.md — reusable prompt entrypoints for analysis/review" 28: "prompts/*.md — reusable prompt entrypoints for analysis/review"
29: "examples/*.yaml and examples/examples.jsonl — examples are illustrative and never override rules" 29: "examples/*.yaml and examples/examples.jsonl — examples are illustrative and never override rules"
30: "tests/*.yaml — consistency checks for future edits" 30: "tests/*.yaml — consistency checks for future edits"
31: "spec/03_risk_policy.yaml — legacy redirect stub for backward compatibility"
32: "spec/04_strategy_rules.yaml — legacy redirect stub for backward compatibility"
load_sequence: load_sequence:
STEP_1_always: STEP_1_always:
+2
View File
@@ -1198,6 +1198,8 @@ python tools/update_sector_universe_from_naver.py --limit 10 --apply # 원본
[x] WBS-7.5: OVERHANG_PRESSURE_V1 폴백 비례화 (2026-06-21 완료, avg_volume_5d 비례식 + EXPERT_PRIOR 등록) [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.6: 슬리피지 실측 캡처 스캐폴딩 구축 완료 (2026-06-21, 비교 자체는 체결 5건 누적 대기)
[x] WBS-7.8: ETF NAV 수집경로 재검토 + 공매도 잔고율 운영절차 문서화 (2026-06-21 완료) [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 탑재)
``` ```
--- ---
@@ -225,8 +225,21 @@ def _collect_one(row: dict[str, Any], *, kis_account: str, include_naver: bool,
normalized.setdefault("relative_return_20d", naver.get("relative_return_20d")) normalized.setdefault("relative_return_20d", naver.get("relative_return_20d"))
normalized.setdefault("volume_ratio_5d", naver.get("volume_ratio_5d")) normalized.setdefault("volume_ratio_5d", naver.get("volume_ratio_5d"))
normalized.setdefault("naver_price_status", naver.get("status")) normalized.setdefault("naver_price_status", naver.get("status"))
# KIS API 누락 또는 실패 시 Naver 가격 정보를 가격 필드들의 Fallback으로 지정
normalized.setdefault("current_price", naver.get("close"))
normalized.setdefault("open", naver.get("open"))
normalized.setdefault("high", naver.get("high"))
normalized.setdefault("low", naver.get("low"))
normalized.setdefault("volume", naver.get("volume"))
provenance["source_priority"].append("naver_finance") provenance["source_priority"].append("naver_finance")
# KIS 및 Naver 가격 정보가 모두 없을 시, GatherTradingData.json 원본 시드 가격을 최후의 수단으로 복원
normalized.setdefault("current_price", _coerce_float(row.get("current_price") or row.get("Current_Price") or row.get("close")))
normalized.setdefault("open", _coerce_float(row.get("open") or row.get("Open")))
normalized.setdefault("high", _coerce_float(row.get("high") or row.get("High")))
normalized.setdefault("low", _coerce_float(row.get("low") or row.get("Low")))
normalized.setdefault("volume", _coerce_float(row.get("volume") or row.get("Volume")))
normalized.setdefault("collection_as_of", _kst_now_iso()) normalized.setdefault("collection_as_of", _kst_now_iso())
return normalized, provenance return normalized, provenance
+44
View File
@@ -248,14 +248,58 @@ def sync_sector_insights_via_clasp_run() -> bool:
return True return True
def run_pre_deploy_linter() -> bool:
print("[deploy_gas] Running pre-deploy gas thin-adapter audit...")
# Run auditor v1
audit_res = subprocess.run(
["python", "tools/audit_gas_thin_adapter_v1.py"],
cwd=str(ROOT),
shell=True,
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
)
if audit_res.returncode != 0:
print("[deploy_gas] Error: tools/audit_gas_thin_adapter_v1.py failed")
print(audit_res.stdout)
print(audit_res.stderr)
return False
# Run validator v2
validate_res = subprocess.run(
["python", "tools/validate_gas_thin_adapter_v2.py"],
cwd=str(ROOT),
shell=True,
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
)
print(validate_res.stdout)
if validate_res.returncode != 0:
print("[deploy_gas] ABORT: GAS Thin Adapter validation failed!")
if validate_res.stderr:
print("STDERR: " + validate_res.stderr)
return False
print("[deploy_gas] Pre-deploy thin-adapter audit PASS")
return True
def main() -> None: def main() -> None:
parser = argparse.ArgumentParser(description="GAS auto-deploy") parser = argparse.ArgumentParser(description="GAS auto-deploy")
parser.add_argument("--dry-run", action="store_true", help="List files without writing") parser.add_argument("--dry-run", action="store_true", help="List files without writing")
parser.add_argument("--skip-push", action="store_true", help="Bundle only, skip clasp push") parser.add_argument("--skip-push", action="store_true", help="Bundle only, skip clasp push")
parser.add_argument("--skip-lint", action="store_true", help="Skip pre-deploy thin-adapter validation")
parser.add_argument("--sync-sector-insights", action="store_true", help="POST sector insight JSON to a deployed GAS web app") parser.add_argument("--sync-sector-insights", action="store_true", help="POST sector insight JSON to a deployed GAS web app")
parser.add_argument("--webapp-url", default=os.environ.get("GAS_WEBAPP_URL", DEFAULT_WEBAPP_URL), help="Apps Script web app URL for sync POST") parser.add_argument("--webapp-url", default=os.environ.get("GAS_WEBAPP_URL", DEFAULT_WEBAPP_URL), help="Apps Script web app URL for sync POST")
args = parser.parse_args() args = parser.parse_args()
if not args.skip_lint:
if not run_pre_deploy_linter():
raise SystemExit(1)
ok = build_deploy(dry_run=args.dry_run) ok = build_deploy(dry_run=args.dry_run)
if not ok: if not ok:
print("[deploy_gas] Some source files missing -- check warnings above") print("[deploy_gas] Some source files missing -- check warnings above")