scripts.check_wiring

Reference › scripts › check_wiring

The wiring gate, ruling RUL-023: when a feature is requested, that means wiring it in, not leaving an unconnected file. The gate looks at the files a change adds and fails when an added piece is not connected. It reads the declared gates through rulings_lint, and tests/scripts/test_check_wiring.py imports it.

Invocation: main takes --repo-root (default the current directory), --rulings-dir (override of the rulings directory) and --json. run_check returns an exit code and a report.

diff_entries reads the change from git (_git, with DiffError on failure) and added_paths selects the added files. Four checks follow. W1 (check_w1_claude_hooks) requires an added Claude hook to be registered in the settings, using _iter_hook_commands. W2 (check_w2_scripts_hooks) requires an added script that works as a hook to be wired. W3 (check_w3_src_modules) requires an added source module to be imported by something, and _orphan_modules finds the modules nothing imports. W4 (check_w4_gate_scripts) requires every gate script declared in a ruling to be named in a runner. Each failure is a Violation.

At a glance

Source scripts/check_wiring.py
Lines of code 469
Classes 2
Functions 15
Imports (internal) scripts.rulings_lint
Imported by none
Tests that import it tests/scripts/test_check_wiring.py

Class diagram

Class diagram scripts.check_wiring

Dependency diagram

Dependency diagram scripts.check_wiring

API

Classes

DiffError

Bases: Exception · Line 80

git diff --name-status master...HEAD could not be computed — not a git repository, git is unavailable, or master doesn't exist.

Violation (dataclass)

Decorators: dataclass · Line 86

Attribute Type Default
check str
artifact str
message str

Functions

