Some checks failed
PR Checklist / pr-checklist (pull_request) Failing after 1m20s
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.
86 lines
3.6 KiB
Python
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()
|