| 1 | #!/usr/bin/env python3 |
| 2 | """Require the actual whole session group and a normal graph without host services. |
| 3 | |
| 4 | Source inclusion compiles all transitive helpers and inline tests, without stubs. |
| 5 | Cargo compilation remains the authority for Rust name/type resolution. This guard |
| 6 | also catches architecture drift which compiles but imports runtime infrastructure. |
| 7 | """ |
| 8 | from pathlib import Path |
| 9 | import argparse |
| 10 | import re |
| 11 | |
| 12 | ROOT = Path(__file__).resolve().parent.parent |
| 13 | GROUP = "crates/tui/src/commands/groups/session/mod.rs" |
| 14 | PROOF = "tests/portable-session/src/commands/groups/mod.rs" |
| 15 | ALLOWED_WORKSPACE = { |
| 16 | "codewhale-portable-session", "codewhale-command-contract", |
| 17 | "codewhale-protocol", "codewhale-sanitize", |
| 18 | } |
| 19 | FORBIDDEN_SERVICES = { |
| 20 | "tokio", "reqwest", "hyper", "rusqlite", "sqlx", "keyring", "dbus", |
| 21 | "zbus", "secret-service", "ratatui", "crossterm", |
| 22 | } |
| 23 | |
| 24 | |
| 25 | def graph_violations(graph): |
| 26 | names = {line.split()[0] for line in graph.splitlines() if line.strip()} |
| 27 | return [f"unapproved normal dependency: {name}" for name in sorted(names) |
| 28 | if (name.startswith("codewhale-") and name not in ALLOWED_WORKSPACE) |
| 29 | or name in FORBIDDEN_SERVICES] |
| 30 | |
| 31 | |
| 32 | def violations(root): |
| 33 | errors = [] |
| 34 | wrapper = root / PROOF |
| 35 | text = wrapper.read_text() |
| 36 | includes = re.findall(r'#\[path\s*=\s*"([^"]+)"\]\s*pub mod session;', text) |
| 37 | if len(includes) != 1 or (wrapper.parent / includes[0]).resolve() != (root / GROUP).resolve(): |
| 38 | errors.append("proof must include the actual session group root, not a selected leaf or stub") |
| 39 | for path in sorted((root / GROUP).parent.rglob("*.rs")): |
| 40 | # Exclude comments, but retain cfg(test) code: its dependency closure matters. |
| 41 | source = "\n".join(line.split("//")[0] for line in path.read_text().splitlines()) |
| 42 | if re.search(r'\b(?:crate::(?:tui|remote_control|commands::(?:traits|contract|CommandResult))|AppAction|codewhale_(?:core|secrets|tui|runtime))\b', source): |
| 43 | errors.append(f"host dependency in session closure: {path.relative_to(root)}") |
| 44 | return errors |
| 45 | |
| 46 | |
| 47 | def main(): |
| 48 | parser = argparse.ArgumentParser(description=__doc__) |
| 49 | parser.add_argument("--graph", type=Path) |
| 50 | args = parser.parse_args() |
| 51 | errors = violations(ROOT) |
| 52 | if args.graph: |
| 53 | errors += graph_violations(args.graph.read_text()) |
| 54 | for error in errors: |
| 55 | print(f"[session-proof] FAIL: {error}") |
| 56 | if not errors: |
| 57 | print("[session-proof] PASS: actual complete group and approved dependency boundary") |
| 58 | return bool(errors) |
| 59 | |
| 60 | |
| 61 | if __name__ == "__main__": |
| 62 | raise SystemExit(main()) |
| 63 |