Files
chanora/tools/validate_docs.py
T
EdisonJwa f1bc9a6c85 chore(repo): initial baseline import (docs v0.9.2 + bootstrap)
Imports the v0.9.2 documentation baseline and the bootstrap files
required by docs/governance/repository-bootstrap-plan.md v0.1.0 §3,
minus the justfile (added in the next commit).

This commit establishes the git history for the project. All previous
work lived only as filesystem state with no version control.
2026-05-14 12:25:33 +08:00

91 lines
2.8 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
DOCS = ROOT / "docs"
ID_FILES = {
"SysRS": DOCS / "requirements" / "sysrs.md",
"SysDes": DOCS / "architecture" / "sysdes.md",
"SRS": DOCS / "requirements" / "srs.md",
"SAD": DOCS / "architecture" / "sad.md",
"SDD": DOCS / "architecture" / "sdd.md",
}
def read(path: Path) -> str:
if not path.exists():
return ""
return path.read_text(encoding="utf-8")
def defined_ids(prefix: str, text: str) -> set[str]:
return set(re.findall(rf"\*\*({prefix}-\d{{3}})\*\*:", text))
def all_refs(prefix: str, text: str) -> set[str]:
return set(re.findall(rf"{prefix}-\d{{3}}", text))
def main() -> int:
if not DOCS.exists():
print("docs/ does not exist; skipping documentation validation.")
return 0
all_text = "\n".join(p.read_text(encoding="utf-8") for p in DOCS.rglob("*.md"))
defined = {
prefix: defined_ids(prefix, read(path))
for prefix, path in ID_FILES.items()
}
failed = False
for prefix, ids in defined.items():
refs = all_refs(prefix, all_text)
undefined = sorted(refs - ids)
if undefined:
failed = True
print(f"[FAIL] Undefined {prefix} references: {', '.join(undefined)}")
else:
print(f"[OK] {prefix}: {len(ids)} defined, 0 undefined references")
srs = read(DOCS / "requirements" / "srs.md")
sad = read(DOCS / "architecture" / "sad.md")
sdd = read(DOCS / "architecture" / "sdd.md")
checks = [
("SRS direct SysRS references", re.findall(r"SysRS-\d{3}", srs)),
("SAD direct SysRS references", re.findall(r"SysRS-\d{3}", sad)),
("SAD direct SysDes references", re.findall(r"SysDes-\d{3}", sad)),
("SDD direct SysRS references", re.findall(r"SysRS-\d{3}", sdd)),
("SDD direct SysDes references", re.findall(r"SysDes-\d{3}", sdd)),
("SDD direct SRS references", re.findall(r"SRS-\d{3}", sdd)),
]
for name, matches in checks:
if matches:
failed = True
print(f"[FAIL] {name}: {len(matches)}")
else:
print(f"[OK] {name}: 0")
old_names = re.findall(r"CHANORA_[A-Za-z0-9_]+_v\d+\.\d+(?:\.\d+)?\.md", all_text)
if old_names:
failed = True
print(f"[FAIL] Old package-style filenames found: {len(set(old_names))}")
else:
print("[OK] No old package-style filenames found")
cjk = re.findall(r"[\u4e00-\u9fff]", all_text)
if cjk:
failed = True
print(f"[FAIL] CJK characters found: {len(cjk)}")
else:
print("[OK] English-only CJK check passed")
return 1 if failed else 0
if __name__ == "__main__":
sys.exit(main())