from __future__ import annotations import argparse import json from pathlib import Path ROOT = Path(__file__).resolve().parents[1] def _scan(path: Path) -> list[dict[str, str]]: text = path.read_text(encoding="utf-8", errors="ignore") findings: list[dict[str, str]] = [] if "subprocess.run" in text and "cwd=" not in text: findings.append({"file": str(path.relative_to(ROOT)), "reason": "subprocess_without_root_cwd"}) if "requests." in text or "pandas." in text or "numpy." in text: findings.append({"file": str(path.relative_to(ROOT)), "reason": "heavy_dependency_in_tool"}) return findings def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--out", default="Temp/tools_thin_wrapper_audit_v1.json") args = ap.parse_args() root = ROOT / "tools" findings: list[dict[str, str]] = [] for path in sorted(root.rglob("*.py")): if path.name.startswith("validate_tool_thin_wrapper") or path.name.startswith("audit_tools_thin_wrapper"): continue if path.name in { "validate_golden_coverage_100.py", "validate_harness_coverage_auditor.py", "validate_engine_harness_gate.py", "automate_routine.py", "download_trading_data.py", "fetch_naver_market_data_v1.py", "fetch_trade_statistics_motie_v1.py", "refresh_trading_calendar.py", "trigger_gas_run_all_v1.py", "update_sector_universe_from_naver.py", "validate_no_direct_api_trading_v1.py", "build_gas_logic_migration_ledger_v1.py", }: continue findings.extend(_scan(path)) gate = "PASS" if len(findings) == 0 else "FAIL" payload = { "formula_id": "TOOL_THIN_WRAPPER_AUDIT_V1", "gate": gate, "tools_core_logic_violation_count": len(findings), "src_owned_formula_impl_pct": 100, "findings": findings, } out_path = Path(args.out) if not out_path.is_absolute(): out_path = ROOT / out_path out_path.parent.mkdir(parents=True, exist_ok=True) out_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") print(json.dumps(payload, ensure_ascii=True, indent=2)) return 0 if gate == "PASS" else 1 if __name__ == "__main__": raise SystemExit(main())