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.
Files changed (131) hide show
  1. vigil_codeintel-0.1.0.dist-info/METADATA +780 -0
  2. vigil_codeintel-0.1.0.dist-info/RECORD +131 -0
  3. vigil_codeintel-0.1.0.dist-info/WHEEL +5 -0
  4. vigil_codeintel-0.1.0.dist-info/entry_points.txt +3 -0
  5. vigil_codeintel-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. vigil_codeintel-0.1.0.dist-info/top_level.txt +3 -0
  7. vigil_forensic/__init__.py +224 -0
  8. vigil_forensic/_git_utils.py +178 -0
  9. vigil_forensic/_shared.py +510 -0
  10. vigil_forensic/_stubs.py +156 -0
  11. vigil_forensic/gate_checks/__init__.py +1 -0
  12. vigil_forensic/gate_checks/_ast_helpers.py +629 -0
  13. vigil_forensic/gate_checks/_deployment_detector.py +573 -0
  14. vigil_forensic/gate_checks/atomic_write_checks.py +1143 -0
  15. vigil_forensic/gate_checks/authority_checks.py +95 -0
  16. vigil_forensic/gate_checks/boundary_breach_checks.py +202 -0
  17. vigil_forensic/gate_checks/broad_except_checks.py +301 -0
  18. vigil_forensic/gate_checks/broad_except_hidden_sentinel_checks.py +365 -0
  19. vigil_forensic/gate_checks/common.py +253 -0
  20. vigil_forensic/gate_checks/config_safety_checks.py +704 -0
  21. vigil_forensic/gate_checks/config_ssot_checks.py +78 -0
  22. vigil_forensic/gate_checks/conflict_checks.py +193 -0
  23. vigil_forensic/gate_checks/context_fallback_checks.py +697 -0
  24. vigil_forensic/gate_checks/context_health_checks.py +289 -0
  25. vigil_forensic/gate_checks/contract_shape_drift_checks.py +459 -0
  26. vigil_forensic/gate_checks/dirty_baseline_check.py +274 -0
  27. vigil_forensic/gate_checks/duplication_checks.py +387 -0
  28. vigil_forensic/gate_checks/embedded_string_checks.py +123 -0
  29. vigil_forensic/gate_checks/empty_output_checks.py +87 -0
  30. vigil_forensic/gate_checks/encoding_checks.py +847 -0
  31. vigil_forensic/gate_checks/export_completeness_checks.py +156 -0
  32. vigil_forensic/gate_checks/fallback_checks.py +41 -0
  33. vigil_forensic/gate_checks/file_proliferation_checks.py +171 -0
  34. vigil_forensic/gate_checks/fix_without_test_checks.py +69 -0
  35. vigil_forensic/gate_checks/forensic_cluster_runners/__init__.py +9 -0
  36. vigil_forensic/gate_checks/forensic_cluster_runners/_helpers.py +71 -0
  37. vigil_forensic/gate_checks/forensic_cluster_runners/advanced_checks.py +322 -0
  38. vigil_forensic/gate_checks/forensic_cluster_runners/core.py +273 -0
  39. vigil_forensic/gate_checks/forensic_cluster_runners/integrity_checks.py +203 -0
  40. vigil_forensic/gate_checks/forensic_cluster_runners/quality_checks.py +666 -0
  41. vigil_forensic/gate_checks/forensic_clusters/__init__.py +193 -0
  42. vigil_forensic/gate_checks/forensic_clusters/allowlist.py +426 -0
  43. vigil_forensic/gate_checks/forensic_clusters/allowlist_writer.py +302 -0
  44. vigil_forensic/gate_checks/forensic_clusters/api_protocol.py +231 -0
  45. vigil_forensic/gate_checks/forensic_clusters/async_quality.py +1156 -0
  46. vigil_forensic/gate_checks/forensic_clusters/code_style.py +808 -0
  47. vigil_forensic/gate_checks/forensic_clusters/core.py +319 -0
  48. vigil_forensic/gate_checks/forensic_clusters/data_quality.py +763 -0
  49. vigil_forensic/gate_checks/forensic_clusters/dead_code.py +480 -0
  50. vigil_forensic/gate_checks/forensic_clusters/edit_mutation.py +842 -0
  51. vigil_forensic/gate_checks/forensic_clusters/exception_boundary.py +240 -0
  52. vigil_forensic/gate_checks/forensic_clusters/legacy_debt.py +556 -0
  53. vigil_forensic/gate_checks/forensic_clusters/static_analysis.py +834 -0
  54. vigil_forensic/gate_checks/forensic_clusters/structural_quality.py +298 -0
  55. vigil_forensic/gate_checks/god_object_zones_checks.py +173 -0
  56. vigil_forensic/gate_checks/hallucination_checks.py +566 -0
  57. vigil_forensic/gate_checks/hunter_artifact_completeness_check.py +139 -0
  58. vigil_forensic/gate_checks/implementation_overfit_checks.py +380 -0
  59. vigil_forensic/gate_checks/import_integrity_checks.py +233 -0
  60. vigil_forensic/gate_checks/imports_in_function_checks.py +283 -0
  61. vigil_forensic/gate_checks/ml_checks.py +318 -0
  62. vigil_forensic/gate_checks/performance_checks.py +106 -0
  63. vigil_forensic/gate_checks/project_specific_runner.py +691 -0
  64. vigil_forensic/gate_checks/provider_capability_checks.py +73 -0
  65. vigil_forensic/gate_checks/refactor_completeness_checks.py +274 -0
  66. vigil_forensic/gate_checks/reliability_checks.py +389 -0
  67. vigil_forensic/gate_checks/reporting_checks.py +55 -0
  68. vigil_forensic/gate_checks/runtime_behavior_checks.py +220 -0
  69. vigil_forensic/gate_checks/security_injection_checks.py +332 -0
  70. vigil_forensic/gate_checks/semantic_intent_checks.py +139 -0
  71. vigil_forensic/gate_checks/size_complexity_checks.py +336 -0
  72. vigil_forensic/gate_checks/stuck_feature_flag_checks.py +354 -0
  73. vigil_forensic/gate_checks/syntax_validity_checks.py +217 -0
  74. vigil_forensic/gate_checks/temporal_freshness_checks.py +79 -0
  75. vigil_forensic/gate_checks/test_quality_checks.py +946 -0
  76. vigil_forensic/gate_checks/testing_checks.py +149 -0
  77. vigil_forensic/gate_checks/toctou_checks.py +367 -0
  78. vigil_forensic/gate_checks/type_checking_checks.py +316 -0
  79. vigil_forensic/gate_models.py +392 -0
  80. vigil_forensic/gate_packs/__init__.py +1 -0
  81. vigil_forensic/gate_packs/universal.py +179 -0
  82. vigil_forensic/gate_profile.json +31 -0
  83. vigil_forensic/gate_registry.py +21 -0
  84. vigil_forensic/language_profiles.py +219 -0
  85. vigil_forensic/meta_findings.py +207 -0
  86. vigil_forensic/self_audit.py +725 -0
  87. vigil_forensic/source_analysis.py +175 -0
  88. vigil_mapper/__init__.py +103 -0
  89. vigil_mapper/_ast_helpers_minimal.py +229 -0
  90. vigil_mapper/_extract_imports_impl.py +123 -0
  91. vigil_mapper/_file_count_guard.py +129 -0
  92. vigil_mapper/_git_utils.py +178 -0
  93. vigil_mapper/_runtime_ast.py +438 -0
  94. vigil_mapper/_runtime_dispatch.py +137 -0
  95. vigil_mapper/_seed_helpers.py +82 -0
  96. vigil_mapper/authority_builder.py +1102 -0
  97. vigil_mapper/cli_entry.py +731 -0
  98. vigil_mapper/conflict_builder.py +818 -0
  99. vigil_mapper/data_contract_builder.py +446 -0
  100. vigil_mapper/findings_builder.py +716 -0
  101. vigil_mapper/fingerprint.py +53 -0
  102. vigil_mapper/hotspot_builder.py +539 -0
  103. vigil_mapper/map_common.py +449 -0
  104. vigil_mapper/map_errors.py +55 -0
  105. vigil_mapper/map_models.py +431 -0
  106. vigil_mapper/map_models_ext.py +206 -0
  107. vigil_mapper/map_models_findings.py +130 -0
  108. vigil_mapper/map_storage.py +455 -0
  109. vigil_mapper/parse_cache.py +795 -0
  110. vigil_mapper/refactor_boundary_builder.py +266 -0
  111. vigil_mapper/runtime_builder.py +527 -0
  112. vigil_mapper/runtime_tracer.py +243 -0
  113. vigil_mapper/runtime_tracer_entry.py +199 -0
  114. vigil_mapper/semantic_diff.py +71 -0
  115. vigil_mapper/source_adapters/__init__.py +109 -0
  116. vigil_mapper/source_adapters/_base.py +264 -0
  117. vigil_mapper/source_adapters/_ir.py +156 -0
  118. vigil_mapper/source_adapters/_lexer.py +309 -0
  119. vigil_mapper/source_adapters/_patterns.py +212 -0
  120. vigil_mapper/source_adapters/_treesitter.py +182 -0
  121. vigil_mapper/source_adapters/go.py +553 -0
  122. vigil_mapper/source_adapters/java.py +541 -0
  123. vigil_mapper/source_adapters/javascript.py +626 -0
  124. vigil_mapper/source_adapters/python.py +325 -0
  125. vigil_mapper/source_adapters/typescript.py +749 -0
  126. vigil_mapper/structural_builder.py +586 -0
  127. vigil_mcp/__init__.py +1 -0
  128. vigil_mcp/_jobs.py +587 -0
  129. vigil_mcp/_paths.py +93 -0
  130. vigil_mcp/forensic_server.py +419 -0
  131. vigil_mcp/map_server.py +452 -0
