"""CI fitness-test for INV-G12 (docs/spec/X15 §4 / R4) — the Agent Platform Port. Hard gate: the intelligence layer (``mcp-server/src``) must contain ZERO Paperclip-specific symbols, and only ``web/agent_platform_port.py`` (+ the shell itself) may import the Paperclip client. The check lives in ``scripts/leak_guard.py`` (one canonical implementation, shared with the interactive ``spec-guard.sh`` hook); this test runs it and fails the build on any violation. Runs OFFLINE — pure source scan, no DB / no imports of the app. """ from __future__ import annotations import importlib.util from pathlib import Path REPO = Path(__file__).resolve().parents[2] _GUARD = REPO / "scripts" / "leak_guard.py" def _load_guard(): spec = importlib.util.spec_from_file_location("leak_guard", _GUARD) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) # type: ignore[union-attr] return mod def test_leak_guard_script_exists() -> None: assert _GUARD.is_file(), "scripts/leak_guard.py is missing (R4)" def test_intelligence_layer_is_platform_clean() -> None: """No Paperclip symbols in mcp-server/src; import seam intact (INV-G12).""" guard = _load_guard() violations = guard.scan() assert not violations, ( "INV-G12 leak-guard found Platform Port violations:\n" + "\n".join(f" • {v}" for v in violations) ) def test_guard_detects_an_injected_intelligence_leak(tmp_path: Path) -> None: """The guard must FAIL on a planted Paperclip symbol (so it can't rot).""" guard = _load_guard() probe = REPO / "mcp-server" / "src" / "legal_mcp" / "_leakguard_selftest.py" probe.write_text('BAD = "use pc.sh wakeup directly"\n', encoding="utf-8") try: violations = guard.scan() assert any("_leakguard_selftest.py" in v for v in violations), ( "leak-guard failed to detect a planted intelligence-layer leak" ) finally: probe.unlink(missing_ok=True)