#!/usr/bin/env python3 """Check tenant-engine against the NetKingdom security layer model (§5, §11). Read-only. Makes two mechanical checks: 1. INTENT.md frontmatter declares the layer and governs; layer.yaml is a derived artifact (derived: true, derived_from: INTENT.md) that must agree with it after ASCII case-folding. The layer is one of §3's closed four tokens (Taxonomy, Tooling, Engine, Staff). Neither form carries a version of the standard or its companion in any key or value (GH-DEC-2026-017 §1-§5, GH-DEC-2026-020, amendments A9, A11, A12 r2). 2. No catalogued Tooling client (OpenBao, key-cape) appears in src/ unless it maps to a declared §5.1 / §5.2 / §5.3 entry. PostgreSQL / SQLite / httpx-to-flex-auth / httpx-to-audit-core are not Tooling contacts. They are listed in layer.yaml non_tooling_clients so the inventory is total. A12 r2 reaches content, not a key name: a `*_version` key (standard_version, companion_version, ...), a versioned `security-layer-model_v0.7.md` or companion path, and a bare `v0.7` token all count. `schema_version` and YAML comments are not reached. Stance, claims and classification maps (pep-stance.yaml, pip-claims.yaml) are not declarations; this check does not read them for A12 and must not (A12 r2, GH-DEC-2026-020 §3). Every run prints the standard text it checks against (VALIDATED_AGAINST) and the scope it ranged over, on success and on failure (GH-DEC-2026-020 §4). There is no emitted conformance record; the version lives in the run. Exit 0 clean, 1 undeclared contact, 2 declaration malformed. """ from __future__ import annotations import argparse import ast import re import sys from pathlib import Path try: import yaml except ImportError: # pragma: no cover - dev extra print("FAIL: PyYAML is required (pip install pyyaml)", file=sys.stderr) raise SystemExit(2) ROOT = Path(__file__).resolve().parents[1] SRC = ROOT / "src" / "tenant_engine" DECL = ROOT / "layer.yaml" INTENT = ROOT / "INTENT.md" # The standard text this checker was built and validated against # (GH-DEC-2026-020 §4, A12 r2: the version belongs to the run). Bump it when # the checker is re-validated against a newer accepted text. VALIDATED_AGAINST = ("net-kingdom/canon/standards/security-layer-model_v0.8.md " "@ net-kingdom f9e1611, with gate-house " "docs/amendments/v0.8-section-11-declaration-amendments.md " "A9, A11, A12 r2 @ gate-house 104f3fc") SCOPE = ("declaration = INTENT.md frontmatter + layer.yaml (every key and value); " "tooling scan = src/tenant_engine/**/*.py; " "not reached: pep-stance.yaml, pip-claims.yaml, comments, schema_version") # A12 r2: what reads as a version of this standard or its companion. _VERSION_KEY = re.compile(r"(?:^|_)version$", re.IGNORECASE) _EXEMPT_KEYS = {"schema_version"} # the declaration file's own schema, not reached _VERSIONED_PATH = re.compile( r"(?:security-layer-model|security[-_]companion|companion)[^\s]*?[_-]v?\d+(?:\.\d+)+", re.IGNORECASE) # A bare version token (v0.7). A declaration names no other versioned text, so # a bare token reads as this standard's. File-name suffixes of other # standards (…_v0.1.md) are preceded by '_' and are not matched here. _BARE_VERSION = re.compile(r"(? str: """ASCII case-fold only; two spellings of a token are one token.""" return str(value).translate(str.maketrans("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")) def version_findings(data: object, where: str) -> list[str]: """Every key or value of a parsed declaration that carries a version (A12 r2).""" found: list[str] = [] def walk(node: object, path: str) -> None: if isinstance(node, dict): for key, value in node.items(): sub = f"{path}.{key}" if path else str(key) if str(key) in _EXEMPT_KEYS: continue if _VERSION_KEY.search(str(key)): found.append(f"{where}: key '{sub}' is a version key") continue if isinstance(key, str): walk(key, sub + " (key)") walk(value, sub) elif isinstance(node, list): for i, item in enumerate(node): walk(item, f"{path}[{i}]") elif isinstance(node, str): if _VERSIONED_PATH.search(node) or _BARE_VERSION.search(node): found.append(f"{where}: '{path}' carries a version: {node.strip()[:80]!r}") walk(data, "") return found def _reject_versions(data: object, where: str) -> None: findings = version_findings(data, where) if findings: print("FAIL: a layer declaration must not carry a version of the standard " "or its companion, in any key or value (A12 r2, GH-DEC-2026-020)", file=sys.stderr) for line in findings: print(f" {line}", file=sys.stderr) raise SystemExit(2) def load_declaration() -> dict: if not DECL.exists(): print(f"FAIL: no declaration at {DECL.relative_to(ROOT)} (§11)", file=sys.stderr) raise SystemExit(2) try: data = yaml.safe_load(DECL.read_text()) except yaml.YAMLError as exc: print(f"FAIL: {DECL.name} is not parseable: {exc}", file=sys.stderr) raise SystemExit(2) from exc for key in ("layer", "role", "repository", "derived", "derived_from", "tooling_contacts"): if key not in data: print(f"FAIL: {DECL.name} missing required key '{key}'", file=sys.stderr) raise SystemExit(2) if data["derived"] is not True or data["derived_from"] != "INTENT.md": print(f"FAIL: {DECL.name} must be marked derived: true, derived_from: INTENT.md " "(§11, GH-DEC-2026-017 §1)", file=sys.stderr) raise SystemExit(2) _reject_versions(data, DECL.name) if _fold(data["layer"]) not in LAYER_VOCABULARY: print(f"FAIL: {DECL.name} layer {data['layer']!r} is outside the closed " f"vocabulary {sorted(LAYER_VOCABULARY)}", file=sys.stderr) raise SystemExit(2) if _fold(data["layer"]) != "engine": print(f"FAIL: declared layer is {data['layer']!r}, expected engine", file=sys.stderr) raise SystemExit(2) if _fold(data["role"]) != "pip": print(f"FAIL: declared role is {data['role']!r}, expected pip", file=sys.stderr) raise SystemExit(2) if data["repository"] != "tenant-engine": print(f"FAIL: repository is {data['repository']!r}", file=sys.stderr) raise SystemExit(2) return data def intent_frontmatter() -> dict: text = INTENT.read_text() if not text.startswith("---"): print("FAIL: INTENT.md has no YAML frontmatter", file=sys.stderr) raise SystemExit(2) block = text.split("---", 2)[1] data = yaml.safe_load(block) or {} if "layer" not in data: print("FAIL: INTENT.md frontmatter has no layer: key (§11)", file=sys.stderr) raise SystemExit(2) _reject_versions(data, "INTENT.md frontmatter") if _fold(data["layer"]) not in LAYER_VOCABULARY: print(f"FAIL: INTENT.md layer {data['layer']!r} is outside the closed " f"vocabulary {sorted(LAYER_VOCABULARY)}", file=sys.stderr) raise SystemExit(2) if _fold(data["layer"]) != "engine": print(f"FAIL: INTENT.md layer is {data.get('layer')!r}, expected Engine", file=sys.stderr) raise SystemExit(2) if _fold(data.get("role", "")) != "pip": print(f"FAIL: INTENT.md role is {data.get('role')!r}, expected PIP", file=sys.stderr) raise SystemExit(2) return data def imported_modules(path: Path) -> set[str]: try: tree = ast.parse(path.read_text()) except SyntaxError: return set() found: set[str] = set() for node in ast.walk(tree): if isinstance(node, ast.Import): found.update(alias.name.split(".")[0] for alias in node.names) elif isinstance(node, ast.ImportFrom) and node.level == 0 and node.module: found.add(node.module.split(".")[0]) return found def scan() -> list[tuple[Path, str, str]]: hits: list[tuple[Path, str, str]] = [] for path in sorted(SRC.rglob("*.py")): text = path.read_text() for module in sorted(imported_modules(path)): if module in TOOLING_IMPORTS: hits.append((path, module, TOOLING_IMPORTS[module])) if TOOLING_ARGV.search(text) or OPENBAO_ADDR.search(text): hits.append((path, "openbao-invocation", "OpenBao argv or address")) return hits def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--report", action="store_true") args = parser.parse_args() # GH-DEC-2026-020 §4: every run states what it checked against and its scope. print(f"checking against: {VALIDATED_AGAINST}") print(f"scope: {SCOPE}") decl = load_declaration() intent = intent_frontmatter() # A11: a disagreement between the governing and derived forms is a finding # in its own right, reported rather than resolved by precedence. Case is # folded first (A9), so what survives is a real disagreement. for key in ("layer", "role"): if _fold(decl[key]) != _fold(intent.get(key, "")): print(f"FAIL: {key} disagrees after case-fold — INTENT.md (governing): " f"{intent.get(key)!r}, layer.yaml (derived): {decl[key]!r}", file=sys.stderr) return 2 hits = scan() if args.report: print(f"tenant-engine — layer {intent['layer']}, role {intent['role']} " f"(INTENT.md governs; layer.yaml derived from {decl['derived_from']})") print(f" tooling contacts declared: {len(decl.get('tooling_contacts') or [])}") print(f" non-tooling clients: {len(decl.get('non_tooling_clients') or [])}") if hits: print("", file=sys.stderr) print("FAIL: undeclared Tooling-layer client (§11 undeclared violation)", file=sys.stderr) for path, module, what in hits: print(f" {path.relative_to(ROOT)}: {module} — {what}", file=sys.stderr) return 1 if decl.get("tooling_contacts"): print("FAIL: tooling_contacts is not empty; this engine claimed none", file=sys.stderr) return 1 if not args.report: print(f"OK: Engine/PIP declaration matches INTENT.md; no Tooling client in " f"{SRC.relative_to(ROOT)}; no version in the declaration; " f"validated against {VALIDATED_AGAINST}") return 0 if __name__ == "__main__": raise SystemExit(main())