diff --git a/architecture_linter.py b/architecture_linter.py new file mode 100644 index 00000000..1e971cf0 --- /dev/null +++ b/architecture_linter.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +""" +Architecture Linter — Sovereignty Enforcement +Scans the codebase for banned providers, models, and API keys. +""" + +import os +import sys +import re + +BANNED_STRINGS = [ + r'anthropic', + r'claude', + r'api\.anthropic\.com', + r'ANTHROPIC_API_KEY', + r'claude-opus', + r'claude-sonnet', + r'claude-haiku' +] + +EXCEPTIONS = [ + 'BANNED_PROVIDERS.md', + 'architecture_linter.py', + 'training/', + 'evaluations/', + 'RELEASE_', + 'metrics_helpers.py' +] + +def is_exception(path): + for exc in EXCEPTIONS: + if exc in path: + return True + return False + +def check_file(path): + violations = [] + try: + with open(path, 'r', encoding='utf-8', errors='ignore') as f: + for i, line in enumerate(f, 1): + for pattern in BANNED_STRINGS: + if re.search(pattern, line, re.IGNORECASE): + violations.append((i, line.strip(), pattern)) + except Exception as e: + print(f"Error reading {path}: {e}") + return violations + +def main(): + print("--- Sovereignty Enforcement: Architecture Linter ---") + total_violations = 0 + + for root, dirs, files in os.walk('.'): + # Skip .git + if '.git' in dirs: + dirs.remove('.git') + + for file in files: + path = os.path.join(root, file) + if is_exception(path): + continue + + violations = check_file(path) + if violations: + print(f"\n[VIOLATION] {path}:") + for line_num, content, pattern in violations: + print(f" Line {line_num}: Found '{pattern}' -> {content}") + total_violations += 1 + + if total_violations > 0: + print(f"\nFAILED: Found {total_violations} sovereignty violations.") + sys.exit(1) + else: + print("\nPASSED: No banned providers detected.") + sys.exit(0) + +if __name__ == "__main__": + main()