Files
timmy-config/scripts/architecture_linter.py
perplexity 3af63cf172
Some checks failed
PR Checklist / pr-checklist (pull_request) Failing after 1m20s
enforce: Anthropic ban — linter, pre-commit, tests, and policy doc
Anthropic is not just removed — it is banned. This commit adds
enforcement at every gate to prevent re-introduction.

1. architecture_linter.py — 9 BANNED rules for Anthropic patterns
   (provider, model slugs, API endpoints, keys, model names).
   Scans all yaml/py/sh/json/md. Skips training data and historical docs.

2. pre-commit.py — scan_banned_providers() runs on every staged file.
   Blocks any commit that introduces Anthropic references.
   Exempt: training/, evaluations/, changelogs, historical cost data.

3. test_sovereignty_enforcement.py — TestAnthropicBan class with 4 tests:
   - No Anthropic in wizard configs
   - No Anthropic in playbooks
   - No Anthropic in fallback chain
   - No Anthropic API key in bootstrap

4. BANNED_PROVIDERS.md — Hard policy document. Golden state config.
   Replacement table. Exception list. Not advisory — mandatory.
2026-04-09 19:27:00 +00:00

86 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""Architecture Linter — Ensuring alignment with the Frontier Local Agenda.
Anthropic is BANNED. Not deprecated, not discouraged — banned.
Any reference to Anthropic as a provider, model, or API endpoint
in active configs is a hard failure.
"""
import os
import sys
import re
SOVEREIGN_RULES = [
# BANNED — hard failures
(r"provider:\s*anthropic", "BANNED: Anthropic provider reference. Anthropic is permanently banned from this system."),
(r"anthropic/claude", "BANNED: Anthropic model reference (anthropic/claude-*). Use kimi-k2.5 or google/gemini-2.5-pro."),
(r"api\.anthropic\.com", "BANNED: Direct Anthropic API endpoint. Anthropic is permanently banned."),
(r"ANTHROPIC_API_KEY", "BANNED: Anthropic API key reference. Remove all Anthropic credentials."),
(r"ANTHROPIC_TOKEN", "BANNED: Anthropic token reference. Remove all Anthropic credentials."),
(r"sk-ant-", "BANNED: Anthropic API key literal (sk-ant-*). Remove immediately."),
(r"claude-opus", "BANNED: Claude Opus model reference. Use kimi-k2.5."),
(r"claude-sonnet", "BANNED: Claude Sonnet model reference. Use kimi-k2.5."),
(r"claude-haiku", "BANNED: Claude Haiku model reference. Use google/gemini-2.5-pro."),
# Existing sovereignty rules
(r"https?://api\.openai\.com", "WARNING: Direct OpenAI API endpoint. Use local custom_provider instead."),
(r"provider:\s*openai", "WARNING: Direct OpenAI provider. Ensure fallback_model is configured."),
(r"api_key: ['\"][^'\"\s]{10,}['\"]", "SECURITY: Hardcoded API key detected. Use environment variables."),
]
# Files to skip (training data, historical docs, changelogs, tests that validate the ban)
SKIP_PATTERNS = [
"training/", "evaluations/", "RELEASE_v", "PERFORMANCE_",
"scores.json", "docs/design-log/", "FALSEWORK.md",
"test_sovereignty_enforcement.py", "test_metrics_helpers.py",
"metrics_helpers.py", # historical cost data
]
def should_skip(path: str) -> bool:
return any(skip in path for skip in SKIP_PATTERNS)
def lint_file(path: str) -> int:
if should_skip(path):
return 0
print(f"Linting {path}...")
content = open(path).read()
violations = 0
for pattern, msg in SOVEREIGN_RULES:
matches = list(re.finditer(pattern, content, re.IGNORECASE))
if matches:
print(f" [!] {msg}")
for m in matches[:3]: # Show up to 3 locations
line_no = content[:m.start()].count('\n') + 1
print(f" Line {line_no}: ...{content[max(0,m.start()-20):m.end()+20].strip()}...")
violations += 1
return violations
def main():
print("--- Architecture Linter (Anthropic BANNED) ---")
files = [f for f in sys.argv[1:] if os.path.isfile(f)]
if not files:
# If no args, scan all yaml/py/sh/json in the repo
for root, _, filenames in os.walk("."):
for fn in filenames:
if fn.endswith((".yaml", ".yml", ".py", ".sh", ".json", ".md")):
path = os.path.join(root, fn)
if not should_skip(path) and ".git" not in path:
files.append(path)
total_violations = sum(lint_file(f) for f in files)
banned = sum(1 for f in files for p, m in SOVEREIGN_RULES
if "BANNED" in m and re.search(p, open(f).read(), re.IGNORECASE)
and not should_skip(f))
print(f"\nLinting complete. Total violations: {total_violations}")
if banned > 0:
print(f"\n🚫 {banned} BANNED provider violation(s) detected. Anthropic is permanently banned.")
sys.exit(1 if total_violations > 0 else 0)
if __name__ == "__main__":
main()