vigil-codeintel 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vigil_codeintel-0.1.0.dist-info/METADATA +780 -0
- vigil_codeintel-0.1.0.dist-info/RECORD +131 -0
- vigil_codeintel-0.1.0.dist-info/WHEEL +5 -0
- vigil_codeintel-0.1.0.dist-info/entry_points.txt +3 -0
- vigil_codeintel-0.1.0.dist-info/licenses/LICENSE +21 -0
- vigil_codeintel-0.1.0.dist-info/top_level.txt +3 -0
- vigil_forensic/__init__.py +224 -0
- vigil_forensic/_git_utils.py +178 -0
- vigil_forensic/_shared.py +510 -0
- vigil_forensic/_stubs.py +156 -0
- vigil_forensic/gate_checks/__init__.py +1 -0
- vigil_forensic/gate_checks/_ast_helpers.py +629 -0
- vigil_forensic/gate_checks/_deployment_detector.py +573 -0
- vigil_forensic/gate_checks/atomic_write_checks.py +1143 -0
- vigil_forensic/gate_checks/authority_checks.py +95 -0
- vigil_forensic/gate_checks/boundary_breach_checks.py +202 -0
- vigil_forensic/gate_checks/broad_except_checks.py +301 -0
- vigil_forensic/gate_checks/broad_except_hidden_sentinel_checks.py +365 -0
- vigil_forensic/gate_checks/common.py +253 -0
- vigil_forensic/gate_checks/config_safety_checks.py +704 -0
- vigil_forensic/gate_checks/config_ssot_checks.py +78 -0
- vigil_forensic/gate_checks/conflict_checks.py +193 -0
- vigil_forensic/gate_checks/context_fallback_checks.py +697 -0
- vigil_forensic/gate_checks/context_health_checks.py +289 -0
- vigil_forensic/gate_checks/contract_shape_drift_checks.py +459 -0
- vigil_forensic/gate_checks/dirty_baseline_check.py +274 -0
- vigil_forensic/gate_checks/duplication_checks.py +387 -0
- vigil_forensic/gate_checks/embedded_string_checks.py +123 -0
- vigil_forensic/gate_checks/empty_output_checks.py +87 -0
- vigil_forensic/gate_checks/encoding_checks.py +847 -0
- vigil_forensic/gate_checks/export_completeness_checks.py +156 -0
- vigil_forensic/gate_checks/fallback_checks.py +41 -0
- vigil_forensic/gate_checks/file_proliferation_checks.py +171 -0
- vigil_forensic/gate_checks/fix_without_test_checks.py +69 -0
- vigil_forensic/gate_checks/forensic_cluster_runners/__init__.py +9 -0
- vigil_forensic/gate_checks/forensic_cluster_runners/_helpers.py +71 -0
- vigil_forensic/gate_checks/forensic_cluster_runners/advanced_checks.py +322 -0
- vigil_forensic/gate_checks/forensic_cluster_runners/core.py +273 -0
- vigil_forensic/gate_checks/forensic_cluster_runners/integrity_checks.py +203 -0
- vigil_forensic/gate_checks/forensic_cluster_runners/quality_checks.py +666 -0
- vigil_forensic/gate_checks/forensic_clusters/__init__.py +193 -0
- vigil_forensic/gate_checks/forensic_clusters/allowlist.py +426 -0
- vigil_forensic/gate_checks/forensic_clusters/allowlist_writer.py +302 -0
- vigil_forensic/gate_checks/forensic_clusters/api_protocol.py +231 -0
- vigil_forensic/gate_checks/forensic_clusters/async_quality.py +1156 -0
- vigil_forensic/gate_checks/forensic_clusters/code_style.py +808 -0
- vigil_forensic/gate_checks/forensic_clusters/core.py +319 -0
- vigil_forensic/gate_checks/forensic_clusters/data_quality.py +763 -0
- vigil_forensic/gate_checks/forensic_clusters/dead_code.py +480 -0
- vigil_forensic/gate_checks/forensic_clusters/edit_mutation.py +842 -0
- vigil_forensic/gate_checks/forensic_clusters/exception_boundary.py +240 -0
- vigil_forensic/gate_checks/forensic_clusters/legacy_debt.py +556 -0
- vigil_forensic/gate_checks/forensic_clusters/static_analysis.py +834 -0
- vigil_forensic/gate_checks/forensic_clusters/structural_quality.py +298 -0
- vigil_forensic/gate_checks/god_object_zones_checks.py +173 -0
- vigil_forensic/gate_checks/hallucination_checks.py +566 -0
- vigil_forensic/gate_checks/hunter_artifact_completeness_check.py +139 -0
- vigil_forensic/gate_checks/implementation_overfit_checks.py +380 -0
- vigil_forensic/gate_checks/import_integrity_checks.py +233 -0
- vigil_forensic/gate_checks/imports_in_function_checks.py +283 -0
- vigil_forensic/gate_checks/ml_checks.py +318 -0
- vigil_forensic/gate_checks/performance_checks.py +106 -0
- vigil_forensic/gate_checks/project_specific_runner.py +691 -0
- vigil_forensic/gate_checks/provider_capability_checks.py +73 -0
- vigil_forensic/gate_checks/refactor_completeness_checks.py +274 -0
- vigil_forensic/gate_checks/reliability_checks.py +389 -0
- vigil_forensic/gate_checks/reporting_checks.py +55 -0
- vigil_forensic/gate_checks/runtime_behavior_checks.py +220 -0
- vigil_forensic/gate_checks/security_injection_checks.py +332 -0
- vigil_forensic/gate_checks/semantic_intent_checks.py +139 -0
- vigil_forensic/gate_checks/size_complexity_checks.py +336 -0
- vigil_forensic/gate_checks/stuck_feature_flag_checks.py +354 -0
- vigil_forensic/gate_checks/syntax_validity_checks.py +217 -0
- vigil_forensic/gate_checks/temporal_freshness_checks.py +79 -0
- vigil_forensic/gate_checks/test_quality_checks.py +946 -0
- vigil_forensic/gate_checks/testing_checks.py +149 -0
- vigil_forensic/gate_checks/toctou_checks.py +367 -0
- vigil_forensic/gate_checks/type_checking_checks.py +316 -0
- vigil_forensic/gate_models.py +392 -0
- vigil_forensic/gate_packs/__init__.py +1 -0
- vigil_forensic/gate_packs/universal.py +179 -0
- vigil_forensic/gate_profile.json +31 -0
- vigil_forensic/gate_registry.py +21 -0
- vigil_forensic/language_profiles.py +219 -0
- vigil_forensic/meta_findings.py +207 -0
- vigil_forensic/self_audit.py +725 -0
- vigil_forensic/source_analysis.py +175 -0
- vigil_mapper/__init__.py +103 -0
- vigil_mapper/_ast_helpers_minimal.py +229 -0
- vigil_mapper/_extract_imports_impl.py +123 -0
- vigil_mapper/_file_count_guard.py +129 -0
- vigil_mapper/_git_utils.py +178 -0
- vigil_mapper/_runtime_ast.py +438 -0
- vigil_mapper/_runtime_dispatch.py +137 -0
- vigil_mapper/_seed_helpers.py +82 -0
- vigil_mapper/authority_builder.py +1102 -0
- vigil_mapper/cli_entry.py +731 -0
- vigil_mapper/conflict_builder.py +818 -0
- vigil_mapper/data_contract_builder.py +446 -0
- vigil_mapper/findings_builder.py +716 -0
- vigil_mapper/fingerprint.py +53 -0
- vigil_mapper/hotspot_builder.py +539 -0
- vigil_mapper/map_common.py +449 -0
- vigil_mapper/map_errors.py +55 -0
- vigil_mapper/map_models.py +431 -0
- vigil_mapper/map_models_ext.py +206 -0
- vigil_mapper/map_models_findings.py +130 -0
- vigil_mapper/map_storage.py +455 -0
- vigil_mapper/parse_cache.py +795 -0
- vigil_mapper/refactor_boundary_builder.py +266 -0
- vigil_mapper/runtime_builder.py +527 -0
- vigil_mapper/runtime_tracer.py +243 -0
- vigil_mapper/runtime_tracer_entry.py +199 -0
- vigil_mapper/semantic_diff.py +71 -0
- vigil_mapper/source_adapters/__init__.py +109 -0
- vigil_mapper/source_adapters/_base.py +264 -0
- vigil_mapper/source_adapters/_ir.py +156 -0
- vigil_mapper/source_adapters/_lexer.py +309 -0
- vigil_mapper/source_adapters/_patterns.py +212 -0
- vigil_mapper/source_adapters/_treesitter.py +182 -0
- vigil_mapper/source_adapters/go.py +553 -0
- vigil_mapper/source_adapters/java.py +541 -0
- vigil_mapper/source_adapters/javascript.py +626 -0
- vigil_mapper/source_adapters/python.py +325 -0
- vigil_mapper/source_adapters/typescript.py +749 -0
- vigil_mapper/structural_builder.py +586 -0
- vigil_mcp/__init__.py +1 -0
- vigil_mcp/_jobs.py +587 -0
- vigil_mcp/_paths.py +93 -0
- vigil_mcp/forensic_server.py +419 -0
- vigil_mcp/map_server.py +452 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Generic refactor boundary loader -- reads from <project_dir>/.cortex/maps/70_refactor_boundaries.json.
|
|
2
|
+
|
|
3
|
+
ARCHITECTURE NOTE: Refactor boundaries are generated/loaded in two phases:
|
|
4
|
+
|
|
5
|
+
1. AUTO-INFERRED BOUNDARIES (computed during map build):
|
|
6
|
+
- source: `.cortex/maps/70_refactor_boundaries.json`
|
|
7
|
+
- phase: "planning"
|
|
8
|
+
- recomputed on every build (Phase 4 will add auto-inference logic)
|
|
9
|
+
- include: SCC clusters, hotspot-scored files, canonical owners
|
|
10
|
+
|
|
11
|
+
2. MANUAL SEED BOUNDARIES (user-authored, override auto-inferred):
|
|
12
|
+
- source: `.cortex/map_seeds/refactor_boundaries.json`
|
|
13
|
+
- phase: "seed" or higher (user-defined)
|
|
14
|
+
- override auto-inferred entries for same allowed_files/forbidden_files
|
|
15
|
+
- auto-inferred entries for files without seed are still included
|
|
16
|
+
|
|
17
|
+
LOADER BEHAVIOR:
|
|
18
|
+
- This module loads GENERATED boundaries from maps/70_refactor_boundaries.json
|
|
19
|
+
- Seed loading handled separately by main builder orchestrator
|
|
20
|
+
- merge strategy: seed entries + auto-inferred entries (seed wins on conflicts)
|
|
21
|
+
- If file missing: return empty list (not an error)
|
|
22
|
+
|
|
23
|
+
Design: generic, works on any project via project_dir parameter.
|
|
24
|
+
"""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import json
|
|
28
|
+
import logging
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
|
|
31
|
+
from .map_errors import MapIntegrityError
|
|
32
|
+
from .map_models_ext import RefactorBoundary
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"load_refactor_seeds",
|
|
36
|
+
"infer_refactor_boundaries",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
_log = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def infer_refactor_boundaries(repo_maps) -> list[RefactorBoundary]:
|
|
43
|
+
"""Auto-infer refactor boundaries from conflict/hotspot/authority maps.
|
|
44
|
+
|
|
45
|
+
Algorithm:
|
|
46
|
+
1. Group conflicts by SCC cluster → boundary candidates
|
|
47
|
+
2. Hotspot scores: safe_refactor (score < 30), caution (30-60), do_not_touch (>= 60)
|
|
48
|
+
3. Authority: canonical_owner matching domain
|
|
49
|
+
4. Union-find: merge clusters with overlapping files
|
|
50
|
+
5. Emit RefactorBoundary per cluster
|
|
51
|
+
"""
|
|
52
|
+
import hashlib
|
|
53
|
+
|
|
54
|
+
boundaries: list[RefactorBoundary] = []
|
|
55
|
+
|
|
56
|
+
# Build hotspot index: file -> score + recommended_mode
|
|
57
|
+
hotspot_by_file: dict[str, dict] = {}
|
|
58
|
+
for h in repo_maps.hotspot:
|
|
59
|
+
target = getattr(h, "target", "")
|
|
60
|
+
score = getattr(h, "hotspot_score", 0)
|
|
61
|
+
mode = "do_not_touch" if score >= 60 else ("caution" if score >= 30 else "safe_refactor")
|
|
62
|
+
hotspot_by_file[target] = {
|
|
63
|
+
"score": score,
|
|
64
|
+
"mode": mode,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Build authority index: domain -> canonical_owner
|
|
68
|
+
authority_by_domain: dict[str, str] = {}
|
|
69
|
+
for auth in repo_maps.authority:
|
|
70
|
+
domain = getattr(auth, "authority_domain", "")
|
|
71
|
+
owner = getattr(auth, "canonical_owner", "")
|
|
72
|
+
if domain:
|
|
73
|
+
authority_by_domain[domain] = owner
|
|
74
|
+
|
|
75
|
+
# Cluster candidates from conflict map (SCC clusters)
|
|
76
|
+
clusters: list[dict] = []
|
|
77
|
+
seen_files: set[str] = set()
|
|
78
|
+
|
|
79
|
+
for conflict in repo_maps.conflict:
|
|
80
|
+
domain = getattr(conflict, "domain", "")
|
|
81
|
+
if domain != "structural_cycles":
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
# Parse sources to collect SCC member files
|
|
85
|
+
sources_parsed: list[dict] = []
|
|
86
|
+
sources_raw = getattr(conflict, "sources", [])
|
|
87
|
+
for src_raw in sources_raw:
|
|
88
|
+
try:
|
|
89
|
+
src = json.loads(src_raw) if isinstance(src_raw, str) else src_raw
|
|
90
|
+
if isinstance(src, dict):
|
|
91
|
+
sources_parsed.append(src)
|
|
92
|
+
except (json.JSONDecodeError, TypeError):
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
scc_files = [s.get("file", "") for s in sources_parsed if s.get("file")]
|
|
96
|
+
|
|
97
|
+
# Skip if already clustered
|
|
98
|
+
if any(f in seen_files for f in scc_files):
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
# Add new cluster
|
|
102
|
+
if scc_files:
|
|
103
|
+
clusters.append({
|
|
104
|
+
"files": scc_files,
|
|
105
|
+
"conflict": conflict,
|
|
106
|
+
"sources": sources_parsed,
|
|
107
|
+
})
|
|
108
|
+
seen_files.update(scc_files)
|
|
109
|
+
|
|
110
|
+
# Classify files by hotspot mode
|
|
111
|
+
def classify_file(f: str) -> str:
|
|
112
|
+
hs = hotspot_by_file.get(f, {})
|
|
113
|
+
return hs.get("mode", "unknown")
|
|
114
|
+
|
|
115
|
+
# Build boundaries
|
|
116
|
+
for cluster in clusters:
|
|
117
|
+
scc_files = cluster["files"]
|
|
118
|
+
conflict = cluster["conflict"]
|
|
119
|
+
sources = cluster["sources"]
|
|
120
|
+
|
|
121
|
+
# Partition by hotspot mode
|
|
122
|
+
safe_files = [f for f in scc_files if classify_file(f) == "safe_refactor"]
|
|
123
|
+
caution_files = [f for f in scc_files if classify_file(f) == "caution"]
|
|
124
|
+
forbidden_files = [f for f in scc_files if classify_file(f) == "do_not_touch"]
|
|
125
|
+
|
|
126
|
+
# Extract canonical owners (entrypoints) from authority
|
|
127
|
+
entrypoints: set[str] = set()
|
|
128
|
+
conflict_domain = getattr(conflict, "domain", "")
|
|
129
|
+
# Remove "structural_cycles" suffix if present to get domain name
|
|
130
|
+
domain_name = conflict_domain.replace("_cycles", "") if conflict_domain.endswith("_cycles") else conflict_domain
|
|
131
|
+
|
|
132
|
+
if domain_name in authority_by_domain:
|
|
133
|
+
owner = authority_by_domain[domain_name]
|
|
134
|
+
if owner:
|
|
135
|
+
entrypoints.add(owner)
|
|
136
|
+
|
|
137
|
+
# Generate stable ID
|
|
138
|
+
cluster_id = hashlib.sha256(
|
|
139
|
+
",".join(sorted(scc_files)).encode("utf-8")
|
|
140
|
+
).hexdigest()[:8]
|
|
141
|
+
|
|
142
|
+
max_fan_in = max((s.get("cluster_max_fan_in", 0) for s in sources), default=0)
|
|
143
|
+
boundary = RefactorBoundary(
|
|
144
|
+
boundary_id=f"auto_{cluster_id}",
|
|
145
|
+
goal=f"Decouple import cycle in cluster ({len(scc_files)} files, max fan_in={max_fan_in})",
|
|
146
|
+
phase="planning",
|
|
147
|
+
allowed_files=tuple(safe_files + caution_files), # Safe + caution can be refactored
|
|
148
|
+
watch_files=(),
|
|
149
|
+
forbidden_files=tuple(forbidden_files), # Do-not-touch files
|
|
150
|
+
entrypoints=tuple(entrypoints),
|
|
151
|
+
must_hold_invariants=(),
|
|
152
|
+
source="auto_inferred",
|
|
153
|
+
evidence=(),
|
|
154
|
+
confidence=0.75,
|
|
155
|
+
freshness="inferred",
|
|
156
|
+
status="inferred",
|
|
157
|
+
)
|
|
158
|
+
boundaries.append(boundary)
|
|
159
|
+
|
|
160
|
+
_log.info(
|
|
161
|
+
"infer_refactor_boundaries: inferred %d boundaries from %d SCC clusters",
|
|
162
|
+
len(boundaries), len(clusters),
|
|
163
|
+
)
|
|
164
|
+
return boundaries
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def load_refactor_seeds(project_dir: Path) -> list[RefactorBoundary]:
|
|
168
|
+
"""Load USER-AUTHORED manual seed boundaries from <project_dir>/.cortex/map_seeds/refactor_boundaries.json.
|
|
169
|
+
|
|
170
|
+
Manual seeds are persistent, user-authored refactor boundaries that override
|
|
171
|
+
auto-inferred boundaries for the same file sets. This function reads from the
|
|
172
|
+
seed directory (persistent, under version control). Auto-inferred boundaries
|
|
173
|
+
are computed separately and merged afterward.
|
|
174
|
+
|
|
175
|
+
IMPORTANT: This loads SEEDS only, not generated output. For generated output,
|
|
176
|
+
use infer_refactor_boundaries() which computes boundaries at build time.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
project_dir: Absolute path to the target project root.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
list[RefactorBoundary]: Boundaries from the seed file, or [] if missing.
|
|
183
|
+
|
|
184
|
+
Raises:
|
|
185
|
+
MapIntegrityError: If JSON is corrupt, schema is invalid, or major version mismatch.
|
|
186
|
+
"""
|
|
187
|
+
project_dir = Path(project_dir).resolve()
|
|
188
|
+
seeds_dir = project_dir / ".cortex" / "map_seeds"
|
|
189
|
+
boundaries_path = seeds_dir / "refactor_boundaries.json"
|
|
190
|
+
|
|
191
|
+
# Missing file — log and return empty
|
|
192
|
+
if not boundaries_path.exists():
|
|
193
|
+
_log.info(
|
|
194
|
+
"load_refactor_boundaries: no boundaries file at %s -- returning empty",
|
|
195
|
+
boundaries_path,
|
|
196
|
+
)
|
|
197
|
+
return []
|
|
198
|
+
|
|
199
|
+
# Read JSON
|
|
200
|
+
try:
|
|
201
|
+
import json
|
|
202
|
+
content = boundaries_path.read_text(encoding="utf-8")
|
|
203
|
+
payload = json.loads(content)
|
|
204
|
+
except (OSError, json.JSONDecodeError, UnicodeDecodeError) as exc:
|
|
205
|
+
raise MapIntegrityError(
|
|
206
|
+
f"Failed to read/parse {boundaries_path}: {exc}"
|
|
207
|
+
) from exc
|
|
208
|
+
|
|
209
|
+
# Validate structure
|
|
210
|
+
if not isinstance(payload, dict):
|
|
211
|
+
raise MapIntegrityError(
|
|
212
|
+
f"Expected JSON object at root, got {type(payload).__name__}"
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Check schema_version
|
|
216
|
+
schema_version_raw = payload.get("schema_version")
|
|
217
|
+
if schema_version_raw is None:
|
|
218
|
+
raise MapIntegrityError("Missing required field: schema_version")
|
|
219
|
+
|
|
220
|
+
schema_version_str = str(schema_version_raw)
|
|
221
|
+
major_version_str = schema_version_str.split(".")[0] if "." in schema_version_str else schema_version_str
|
|
222
|
+
try:
|
|
223
|
+
major_version = int(major_version_str)
|
|
224
|
+
except ValueError:
|
|
225
|
+
raise MapIntegrityError(
|
|
226
|
+
f"Invalid schema_version format: {schema_version_str}"
|
|
227
|
+
) from None
|
|
228
|
+
|
|
229
|
+
if major_version > 1:
|
|
230
|
+
raise MapIntegrityError(
|
|
231
|
+
f"Major version {major_version} > 1 -- incompatible schema"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Parse entries
|
|
235
|
+
entries_raw = payload.get("entries", [])
|
|
236
|
+
if not isinstance(entries_raw, list):
|
|
237
|
+
raise MapIntegrityError(
|
|
238
|
+
f"Expected 'entries' to be a list, got {type(entries_raw).__name__}"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
boundaries: list[RefactorBoundary] = []
|
|
242
|
+
for i, raw_entry in enumerate(entries_raw):
|
|
243
|
+
if not isinstance(raw_entry, dict):
|
|
244
|
+
_log.warning(
|
|
245
|
+
"load_refactor_boundaries: skipping entry %d (not a dict, got %s)",
|
|
246
|
+
i, type(raw_entry).__name__,
|
|
247
|
+
)
|
|
248
|
+
continue
|
|
249
|
+
try:
|
|
250
|
+
boundary = RefactorBoundary.from_dict(raw_entry)
|
|
251
|
+
boundaries.append(boundary)
|
|
252
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
253
|
+
_log.warning(
|
|
254
|
+
"load_refactor_boundaries: skipping entry %d: %s",
|
|
255
|
+
i, exc,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
_log.info(
|
|
259
|
+
"load_refactor_seeds: loaded %d boundaries from %s",
|
|
260
|
+
len(boundaries), boundaries_path,
|
|
261
|
+
)
|
|
262
|
+
return boundaries
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# Backward compatibility alias (deprecated, use load_refactor_seeds)
|
|
266
|
+
load_refactor_boundaries = load_refactor_seeds
|