@@ -0,0 +1,78 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+ from vigil_forensic._shared import EvidenceReference, GateCategory, GateImpact, GateSeverity
6
+ from vigil_forensic.gate_models import PostExecGateContext
7
+ from .common import build_check_result, build_finding, iter_touched_snapshots
8
+ import logging
9
+ _log = logging.getLogger(__name__)
10
+
11
+ # Config keys that must be read via the canonical config path, not env vars directly.
12
+ _ENV_BYPASS_KEYS = {"VIGIL_PORT", "CONTROL_PLANE_PORT", "VIGIL_DB_PATH"}
13
+
14
+ # Files that are legitimately allowed to read these env vars (e.g. the canonical config resolver itself).
15
+ _ALLOWED_ENV_READERS = {"config.py", "settings.py", "config_loader.py", "vigil_config.py"}
16
+
17
+ # Matches os.environ.get( or getenv( followed (anywhere on the same line) by a known key.
18
+ _ENV_PATTERN = re.compile(r'(?:os\.environ\.get\s*\(|getenv\s*\()')
19
+
20
+
21
+ def run_config_ssot_checks(ctx: PostExecGateContext):
22
+ findings = []
23
+ profile = ctx.repo_profile
24
+ if profile is None:
25
+ return build_check_result(check_id="config_ssot", category=GateCategory.CONFIG_SSOT)
26
+ for snapshot in iter_touched_snapshots(ctx):
27
+ if not snapshot.exists:
28
+ continue
29
+ # -- existing check: canonical literal owners --
30
+ for literal, owners in profile.canonical_literal_owners.items():
31
+ if literal not in snapshot.text:
32
+ continue
33
+ if snapshot.path in owners:
34
+ continue
35
+ findings.append(
36
+ build_finding(
37
+ check_id="config_ssot.literal_owner",
38
+ category=GateCategory.CONFIG_SSOT,
39
+ title="Touched code bypasses a canonical config owner",
40
+ severity=GateSeverity.HIGH,
41
+ impact=GateImpact.REVISE,
42
+ summary=f"Literal '{literal}' appears in {snapshot.path}, but canonical owner paths are {', '.join(owners[:3])}.",
43
+ recommendation="Move or reference the invariant from its canonical owner instead of duplicating the literal.",
44
+ evidence=[EvidenceReference(kind="file", path=snapshot.path, detail=literal)],
45
+
46
+ repair_kind='fix_contract',
47
+ executor_action='Fix contract violation',
48
+ proof_required='Contract respected',
49
+ allowlist_allowed=False,
50
+ )
51
+ )
52
+ # -- new check: env var bypass of known config keys --
53
+ filename = snapshot.path.split("/")[-1].split("\\")[-1]
54
+ if filename not in _ALLOWED_ENV_READERS:
55
+ for lineno, line in enumerate(snapshot.text.splitlines(), start=1):
56
+ if not _ENV_PATTERN.search(line):
57
+ continue
58
+ matched_key = next((k for k in _ENV_BYPASS_KEYS if k in line), None)
59
+ if matched_key is None:
60
+ continue
61
+ findings.append(
62
+ build_finding(
63
+ check_id="config_ssot.env_bypass",
64
+ category=GateCategory.CONFIG_SSOT,
65
+ title="Config value accessed via environment variable bypass",
66
+ severity=GateSeverity.MEDIUM,
67
+ impact=GateImpact.REVISE,
68
+ summary="Config value accessed via environment variable bypass instead of canonical config path",
69
+ recommendation=f"Read '{matched_key}' through the canonical config resolver, not directly via os.environ/getenv.",
70
+ evidence=[EvidenceReference(kind="file", path=snapshot.path, detail=f"line {lineno}: {line.strip()}")],
71
+
72
+ repair_kind='fix_contract',
73
+ executor_action='Fix contract violation',
74
+ proof_required='Contract respected',
75
+ allowlist_allowed=False,
76
+ )
77
+ )
78
+ return build_check_result(check_id="config_ssot", category=GateCategory.CONFIG_SSOT, findings=findings)
@@ -0,0 +1,193 @@
1
+ """Unresolved high-conflict touch forensic gate (Finding 6.6).
2
+
3
+ conflict_touch: detect when a PR touches files flagged in the conflict map
4
+ as unresolved and with severity high or critical.
5
+
6
+ Primary path: use ctx.maps.conflict when hydrated.
7
+ Fallback: read .cortex/maps/50_conflict_map.json from disk (legacy callers).
8
+
9
+ Fail-open: map missing, unreadable, or malformed -> return empty findings.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import logging
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ from vigil_forensic._shared import EvidenceReference, GateCategory, GateImpact, GateSeverity
19
+ from vigil_forensic.gate_models import PostExecGateContext
20
+
21
+ _CATEGORY = GateCategory.CONTRACT
22
+ from .common import build_check_result, build_finding, normalize_path
23
+
24
+ _log = logging.getLogger(__name__)
25
+
26
+ _MAP_REL_PATH = ".cortex/maps/50_conflict_map.json"
27
+
28
+ _HIGH_SEVERITIES = frozenset({"high", "critical"})
29
+
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # Internal helpers
33
+ # ---------------------------------------------------------------------------
34
+
35
+
36
+ def _load_conflicts(project_dir: Path) -> list[dict[str, Any]] | None:
37
+ """Load conflict entries from the map file.
38
+
39
+ Returns a list of conflict dicts on success, or None on any failure
40
+ (missing file, JSON error, unexpected top-level shape).
41
+ """
42
+ map_path = project_dir / _MAP_REL_PATH
43
+ try:
44
+ raw = map_path.read_text(encoding="utf-8")
45
+ except OSError as exc:
46
+ _log.debug("conflict_touch: map not found at %s: %s", map_path, exc)
47
+ return None
48
+
49
+ try:
50
+ data = json.loads(raw)
51
+ except json.JSONDecodeError as exc:
52
+ _log.debug("conflict_touch: map JSON malformed: %s", exc)
53
+ return None
54
+
55
+ if not isinstance(data, dict):
56
+ _log.debug("conflict_touch: map root is not a dict")
57
+ return None
58
+
59
+ conflicts_raw = data.get("conflicts")
60
+ if not isinstance(conflicts_raw, list):
61
+ _log.debug("conflict_touch: map has no 'conflicts' list")
62
+ return None
63
+
64
+ return [c for c in conflicts_raw if isinstance(c, dict)]
65
+
66
+
67
+ def _entry_from_conflict(c: Any) -> dict[str, Any]:
68
+ """Convert a ConflictEntry dataclass instance to the dict format used internally.
69
+
70
+ ConflictEntry uses ``conflict_status`` (open/in_progress/resolved) rather than
71
+ a boolean ``resolved`` field. Map to the dict shape that _build_high_conflict_files
72
+ expects: ``resolved`` = True only when conflict_status == "resolved".
73
+ """
74
+ conflict_status = str(getattr(c, "conflict_status", "open") or "open").lower()
75
+ # Gather files from ``sources`` tuples (JSON strings containing {"file": ...} dicts)
76
+ # and from the subject field as a best-effort path hint.
77
+ import json as _json
78
+ files: list[str] = []
79
+ for src_raw in (getattr(c, "sources", ()) or ()):
80
+ try:
81
+ src = _json.loads(src_raw) if isinstance(src_raw, str) else src_raw
82
+ if isinstance(src, dict):
83
+ # Sources may carry a "file" key
84
+ if "file" in src:
85
+ files.append(str(src["file"]))
86
+ except _json.JSONDecodeError as exc:
87
+ _log.debug("conflict_checks: skipping malformed source JSON %r: %s", src_raw, exc)
88
+ # Fallback safe here: one unparseable source string doesn't affect other sources
89
+ # Evidence strings may also contain file paths
90
+ for ev in (getattr(c, "evidence", ()) or ()):
91
+ ev_str = str(ev)
92
+ if ev_str and ("/" in ev_str or "\\" in ev_str) and not ev_str.startswith("{"):
93
+ files.append(ev_str)
94
+ return {
95
+ "files": files,
96
+ "severity": str(getattr(c, "severity", "medium") or "medium"),
97
+ "resolved": conflict_status == "resolved",
98
+ "summary": str(getattr(c, "subject", "") or ""),
99
+ }
100
+
101
+
102
+ def _build_high_conflict_files(conflicts: list[dict[str, Any]]) -> set[str]:
103
+ """Return the set of file paths that are unresolved AND high/critical severity."""
104
+ result: set[str] = set()
105
+ for entry in conflicts:
106
+ resolved = entry.get("resolved", True)
107
+ if resolved:
108
+ continue
109
+ severity = str(entry.get("severity", "")).lower()
110
+ if severity not in _HIGH_SEVERITIES:
111
+ continue
112
+ files_raw = entry.get("files")
113
+ if isinstance(files_raw, list):
114
+ for f in files_raw:
115
+ if f:
116
+ result.add(normalize_path(str(f)))
117
+ return result
118
+
119
+
120
+ # ---------------------------------------------------------------------------
121
+ # Public gate function
122
+ # ---------------------------------------------------------------------------
123
+
124
+
125
+ def run_conflict_touch_checks(ctx: PostExecGateContext):
126
+ """Emit a finding for every changed file that is an unresolved high/critical conflict.
127
+
128
+ Primary path: use ctx.maps.conflict when hydrated and not missing.
129
+ Fallback: read map JSON file from disk (legacy callers / maps not loaded).
130
+
131
+ Fail-open: if the map is missing or malformed, return empty findings.
132
+ """
133
+ # --- Primary: ctx.maps ---------------------------------------------------
134
+ conflicts: list[dict[str, Any]] | None = None
135
+ if ctx.maps is not None and not getattr(ctx.maps, "missing", False):
136
+ cm = getattr(ctx.maps, "conflict", None)
137
+ if cm:
138
+ conflicts = [_entry_from_conflict(c) for c in cm]
139
+ _log.debug("conflict_touch: using %d entries from ctx.maps", len(conflicts))
140
+
141
+ # --- Fallback: file read --------------------------------------------------
142
+ if conflicts is None:
143
+ conflicts = _load_conflicts(ctx.project_dir)
144
+
145
+ if conflicts is None:
146
+ return build_check_result(
147
+ check_id="conflict_touch",
148
+ category=_CATEGORY,
149
+ notes=["conflict_touch: map missing or malformed -- skipped (fail-open)"],
150
+ )
151
+
152
+ high_conflict_files = _build_high_conflict_files(conflicts)
153
+
154
+ findings = []
155
+ for raw_path in ctx.changed_files_observed:
156
+ normalized = normalize_path(raw_path)
157
+ if normalized in high_conflict_files:
158
+ findings.append(
159
+ build_finding(
160
+ check_id="conflict_touch.unresolved_high_conflict",
161
+ category=_CATEGORY,
162
+ title="Changed file has an unresolved high-severity conflict",
163
+ severity=GateSeverity.MEDIUM,
164
+ impact=GateImpact.REVISE,
165
+ summary=(
166
+ f"File '{normalized}' was changed and is flagged in the conflict map "
167
+ f"as unresolved with high or critical severity."
168
+ ),
169
+ recommendation=(
170
+ "Resolve the conflict recorded in the conflict map before landing "
171
+ f"this change, or update {_MAP_REL_PATH} if the conflict was "
172
+ "already addressed."
173
+ ),
174
+ evidence=[
175
+ EvidenceReference(
176
+ kind="changed_file",
177
+ path=normalized,
178
+ detail="unresolved high/critical conflict in conflict map",
179
+ )
180
+ ],
181
+
182
+ repair_kind='fix_contract',
183
+ executor_action='Fix conflict',
184
+ proof_required='No conflict',
185
+ allowlist_allowed=False,
186
+ )
187
+ )
188
+
189
+ return build_check_result(
190
+ check_id="conflict_touch",
191
+ category=_CATEGORY,
192
+ findings=findings,
193
+ )