File size: 4,487 Bytes
5e0b58b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"""Validate documentation links and canonical baseline claims."""

from __future__ import annotations

import argparse
import re
import subprocess
import sys
from pathlib import Path
from urllib.parse import unquote

REPO_ROOT = Path(__file__).resolve().parents[1]
DOCS_ROOT = REPO_ROOT / "docs"
ROADMAP_DOCS = (
    DOCS_ROOT / "08-roadmap" / "TODO.md",
    DOCS_ROOT / "08-roadmap" / "ROADMAP.md",
)
MARKDOWN_LINK = re.compile(r"(?<!!)\[[^\]]+\]\(([^)]+)\)")
BASELINE_PATTERN = re.compile(r"\*\*Baseline:\*\*\s+`?brain5d-core\s+([^`\s]+)`?")
COLLECTED_PATTERN = re.compile(r"(\d+)\s+tests collected")


def markdown_files() -> tuple[Path, ...]:
    """Return tracked documentation files in stable order."""
    return tuple(sorted(DOCS_ROOT.rglob("*.md")))


def check_markdown_links(files: tuple[Path, ...], root: Path = REPO_ROOT) -> list[str]:
    """Return one error for every missing local Markdown link target."""
    root = root.resolve()
    errors: list[str] = []
    for source in files:
        source = source.resolve()
        text = source.read_text(encoding="utf-8")
        for match in MARKDOWN_LINK.finditer(text):
            target = match.group(1).strip().strip("<>")
            target = target.split("#", 1)[0].strip()
            if not target or target.startswith(
                ("http://", "https://", "mailto:", "data:")
            ):
                continue
            candidate = (source.parent / unquote(target)).resolve()
            try:
                candidate.relative_to(root)
            except ValueError:
                errors.append(f"{source}: link escapes repository: {target}")
                continue
            if not candidate.exists():
                errors.append(f"{source}: missing link target: {target}")
    return errors


def check_baseline_version() -> list[str]:
    """Ensure the canonical roadmap baseline matches ``pyproject.toml``."""
    pyproject = (REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8")
    version_match = re.search(r'^version\s*=\s*"([^"]+)"', pyproject, re.MULTILINE)
    if version_match is None:
        return ["pyproject.toml: canonical project version is missing"]
    expected = version_match.group(1)
    errors: list[str] = []
    for path in ROADMAP_DOCS:
        text = path.read_text(encoding="utf-8")
        match = BASELINE_PATTERN.search(text)
        if match is None:
            errors.append(f"{path.relative_to(REPO_ROOT)}: baseline claim is missing")
        elif match.group(1) != expected:
            errors.append(
                f"{path.relative_to(REPO_ROOT)}: baseline {match.group(1)} "
                f"does not match {expected}"
            )
    return errors


def check_collected_test_count() -> list[str]:
    """Compare the documented collected-test count with pytest collection."""
    result = subprocess.run(
        [sys.executable, "-m", "pytest", "--collect-only", "-q"],
        cwd=REPO_ROOT,
        capture_output=True,
        text=True,
        check=False,
    )
    output = f"{result.stdout}\n{result.stderr}"
    actual_match = COLLECTED_PATTERN.search(output)
    if actual_match is None:
        return ["pytest collection did not report a collected test count"]

    declared_text = (DOCS_ROOT / "08-roadmap" / "TODO.md").read_text(encoding="utf-8")
    declared_match = re.search(r"- \[x\] (\d+) tests collected", declared_text)
    if declared_match is None:
        return ["docs/08-roadmap/TODO.md: collected-test claim is missing"]
    if declared_match.group(1) != actual_match.group(1):
        return [
            "docs/08-roadmap/TODO.md: collected-test claim "
            f"{declared_match.group(1)} does not match pytest ({actual_match.group(1)})"
        ]
    return []


def main() -> int:
    """Run documentation checks and return a shell-friendly status code."""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--check-tests",
        action="store_true",
        help="also compare the documented test count with pytest collection",
    )
    args = parser.parse_args()

    errors = check_markdown_links(markdown_files())
    errors.extend(check_baseline_version())
    if args.check_tests:
        errors.extend(check_collected_test_count())

    if errors:
        for error in errors:
            print(f"ERROR: {error}")
        return 1
    print("Documentation consistency checks passed.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())