-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcheck_orphan_modules.py
More file actions
278 lines (232 loc) · 10.4 KB
/
check_orphan_modules.py
File metadata and controls
278 lines (232 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
"""Detect orphan modules — code that exists, has tests, is re-exported,
but has no production callers.
Audit finding 2026-05-03 round 2: 10+ modules in ``src/divineos/`` had
no production callers but were tested and importable. Each successive
maintenance commit (broad-except tightening, type-check pass, etc.)
paid the tax for code that did nothing. The audit suggested:
For each src/divineos/**/*.py (excluding __init__.py):
1. Find imports from {module} or from {parent}.{name} in src/
2. Find imports in .claude/hooks/*.sh
3. If both empty AND tests/ has importers → flag as orphan
This script implements that detector, plus respects the project's
``# AGENT_RUNTIME`` marker convention. A module marked AGENT_RUNTIME
is INTENTIONALLY unwired into the CLI/import graph but invoked from
a separate runtime context (e.g., Claude Code hooks). Marked modules
are excluded from the orphan list.
Output format mirrors ``check_doc_counts.py``: prints findings to
stdout, exits 0 on clean tree, non-zero if orphans found (so it can
be wired into pre-commit / CI when ready).
Note: this script is NOT yet wired into the gate. It's a tool for
running periodically to catch accumulation. Wiring it as a hard
gate would block any PR that introduces a new module before its
caller lands, which is too strict.
Known limitations:
* Modules reached only through ``from <package> import <symbol>``
re-export shapes are flagged as orphans because the dotted path
doesn't appear in any source file. The 39 council expert modules
are an example: each is imported by ``council/experts/__init__.py``
as ``from divineos.core.council.experts.feynman import
create_feynman_wisdom``, then re-exported, then used elsewhere as
``from divineos.core.council.experts import create_feynman_wisdom``.
The static check sees the second pattern but doesn't follow the
re-export back to ``feynman.py``.
* CLI commands registered dynamically via ``register(cli)`` (the
pattern in ``cli/__init__.py``) won't show up as imports from
the command module's full dotted path.
Treat the output as a triage starting point, not ground truth.
For each finding, manually verify whether it's a real orphan
(audit round 2's list of 10 modules is the canonical confirmed
set) or one of the above false-positive shapes.
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
SRC = ROOT / "src" / "divineos"
TESTS = ROOT / "tests"
HOOKS = ROOT / ".claude" / "hooks"
def _collect_module_paths() -> list[Path]:
"""Return every non-init Python module under src/divineos/ as a path."""
out: list[Path] = []
for p in SRC.rglob("*.py"):
if p.name == "__init__.py":
continue
if "__pycache__" in p.parts:
continue
out.append(p)
return out
def _module_dotted_name(path: Path) -> str:
"""Convert ``src/divineos/core/foo/bar.py`` to ``divineos.core.foo.bar``."""
rel = path.relative_to(SRC.parent)
parts = list(rel.with_suffix("").parts)
return ".".join(parts)
def _is_intentionally_unwired(path: Path) -> bool:
"""Modules with an explicit unwired-marker are intentionally not
in the import graph. Two markers honored:
* ``AGENT_RUNTIME`` — invoked from a separate runtime context
(Claude Code hooks, external workflow runners, etc.)
* ``PHASE_1_STAGED`` — staged for a later wiring phase. Same
semantics as AGENT_RUNTIME for orphan-detection: known-by-design
unwired, not accidentally orphaned.
Markers must appear in the first ~2000 chars (typical docstring
location). Renamed from ``_is_agent_runtime`` 2026-05-07 per
round-2 audit because the function now honors more than the
AGENT_RUNTIME marker.
"""
try:
text = path.read_text(encoding="utf-8", errors="replace")
except OSError:
return False
return bool(re.search(r"AGENT_RUNTIME|PHASE_1_STAGED", text[:2000]))
# Backward-compat alias — keep the old name pointing at the new function
# so any external tooling that imports _is_agent_runtime keeps working.
_is_agent_runtime = _is_intentionally_unwired
def _has_caller_in(needle_module: str, search_root: Path, exclude: Path | None = None) -> bool:
"""Return True if any file under ``search_root`` imports ``needle_module``.
Matches both ``from <needle_module> import ...`` and
``import <needle_module>`` patterns. Excludes the module's own
``__init__.py`` (since a package re-export isn't a caller) and
the file at ``exclude`` (the module itself).
"""
# Pattern: bare module-name reference or sub-module reference
pat = re.compile(
rf"\b(?:from\s+{re.escape(needle_module)}\b|import\s+{re.escape(needle_module)}\b)"
)
for p in search_root.rglob("*.py"):
if exclude and p.resolve() == exclude.resolve():
continue
if "__pycache__" in p.parts:
continue
# Skip the module's package __init__.py — re-exports aren't real callers
if p.name == "__init__.py":
parent = p.parent
# If this __init__.py is the parent of the module we're checking,
# treat its imports as re-exports, not callers.
try:
parent_dotted = _module_dotted_name(parent / "_dummy.py").rsplit(".", 1)[0]
if needle_module.startswith(parent_dotted + "."):
continue
except ValueError:
# Path isn't under SRC (e.g., a test conftest); fall through.
pass
try:
text = p.read_text(encoding="utf-8", errors="replace")
except OSError:
continue
if pat.search(text):
return True
return False
def _is_reexported_through_parent_init(module_path: Path) -> bool:
"""Return True if the module is wired via parent ``__init__.py`` re-export.
Round-2 audit (2026-05-07) flagged the council expert modules and
register(cli)-pattern CLI modules as orphans because their only
"caller" was the parent package's ``__init__.py``. The naive
"skip __init__.py" rule lost that signal.
A module is reached via re-export when:
1. The parent ``__init__.py`` references the module (full dotted
path or short name), AND
2. Either the parent package has callers somewhere in src/, OR
the __init__.py calls ``<module_short>.register(cli)``.
"""
init_path = module_path.parent / "__init__.py"
if not init_path.exists():
return False
try:
init_text = init_path.read_text(encoding="utf-8", errors="replace")
except OSError:
return False
module_dotted = _module_dotted_name(module_path)
module_short = module_path.stem
pat_from_module = re.compile(rf"from\s+{re.escape(module_dotted)}\s+import")
# Word-boundary on short name so ``feynman`` matches in multi-line
# tuple imports but not inside ``pre_feynman_v2``.
pat_short = re.compile(rf"(?<![A-Za-z0-9_]){re.escape(module_short)}(?![A-Za-z0-9_])")
pat_register = re.compile(rf"{re.escape(module_short)}\.register\(cli\)")
init_imports_module = (
pat_from_module.search(init_text) is not None or pat_short.search(init_text) is not None
)
if not init_imports_module:
return False
if pat_register.search(init_text):
return True
parent_dotted = _module_dotted_name(init_path).rsplit(".", 1)[0]
pat_parent_use = re.compile(
rf"(?:from\s+{re.escape(parent_dotted)}\s+import"
rf"|import\s+{re.escape(parent_dotted)})"
)
for p in SRC.rglob("*.py"):
if p.resolve() == init_path.resolve():
continue
if p.resolve() == module_path.resolve():
continue
if "__pycache__" in p.parts:
continue
try:
t = p.read_text(encoding="utf-8", errors="replace")
except OSError:
continue
if pat_parent_use.search(t):
return True
return False
def _has_caller_in_shell(needle: str) -> bool:
"""Return True if any .sh hook references ``divineos.<needle>``
(e.g., via ``python -m divineos.<needle>``)."""
if not HOOKS.exists():
return False
pat = re.compile(rf"\bdivineos\.{re.escape(needle)}\b")
for p in HOOKS.rglob("*.sh"):
try:
text = p.read_text(encoding="utf-8", errors="replace")
except OSError:
continue
if pat.search(text):
return True
return False
def find_orphans() -> list[tuple[Path, str]]:
"""Return a list of (path, reason) for every orphan module.
A module is an orphan if:
1. It has NO production caller (nothing in src/divineos/ imports
it, except its own __init__.py re-export)
2. It has NO hook caller (nothing in .claude/hooks/ runs it)
3. It DOES have a test importer (otherwise it's just unused code,
not an orphan — covered by vulture/dead-code scan)
4. It is NOT marked ``AGENT_RUNTIME``
"""
orphans: list[tuple[Path, str]] = []
for path in _collect_module_paths():
if _is_intentionally_unwired(path):
continue
dotted = _module_dotted_name(path)
# Check production callers in src/
if _has_caller_in(dotted, SRC, exclude=path):
continue
# Check hook callers
if _has_caller_in_shell(dotted.removeprefix("divineos.")):
continue
# Check re-export through parent __init__.py (round-2 audit fix).
if _is_reexported_through_parent_init(path):
continue
# Confirm there IS a test importer (otherwise it's not an
# "orphan-with-tests" but plain dead code).
if not _has_caller_in(dotted, TESTS):
continue
orphans.append((path, "no production callers, has tests"))
return orphans
def main() -> int:
orphans = find_orphans()
if not orphans:
print("Orphan check OK (no modules found that have tests but no production callers)")
return 0
print(f"Found {len(orphans)} orphan module(s):")
for path, reason in orphans:
rel = path.relative_to(ROOT)
print(f" {rel}: {reason}")
print()
print("For each: decide one of —")
print(" (a) Wire it into a production code path")
print(" (b) Add `# AGENT_RUNTIME` marker if invoked from outside the CLI graph")
print(" (c) Delete the module + its tests (audit Tier 2 dead-chain pattern)")
return 1
if __name__ == "__main__":
sys.exit(main())