#!/usr/bin/env python3 # ============================================================================= # Project : modelmap # File : tools/check_headers.py # Purpose : Fail if any tracked source file lacks the mandatory author header # Author : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Website : https://modelmap.io # Created : 2026-08-12 # Modified : 2026-08-12 # Platform : macOS / Apple Silicon (arm64) # License : All rights reserved (research code) # ============================================================================= """Header compliance checker (charter ยง0.1). Scans git-tracked source files and verifies each begins (after an optional shebang) with the standardized author header, or โ€” for markdown โ€” with YAML front matter carrying the author fields. Exits non-zero on any violation, so it can gate commits. """ from __future__ import annotations import subprocess import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent HASH_COMMENT_EXT = {".py", ".sh", ".zsh", ".yaml", ".yml", ".toml", ".cff"} SLASH_COMMENT_EXT = {".c", ".cpp", ".h", ".hpp", ".metal", ".swift", ".m", ".js", ".ts", ".css"} MARKDOWN_EXT = {".md"} HASH_COMMENT_NAMES = {"Makefile"} REQUIRED_TOKENS = ( "Project : modelmap", "Author : Simon-Pierre Boucher", "Contact : contact@spboucher.ai", ) MD_REQUIRED_TOKENS = ( "project: modelmap", "author: Simon-Pierre Boucher", "contact: contact@spboucher.ai", ) # Files that carry no comments / are data or licenses. CLAUDE.md is the # authoritative charter (its content is the spec itself, not a research doc). # README.md is the forge-rendered public summary โ€” front matter would display # as raw text; author attribution lives in its visible body instead. EXEMPT = {"LICENSE", ".gitignore", "package.json", "package-lock.json", "CLAUDE.md", "README.md"} def tracked_files() -> list[Path]: out = subprocess.run( ["git", "ls-files"], cwd=ROOT, capture_output=True, text=True, check=True ).stdout return [ROOT / line for line in out.splitlines() if line.strip()] def head_of(path: Path, n_bytes: int = 4096) -> str: try: return path.read_text(errors="replace")[:n_bytes] except OSError: return "" def check(path: Path) -> str | None: """Return an error string, or None if compliant / not applicable.""" name = path.name if name in EXEMPT: return None ext = path.suffix.lower() text = head_of(path) if text.startswith("#!"): text = text.split("\n", 1)[1] if "\n" in text else "" if ext in MARKDOWN_EXT: if not text.lstrip().startswith("---"): return "missing YAML front matter" missing = [t for t in MD_REQUIRED_TOKENS if t not in text] return f"front matter missing: {', '.join(missing)}" if missing else None if ext in HASH_COMMENT_EXT or ext in SLASH_COMMENT_EXT or name in HASH_COMMENT_NAMES: missing = [t for t in REQUIRED_TOKENS if t not in text] return f"header missing: {', '.join(missing)}" if missing else None return None # extension not governed by the header rule def main() -> int: errors: list[tuple[Path, str]] = [] for f in tracked_files(): if not f.is_file(): continue err = check(f) if err: errors.append((f, err)) if errors: print(f"check_headers: {len(errors)} non-compliant file(s):") for f, err in errors: print(f" {f.relative_to(ROOT)}: {err}") return 1 print("check_headers: all tracked source files carry the mandatory header.") return 0 if __name__ == "__main__": sys.exit(main())