Function Signature Summary
_git _git(repo_root: Path, **argv: str) -> str Run one git command, or raise :class:DiffError.
diff_entries diff_entries(repo_root: Path) -> list[tuple[str, str]] (status, path) for everything THIS BRANCH introduces over master.
added_paths added_paths(entries: list[tuple[str, str]]) -> list[str]
_iter_hook_commands _iter_hook_commands(settings: object) -> list[str] Every command string nested under the hooks table of a parsed .claude/settings.json — any event, any matcher, per RUL-023's W1.
check_w1_claude_hooks check_w1_claude_hooks(added: list[str], repo_root: Path) -> list[Violation] A file newly added under .claude/hooks/ must be registered in .claude/settings.json (any event/matcher) — otherwise the live dispatcher never loads it, exactly the…
check_w2_scripts_hooks check_w2_scripts_hooks(added: list[str], repo_root: Path) -> list[Violation] A file newly added under scripts/hooks/ (other than the installer itself) must be referenced by scripts/hooks/install-git-hooks.sh — otherwise running the installer never activates it.
_module_dotted_name _module_dotted_name(rel_path: str) -> str | None src/pkg/sub/mod.py -> pkg.sub.mod. None for anything not a Python file under src/ — INCLUDING __init__.py: a package marker is never independently "imported" by name (``from…
_orphan_modules _orphan_modules(repo_root: Path) -> set[str] Shell out to the sanctioned AST toolkit's orphans query — the SAME production-reachability BFS that gates AGENTS.md Hard Rule 8 elsewhere (find_orphan_modules: roots are first-party imports…
check_w3_src_modules check_w3_src_modules(added: list[str], repo_root: Path) -> list[Violation] A newly added src/ module must be reachable from at least one production entrypoint via the transitive first-party import graph — an import from a file that is itself unreachable does not count…
_gate_declarations _gate_declarations(rulings_dir: Path) -> list[tuple[str, str, str]] (ruling_id, script_rel_path, ruling_file) for every gate <script> <rule-id> enforcement directive across every ruling.
_runner_texts _runner_texts(repo_root: Path) -> list[str]
check_w4_gate_scripts check_w4_gate_scripts(added: list[str], repo_root: Path, rulings_dir: Path) -> list[Violation] A scripts/*.py gate script that THIS diff adds, and that some ruling declares as its gate mechanism, must be invoked by at least one runner (health_check.py / .claude/hooks / scripts/hooks /…
run_check run_check(repo_root: Path, rulings_dir: Path | None = None) -> tuple[int, dict] Injection seam: tests pass a throwaway tmp_path repo root (and, optionally, an explicit rulings directory) — never touches the live checkout.
_print_text_report _print_text_report(report: dict) -> None
main main(argv: list[str] | None = None) -> int

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/scripts/check_wiring.py

#!/usr/bin/env python3
"""Wiring gate — RUL-023 ("when I ask for a feature that means wiring the
damn thing not just code vomit", verbatim, 2026-07-27).

See ``~/workspace/knowledge-wiki/rulings/RUL-023-feature-means-wired.md`` for
the ruling text and the incident (in a single session three capabilities were
built and reported complete while inert: a git hook whose ``core.hooksPath``
pointed elsewhere, a ``.claude/hooks/`` script registered only in a
worktree's tracked ``settings.json``, and a gate no runner invoked). RUL-012
already defines *done* as wired+deployed; this mechanically checks it for the
artifact classes that are easiest to leave inert.

Scope: everything this gate flags is scoped to **the current branch's diff
against ``master``** (``git diff --name-status master...HEAD``), so it grades
what THIS change added, never the tree's accumulated history. No diff against
master -> exit 0, nothing to grade.

Checks (see the module docstring of each ``check_w*`` function for detail):

    W1  a file added under ``.claude/hooks/`` must be registered in
        ``.claude/settings.json``.
    W2  a file added under ``scripts/hooks/`` (other than the installer
        itself) must be referenced by ``scripts/hooks/install-git-hooks.sh``.
    W3  a new ``.py`` module added under ``src/`` must be TRANSITIVELY
        reachable from a production entrypoint — the same reachability BFS
        the AST toolkit's ``orphans`` query runs for AGENTS.md Hard Rule 8
        (``find_orphan_modules``: roots are first-party imports of top-level
        ``scripts/*.py``, ``pyproject.toml`` entry-points, ``__main__``-
        guarded modules, and ``orchestrata_ultimate.__main__``). An import
        from a file that is itself unreachable does not count — this does
        NOT hand-roll a second reachability engine.
    W4  a ``scripts/*.py`` file added in this diff that some ruling under
        RULINGS_DIR declares as its ``gate`` mechanism must be invoked by
        ``scripts/health_check.py``, a file under ``.claude/hooks/``, a file
        under ``scripts/hooks/``, or ``scripts/run_tests.py``.

RULINGS_DIR is read with ``os.environ.get(...)`` at call time (RUL-003) via
the SAME resolution + missing-directory SKIP behaviour as
``scripts/rulings_lint.py`` (imported from there, not reimplemented) — a
machine without the knowledge-wiki clone must not go false-red: W4 is
skipped, W1-W3 still run.

Exit 0 clean (or skipped, with a reason). Exit 1 on any violation, one line
per finding: check id, artifact path, and the exact wiring site it is
missing. Not a git repository, git unavailable, or no divergence from
master: exit 0 with a printed reason, never crash.

Usage:
    python scripts/check_wiring.py [--repo-root PATH] [--rulings-dir PATH] [--json]
"""

from __future__ import annotations

import argparse
import json
import re
import subprocess
import sys
from dataclasses import asdict, dataclass
from pathlib import Path

# Repo root on sys.path so `scripts` (imported below) resolves regardless of
# how this file is invoked (direct `python scripts/check_wiring.py`, `-m`,
# or as a test import) — same pattern as scripts/refactor.py.
_REPO_ROOT = Path(__file__).resolve().parent.parent
if str(_REPO_ROOT) not in sys.path:
    sys.path.insert(0, str(_REPO_ROOT))

from scripts.rulings_lint import (  # noqa: E402
    _default_rulings_dir,
    _extract_enforcement_lines,
)

_RUL_ID_RE = re.compile(r"^(RUL-\d+)")
_ORPHAN_LINE_RE = re.compile(r"^  ([A-Za-z_][\w.]*)$")
_RUNNER_RELATIVE_PATHS = ("scripts/health_check.py", "scripts/run_tests.py")
_HOOK_DIRS = (".claude/hooks", "scripts/hooks")


class DiffError(Exception):
    """``git diff --name-status master...HEAD`` could not be computed —
    not a git repository, git is unavailable, or ``master`` doesn't exist."""


@dataclass
class Violation:
    check: str
    artifact: str
    message: str


def _git(repo_root: Path, *argv: str) -> str:
    """Run one git command, or raise :class:`DiffError`."""
    try:
        result = subprocess.run(
            ["git", *argv],
            cwd=repo_root,
            capture_output=True,
            text=True,
            timeout=30,
        )
    except (OSError, subprocess.TimeoutExpired) as exc:
        raise DiffError(str(exc)) from exc
    if result.returncode != 0:
        raise DiffError(result.stderr.strip() or f"git {argv[0]} failed")
    return result.stdout


def diff_entries(repo_root: Path) -> list[tuple[str, str]]:
    """``(status, path)`` for everything THIS BRANCH introduces over master.

    Graded against the MERGE BASE, so master's own history is never graded,
    and against the WORKING TREE, so work that has not been committed yet is
    graded too.

    That second half is the whole point. The previous implementation ran
    ``git diff --name-status master...HEAD`` — a commit-range diff. Agents in
    this repo are forbidden to commit, so a session's entire body of work sits
    uncommitted in the working tree with ``HEAD == master``; the range was
    empty and this gate reported ``SKIP — no diff against master`` and exited
    0. Measured 2026-08-12 on feat/session-262-gate-and-provenance: 147 files
    changed vs master, and the gate graded ZERO of them. A gate that
    self-disables exactly when there is work to grade is a false green.

    Three sources are unioned, later ones winning on conflict:

    1. ``<merge-base>..HEAD`` — commits the branch already carries.
    2. ``git diff --name-status <merge-base>`` — the working tree (staged and
       unstaged alike) against that same base.
    3. ``git ls-files --others --exclude-standard`` — brand-new untracked
       files, graded ``A``. Without these a newly written script is invisible.

    Raises ``DiffError`` when git cannot run at all (missing git, not a repo,
    no ``master`` ref) — the caller turns that into a SKIP, never a crash.
    """
    base = _git(repo_root, "merge-base", "master", "HEAD").strip()
    if not base:
        raise DiffError("could not resolve merge-base of master and HEAD")

    # Ordered, so a later source overrides an earlier one for the same path
    # while the first-seen order is preserved for stable reporting.
    merged: dict[str, str] = {}

    def _absorb(raw: str) -> None:
        for line in raw.splitlines():
            if not line.strip():
                continue
            parts = line.split("\t")
            merged[parts[-1]] = parts[0]  # rename lines: STATUS OLD NEW

    _absorb(_git(repo_root, "diff", "--name-status", f"{base}..HEAD"))
    _absorb(_git(repo_root, "diff", "--name-status", base))

    for line in _git(
        repo_root, "ls-files", "--others", "--exclude-standard"
    ).splitlines():
        path = line.strip()
        if path:
            merged[path] = "A"

    return [(status, path) for path, status in merged.items()]


def added_paths(entries: list[tuple[str, str]]) -> list[str]:
    return [path for status, path in entries if status == "A"]


def _iter_hook_commands(settings: object) -> list[str]:
    """Every ``command`` string nested under the ``hooks`` table of a parsed
    ``.claude/settings.json`` — any event, any matcher, per RUL-023's W1."""
    commands: list[str] = []
    if not isinstance(settings, dict):
        return commands
    hooks_section = settings.get("hooks")
    if not isinstance(hooks_section, dict):
        return commands
    for entries in hooks_section.values():
        if not isinstance(entries, list):
            continue
        for entry in entries:
            if not isinstance(entry, dict):
                continue
            for h in entry.get("hooks", []):
                if isinstance(h, dict) and isinstance(h.get("command"), str):
                    commands.append(h["command"])
    return commands


def check_w1_claude_hooks(added: list[str], repo_root: Path) -> list[Violation]:
    """A file newly added under ``.claude/hooks/`` must be registered in
    ``.claude/settings.json`` (any event/matcher) — otherwise the live
    dispatcher never loads it, exactly the ``require-design-before-build.sh``
    incident RUL-023 names."""
    hook_files = [p for p in added if p.startswith(".claude/hooks/")]
    if not hook_files:
        return []
    settings_path = repo_root / ".claude" / "settings.json"
    commands: list[str] = []
    if settings_path.exists():
        try:
            commands = _iter_hook_commands(
                json.loads(settings_path.read_text(encoding="utf-8"))
            )
        except json.JSONDecodeError:
            commands = []
    violations = []
    for rel in hook_files:
        basename = Path(rel).name
        if not any(basename in cmd for cmd in commands):
            violations.append(
                Violation(
                    "W1",
                    rel,
                    f"{basename} must be registered under a PreToolUse/"
                    f"PostToolUse/Stop matcher in .claude/settings.json",
                )
            )
    return violations


def check_w2_scripts_hooks(added: list[str], repo_root: Path) -> list[Violation]:
    """A file newly added under ``scripts/hooks/`` (other than the installer
    itself) must be referenced by ``scripts/hooks/install-git-hooks.sh`` —
    otherwise running the installer never activates it."""
    hook_files = [
        p
        for p in added
        if p.startswith("scripts/hooks/") and Path(p).name != "install-git-hooks.sh"
    ]
    if not hook_files:
        return []
    installer = repo_root / "scripts" / "hooks" / "install-git-hooks.sh"
    text = installer.read_text(encoding="utf-8") if installer.exists() else ""
    violations = []
    for rel in hook_files:
        basename = Path(rel).name
        if basename not in text:
            violations.append(
                Violation(
                    "W2",
                    rel,
                    f"{basename} is not referenced by "
                    f"scripts/hooks/install-git-hooks.sh",
                )
            )
    return violations


def _module_dotted_name(rel_path: str) -> str | None:
    """``src/pkg/sub/mod.py`` -> ``pkg.sub.mod``. ``None`` for anything not a
    Python file under ``src/`` — INCLUDING ``__init__.py``: a package marker
    is never independently "imported" by name (``from pkg.sub.mod import X``
    does not import bare ``pkg.sub`` per ``who_imports``'s matching, even
    though Python's own import machinery loads it transitively). Its
    reachability is entirely a function of its sibling modules', which W3
    already checks — grading the marker file itself would false-positive on
    every brand-new package's ``__init__.py``."""
    if not (rel_path.startswith("src/") and rel_path.endswith(".py")):
        return None
    parts = rel_path[len("src/") :].split("/")
    if parts[-1] == "__init__.py":
        return None
    parts[-1] = parts[-1][: -len(".py")]
    if not parts or any(not p for p in parts):
        return None
    return ".".join(parts)


def _orphan_modules(repo_root: Path) -> set[str]:
    """Shell out to the sanctioned AST toolkit's ``orphans`` query — the SAME
    production-reachability BFS that gates AGENTS.md Hard Rule 8 elsewhere
    (``find_orphan_modules``: roots are first-party imports of top-level
    ``scripts/*.py``, ``pyproject.toml`` entry-points, ``__main__``-guarded
    modules, and ``orchestrata_ultimate.__main__``; reachability follows the
    first-party import graph with package-``__init__`` re-export semantics).
    W3 must not hand-roll a second reachability engine — reusing this one
    keeps "is X wired" answered identically everywhere in the project.

    One subprocess call for the WHOLE diff, not one per added module:
    ``orphans`` always recomputes the full graph regardless of scope, so
    batching amortises that cost across every module W3 needs to grade
    instead of paying it once per module. The AST tool is resolved next to
    THIS script (this checkout's own tooling), never under *repo_root* —
    *repo_root* is the tree being examined (real project root in
    production, a throwaway fixture in tests), which need not carry its own
    copy of the toolkit. ``--source`` is *repo_root*'s bare ``src/`` (not
    ``src/orchestrata_ultimate``): the real tree also carries sibling
    packages (``orchestrata_ast``, ``orchestrata_tools``) that
    :func:`_module_dotted_name` grades too, and a narrower root would drop
    them from the reachability graph entirely."""
    ast_tool = Path(__file__).resolve().parent / "serialize_codebase_ast.py"
    result = subprocess.run(
        [
            sys.executable,
            str(ast_tool),
            "orphans",
            "--source",
            str(repo_root / "src"),
            "--scripts",
            str(repo_root / "scripts"),
            "--pyproject",
            str(repo_root / "pyproject.toml"),
        ],
        capture_output=True,
        text=True,
        timeout=120,
    )
    orphans: set[str] = set()
    for line in result.stdout.splitlines():
        m = _ORPHAN_LINE_RE.match(line)
        if m:
            orphans.add(m.group(1))
    return orphans


def check_w3_src_modules(added: list[str], repo_root: Path) -> list[Violation]:
    """A newly added ``src/`` module must be reachable from at least one
    production entrypoint via the transitive first-party import graph — an
    import from a file that is itself unreachable does not count (that was
    the false-positive-prone proxy this replaced: "something outside my own
    package imports me" said nothing about whether that importer was ever
    itself wired in)."""
    modules = {rel: m for rel in added if (m := _module_dotted_name(rel))}
    if not modules:
        return []
    orphans = _orphan_modules(repo_root)
    violations = []
    for rel, module in modules.items():
        if module in orphans:
            violations.append(
                Violation(
                    "W3",
                    rel,
                    f"{module!r} is unreachable from every production "
                    f"entrypoint (scripts/*.py imports, pyproject.toml "
                    f"entry-points, __main__ guards) — no live path wires "
                    f"it in",
                )
            )
    return violations


def _gate_declarations(rulings_dir: Path) -> list[tuple[str, str, str]]:
    """``(ruling_id, script_rel_path, ruling_file)`` for every ``gate
    <script> <rule-id>`` enforcement directive across every ruling."""
    declarations = []
    for path in sorted(rulings_dir.glob("RUL-*.md")):
        m = _RUL_ID_RE.match(path.stem)
        if not m:
            continue
        text = path.read_text(encoding="utf-8", errors="replace")
        for directive in _extract_enforcement_lines(text):
            parts = directive.split(None, 2)
            if len(parts) >= 2 and parts[0] == "gate":
                declarations.append((m.group(1), parts[1], str(path)))
    return declarations


def _runner_texts(repo_root: Path) -> list[str]:
    texts = []
    for rel in _RUNNER_RELATIVE_PATHS:
        p = repo_root / rel
        if p.exists():
            texts.append(p.read_text(encoding="utf-8", errors="replace"))
    for hook_dir in _HOOK_DIRS:
        d = repo_root / hook_dir
        if d.is_dir():
            for f in sorted(d.iterdir()):
                if f.is_file():
                    texts.append(f.read_text(encoding="utf-8", errors="replace"))
    return texts


def check_w4_gate_scripts(
    added: list[str], repo_root: Path, rulings_dir: Path
) -> list[Violation]:
    """A ``scripts/*.py`` gate script that THIS diff adds, and that some
    ruling declares as its ``gate`` mechanism, must be invoked by at least
    one runner (health_check.py / .claude/hooks / scripts/hooks /
    run_tests.py) — otherwise nothing ever activates it."""
    added_set = set(added)
    declarations = [d for d in _gate_declarations(rulings_dir) if d[1] in added_set]
    if not declarations:
        return []
    texts = _runner_texts(repo_root)
    violations = []
    for ruling_id, script_rel, ruling_path in declarations:
        if not any(script_rel in text for text in texts):
            violations.append(
                Violation(
                    "W4",
                    script_rel,
                    f"{ruling_id} ({ruling_path}) declares this as its gate "
                    f"mechanism, but no runner invokes it",
                )
            )
    return violations


def run_check(repo_root: Path, rulings_dir: Path | None = None) -> tuple[int, dict]:
    """Injection seam: tests pass a throwaway tmp_path repo root (and,
    optionally, an explicit rulings directory) — never touches the live
    checkout."""
    try:
        entries = diff_entries(repo_root)
    except DiffError as exc:
        return 0, {
            "status": "skipped",
            "reason": f"could not compute diff against master at {repo_root}: {exc}",
        }
    if not entries:
        return 0, {"status": "skipped", "reason": "no diff against master"}
    added = added_paths(entries)

    rdir = rulings_dir if rulings_dir is not None else _default_rulings_dir()
    w4_skipped = not rdir.exists()

    violations = [
        *check_w1_claude_hooks(added, repo_root),
        *check_w2_scripts_hooks(added, repo_root),
        *check_w3_src_modules(added, repo_root),
    ]
    if not w4_skipped:
        violations.extend(check_w4_gate_scripts(added, repo_root, rdir))

    report = {
        "status": "pass" if not violations else "fail",
        "repo_root": str(repo_root),
        "added_files": added,
        "w4_skipped": w4_skipped,
        "violations": [asdict(v) for v in violations],
    }
    return (0 if not violations else 1), report


def _print_text_report(report: dict) -> None:
    if report["status"] == "skipped":
        print(f"[wiring] SKIP — {report['reason']}", flush=True)
        return
    if report["status"] == "pass":
        print("[wiring] OK — RUL-023 satisfied", flush=True)
        return
    print(f"[wiring] FAIL — {len(report['violations'])} violation(s):", flush=True)
    for v in report["violations"]:
        print(f"  [{v['check']}] {v['artifact']}: {v['message']}", flush=True)


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--repo-root", default=None, help="Directory to gate (default: cwd)"
    )
    parser.add_argument("--rulings-dir", default=None, help="Override RULINGS_DIR")
    parser.add_argument("--json", action="store_true", help="Machine-readable output")
    args = parser.parse_args(argv)

    repo_root = Path(args.repo_root).resolve() if args.repo_root else Path.cwd()
    rulings_dir = Path(args.rulings_dir).expanduser() if args.rulings_dir else None
    exit_code, report = run_check(repo_root, rulings_dir=rulings_dir)

    if args.json:
        print(json.dumps(report, indent=2, default=str))
    else:
        _print_text_report(report)
    return exit_code


if __name__ == "__main__":
    sys.exit(main())