#!/usr/bin/env python3 """Validate lightweight documentation synchronization rules.""" from __future__ import annotations import re import sys from pathlib import Path ROOT = Path(".") def read_text(path: Path, errors: list[str]) -> str: if not path.exists(): errors.append(f"missing file: {path}") return "" return path.read_text(encoding="utf-8") def main() -> int: errors: list[str] = [] docs_index = ROOT / "docs" / "README.md" readme = ROOT / "README.md" claude = ROOT / "CLAUDE.md" commands = ROOT / "docs" / "commands.md" docs_index_text = read_text(docs_index, errors) readme_text = read_text(readme, errors) claude_text = read_text(claude, errors) commands_text = read_text(commands, errors) if docs_index_text and "Single Source of Truth" not in docs_index_text: errors.append("docs/README.md: missing 'Single Source of Truth' section") if readme_text and "docs/README.md" not in readme_text: errors.append("README.md: missing docs/README.md routing link") if claude_text and "docs/README.md" not in claude_text: errors.append("CLAUDE.md: missing docs/README.md routing link") # Prevent volatile hard-coded scale numbers in summary docs. volatile_patterns: list[tuple[str, re.Pattern[str]]] = [ ("README.md", re.compile(r"\b\d+\s*개 테스트\b")), ("README.md", re.compile(r"\b\d+\s*tests\s+across\b", re.IGNORECASE)), ("README.md", re.compile(r"\(\d+\s*개 API\)")), ("README.md", re.compile(r"\(\d+\s*API endpoints?\)", re.IGNORECASE)), ("CLAUDE.md", re.compile(r"\b\d+\s*tests\s+across\b", re.IGNORECASE)), ("CLAUDE.md", re.compile(r"\(\d+\s*API endpoints?\)", re.IGNORECASE)), ("docs/commands.md", re.compile(r"Run full test suite with coverage\s*\(\d+", re.IGNORECASE)), ] text_by_name = { "README.md": readme_text, "CLAUDE.md": claude_text, "docs/commands.md": commands_text, } for name, pattern in volatile_patterns: text = text_by_name.get(name, "") if text and pattern.search(text): errors.append(f"{name}: contains volatile hard-coded scale text ({pattern.pattern})") # Command doc should list all dashboard endpoints exposed by app.py. for endpoint in ("/api/pnl/history", "/api/positions"): if commands_text and endpoint not in commands_text: errors.append(f"docs/commands.md: missing dashboard endpoint {endpoint}") if errors: print("[FAIL] docs sync validation failed") for err in errors: print(f"- {err}") return 1 print("[OK] docs sync validation passed") return 0 if __name__ == "__main__": sys.exit(main())