#!/usr/bin/env python3 """Enforce lightweight naming/layout hygiene for Soteria sources.""" from __future__ import annotations import argparse import re import sys from pathlib import Path GENERIC_TOKENS = { "tmp", "temp", "foo", "bar", "baz", "misc", "new", "old", "final", "wip", } MAX_DEPTH = 10 def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument("--root", default=".") return parser.parse_args() def iter_source_files(root: Path) -> list[Path]: files: list[Path] = [] for rel_root, suffixes in ( ("cmd", {".go"}), ("internal", {".go"}), ("web/src", {".ts", ".tsx", ".css"}), ): base = root / rel_root if not base.exists(): continue for path in sorted(base.rglob("*")): if not path.is_file(): continue if path.suffix not in suffixes: continue rel = path.relative_to(root).as_posix() if rel.endswith("_test.go"): continue if rel.startswith("internal/server/ui-dist/"): continue files.append(path) return files def filename_tokens(path: Path) -> list[str]: stem = path.stem return [tok for tok in re.split(r"[-_]", stem) if tok] def main() -> int: args = parse_args() root = Path(args.root).resolve() violations: list[str] = [] files = iter_source_files(root) for path in files: rel = path.resolve().relative_to(root).as_posix() depth = len(Path(rel).parts) if depth > MAX_DEPTH: violations.append(f"{rel}: depth {depth} exceeds {MAX_DEPTH}") tokens = {tok.lower() for tok in filename_tokens(path)} bad = sorted(tokens.intersection(GENERIC_TOKENS)) if bad: violations.append(f"{rel}: non-descriptive filename token(s): {', '.join(bad)}") if violations: print("Structure hygiene check failed:") for item in violations: print(item) return 1 print("Structure hygiene checks: ok") return 0 if __name__ == "__main__": sys.exit(main())