code_check.py file-level

at main · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 πŸ’₯ blast risk
sha256:c docs(KD-STAGING): sync governance after KD-6b DONE Mirror workspace go… · aaronrene · Jul 10, 2026
1 """``muse code code-check`` β€” code invariant enforcement.
2
3 Evaluates semantic rules declared in ``.muse/code_invariants.toml`` against
4 the code snapshot of the specified commit and reports violations.
5
6 Built-in rule types (declared in TOML)::
7
8 [[rule]]
9 name = "complexity_gate"
10 severity = "error"
11 rule_type = "max_complexity"
12 [rule.params]
13 threshold = 10
14
15 [[rule]]
16 name = "no_cycles"
17 severity = "error"
18 rule_type = "no_circular_imports"
19
20 [[rule]]
21 name = "dead_exports"
22 severity = "warning"
23 rule_type = "no_dead_exports"
24
25 [[rule]]
26 name = "coverage_floor"
27 severity = "warning"
28 rule_type = "test_coverage_floor"
29 [rule.params]
30 min_ratio = 0.30
31
32 Usage::
33
34 muse code code-check # check HEAD
35 muse code code-check abc1234 # check specific commit
36 muse code code-check --strict # exit 1 on any error-severity violation
37 muse code code-check --json # machine-readable JSON output
38 muse code code-check --rules my_rules.toml # custom rules file
39 muse code code-check --filter error # show only error-severity violations
40 muse code code-check --diff HEAD~1 # show only NEW violations vs reference
41 muse code code-check --diff main --strict # CI ratchet: fail only on regressions
42 """
43
44 from __future__ import annotations
45
46 import argparse
47 import json
48 import logging
49 import pathlib
50 import sys
51
52 from muse.core.invariants import diff_reports, format_report, make_report
53 from muse.core.repo import require_repo
54 from muse.core.store import get_commit_snapshot_manifest, get_head_commit_id, read_current_branch
55 from muse.core.validation import contain_path
56 from muse.plugins.code._invariants import load_invariant_rules, run_invariants
57
58 logger = logging.getLogger(__name__)
59
60 _SEVERITIES = ("error", "warning", "info")
61
62
63 def _resolve_commit(root: pathlib.Path, ref: str | None) -> str | None:
64 """Resolve *ref* to a full commit ID.
65
66 Resolution order:
67 1. ``None`` β†’ HEAD of the current branch.
68 2. Branch name that exists in the store β†’ HEAD commit of that branch.
69 3. Anything else β†’ returned as-is (assumed to be a full or partial
70 commit ID; the caller validates via the snapshot manifest).
71 """
72 if ref is None:
73 branch = read_current_branch(root)
74 return get_head_commit_id(root, branch)
75 # Try treating ref as a branch name first.
76 branch_commit = get_head_commit_id(root, ref)
77 if branch_commit is not None:
78 return branch_commit
79 # Fall back: treat as a literal commit ID.
80 return ref
81
82
83 def register(subparsers: "argparse._SubParsersAction[argparse.ArgumentParser]") -> None:
84 """Register the code-check subcommand."""
85 parser = subparsers.add_parser(
86 "code-check",
87 help="Enforce code invariant rules against a commit snapshot.",
88 description=__doc__,
89 formatter_class=argparse.RawDescriptionHelpFormatter,
90 )
91 parser.add_argument(
92 "commit_arg", nargs="?", default=None,
93 metavar="commit",
94 help="Commit ID to check (default: HEAD).",
95 )
96 parser.add_argument(
97 "--strict", action="store_true",
98 help="Exit 1 when any error-severity violation is found.",
99 )
100 parser.add_argument(
101 "--json", action="store_true", dest="as_json",
102 help="Emit machine-readable JSON.",
103 )
104 parser.add_argument(
105 "--rules", default=None, dest="rules_file",
106 metavar="RULES_FILE",
107 help=(
108 "Path to a TOML invariants file inside the repo "
109 "(default: .muse/code_invariants.toml)."
110 ),
111 )
112 parser.add_argument(
113 "--filter",
114 dest="severity_filter",
115 choices=_SEVERITIES,
116 default=None,
117 metavar="SEVERITY",
118 help="Show only violations of this severity (error | warning | info).",
119 )
120 parser.add_argument(
121 "--diff",
122 dest="diff_ref",
123 default=None,
124 metavar="REF",
125 help=(
126 "Show only violations that are NEW compared to REF. "
127 "Ideal for CI: muse code code-check --diff HEAD~1 --strict "
128 "fails only when you introduce a new violation."
129 ),
130 )
131 parser.set_defaults(func=run)
132
133
134 def run(args: argparse.Namespace) -> None:
135 """Enforce code invariant rules against a commit snapshot.
136
137 Reports cyclomatic complexity violations, import cycles, dead exports,
138 and test coverage shortfalls based on the rules in
139 ``.muse/code_invariants.toml`` (or built-in defaults when the file is
140 absent).
141
142 The ``--rules`` path is validated against the repo root β€” paths that
143 escape the repository (e.g. ``../../shared/rules.toml``) are rejected.
144 """
145 commit_arg: str | None = args.commit_arg
146 strict: bool = args.strict
147 as_json: bool = args.as_json
148 rules_file: str | None = args.rules_file
149 severity_filter: str | None = args.severity_filter
150 diff_ref: str | None = args.diff_ref
151
152 root = require_repo()
153
154 commit_id = _resolve_commit(root, commit_arg)
155 if commit_id is None:
156 print("❌ No commit found.", file=sys.stderr)
157 raise SystemExit(1)
158
159 rules_path: pathlib.Path | None = None
160 if rules_file:
161 try:
162 rules_path = contain_path(root, rules_file)
163 except ValueError as exc:
164 print(f"❌ {exc}", file=sys.stderr)
165 raise SystemExit(1)
166 if not rules_path.exists():
167 print(f"❌ Rules file not found: {rules_path}", file=sys.stderr)
168 raise SystemExit(1)
169
170 rules = load_invariant_rules(rules_path)
171 report = run_invariants(root, commit_id, rules)
172
173 # --diff: subtract violations already present in the reference commit so
174 # only regressions are reported.
175 if diff_ref is not None:
176 ref_id = _resolve_commit(root, diff_ref)
177 if ref_id is None:
178 print(f"❌ Could not resolve --diff ref: {diff_ref!r}", file=sys.stderr)
179 raise SystemExit(1)
180 if get_commit_snapshot_manifest(root, ref_id) is None:
181 print(
182 f"❌ Commit {diff_ref!r} not found β€” cannot use as --diff base.",
183 file=sys.stderr,
184 )
185 raise SystemExit(1)
186 ref_report = run_invariants(root, ref_id, rules)
187 report = diff_reports(report, ref_report)
188
189 # --filter: drop violations that don't match the requested severity.
190 if severity_filter is not None:
191 filtered = [v for v in report["violations"] if v["severity"] == severity_filter]
192 report = make_report(
193 report["commit_id"],
194 report["domain"],
195 filtered,
196 report["rules_checked"],
197 )
198
199 if as_json:
200 payload = dict(report)
201 if diff_ref is not None:
202 payload["diff_ref"] = diff_ref
203 if severity_filter is not None:
204 payload["severity_filter"] = severity_filter
205 print(json.dumps(payload))
206 if strict and report["has_errors"]:
207 raise SystemExit(1)
208 return
209
210 diff_label = f" (new since {diff_ref})" if diff_ref else ""
211 filter_label = f" [{severity_filter} only]" if severity_filter else ""
212 print(
213 f"\ncode-check {commit_id[:8]}{diff_label}{filter_label}"
214 f" β€” {report['rules_checked']} rules"
215 )
216 print(format_report(report))
217
218 if strict and report["has_errors"]:
219 raise SystemExit(1)