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,289 @@
1
+ """Context health gate (B3 wave, 2026-04-23).
2
+
3
+ The autoforensics system previously had a silent failure mode: when the
4
+ ``PostExecGateContext`` was assembled with empty fields (e.g. ``file_snapshots
5
+ == {}`` because snapshot collection failed upstream, or ``touched_files == ()``
6
+ because the changed-files list was lost), every write-safety gate would
7
+ trivially return "no findings". The report would read PASS even though no
8
+ meaningful audit actually happened.
9
+
10
+ This gate closes that gap. It runs FIRST in the dispatch order (conceptually
11
+ — ordering is enforced by GATE_SPECS position) and emits meta-level findings
12
+ describing exactly which context slots were unpopulated and why that matters.
13
+
14
+ Design rules
15
+ ------------
16
+ * Runs in BOTH static (sessionless) and full-audit modes. It is the single
17
+ gate whose job is to tell you whether the OTHER gates had enough context
18
+ to produce meaningful results.
19
+ * Uses ``attempt_id`` to detect static mode — the static CLI sets
20
+ ``attempt_id="forensic_audit_static"`` / ``self_audit`` (see
21
+ ``cli_forensic_audit._build_static_context`` and
22
+ ``self_audit.build_synthetic_context``). In static mode we deliberately
23
+ suppress findings that are expected to be empty (e.g. empty
24
+ ``file_snapshots`` is the normal case for the CLI static runner).
25
+ * Emits findings via the shared ``build_finding`` helper so they flow through
26
+ the same allowlist / severity override machinery as the other gates. It
27
+ does NOT use ``meta_findings.emit_meta_finding`` because this gate runs
28
+ in-band (it's a real gate runner, not an out-of-band emitter).
29
+ """
30
+ from __future__ import annotations
31
+
32
+ import logging
33
+
34
+ from vigil_forensic._shared import (
35
+ EvidenceReference,
36
+ GateCategory,
37
+ GateCheckResult,
38
+ GateFinding,
39
+ GateImpact,
40
+ GateSeverity,
41
+ )
42
+ from vigil_forensic.gate_models import PostExecGateContext
43
+
44
+ from .common import build_check_result, build_finding
45
+
46
+ _log = logging.getLogger(__name__)
47
+
48
+ # Static-mode attempt_id sentinel values. Kept here rather than imported from
49
+ # cli_forensic_audit to avoid a circular import between gate_checks/* and
50
+ # BRAIN/autoforensics/cli_forensic_audit.py.
51
+ _STATIC_MODE_ATTEMPT_IDS: frozenset[str] = frozenset({
52
+ "forensic_audit_static",
53
+ "self_audit",
54
+ })
55
+
56
+
57
+ def _is_static_mode(ctx: PostExecGateContext) -> bool:
58
+ """Return True if the context was assembled by the static CLI runners.
59
+
60
+ Static-mode contexts legitimately have empty ``file_snapshots`` (self_audit
61
+ populates them, but cli_forensic_audit builds them lazily on access),
62
+ empty ``session_artifacts``, missing git state, etc. Suppressing findings
63
+ in that mode avoids drowning the report in meta-noise when the user is
64
+ intentionally running a sessionless scan.
65
+ """
66
+ return ctx.attempt_id in _STATIC_MODE_ATTEMPT_IDS
67
+
68
+
69
+ def _has_git_state(ctx: PostExecGateContext) -> bool:
70
+ """Detect whether the context carries git-derived state.
71
+
72
+ ``PostExecGateContext`` does not have a dedicated ``git_state`` attribute
73
+ in the current data model — git-dependent information is scattered across
74
+ ``runtime_state.extras``, ``forensic_report``, and per-gate git_show
75
+ probes. We treat "git state available" as any of:
76
+
77
+ * ``ctx.runtime_state.extras`` carries a key containing "git";
78
+ * ``ctx.artifact_refs`` references a git artifact;
79
+ * ``ctx.forensic_report`` has a non-empty ``current_task_id`` (implies
80
+ a live session with git history reachable).
81
+
82
+ This heuristic is intentionally generous: we only want to emit a finding
83
+ when NO path to git state is available, not every time a particular
84
+ optional field is empty.
85
+ """
86
+ # Runtime-state extras often carry git_hash, git_branch, etc.
87
+ try:
88
+ for key, _val in ctx.runtime_state.extras:
89
+ if "git" in str(key).lower():
90
+ return True
91
+ except AttributeError:
92
+ pass
93
+
94
+ # Artifact references — e.g., git_blame_<file>.txt
95
+ for key in ctx.artifact_refs.keys():
96
+ if "git" in str(key).lower():
97
+ return True
98
+
99
+ # Live session with current_task_id implies git history reachable
100
+ current_task = getattr(ctx.forensic_report, "current_task_id", None)
101
+ if current_task:
102
+ return True
103
+
104
+ return False
105
+
106
+
107
+ def _session_dir_exists(ctx: PostExecGateContext) -> bool:
108
+ """Return True if the expected session artifact directory exists on disk.
109
+
110
+ Only meaningful when ``session_number > 0`` (i.e. we're in an active
111
+ session, not a static scan). Layout follows the project convention of
112
+ ``<project_dir>/.cortex/sessions/<session_number>/``.
113
+ """
114
+ if ctx.session_number <= 0:
115
+ return True # no session → no directory expected
116
+ session_dir = ctx.project_dir / ".cortex" / "sessions" / str(ctx.session_number)
117
+ try:
118
+ return session_dir.is_dir()
119
+ except OSError as exc:
120
+ _log.warning(
121
+ "context_health: OSError probing session dir %s: %s", session_dir, exc,
122
+ )
123
+ # If we can't even probe the directory, downstream gates will also
124
+ # struggle — surface it as "missing" to be safe.
125
+ return False
126
+
127
+
128
+ def run_context_health_checks(ctx: PostExecGateContext) -> GateCheckResult:
129
+ """Emit meta-level findings describing gaps in the audit context itself.
130
+
131
+ Runs in BOTH static and full-audit modes. Findings that would be
132
+ expected in static mode (empty snapshots, no git state, no session dir)
133
+ are suppressed when ``_is_static_mode(ctx)`` is True so the noise floor
134
+ stays meaningful.
135
+ """
136
+ findings: list[GateFinding] = []
137
+ static = _is_static_mode(ctx)
138
+
139
+ # 1. Empty file_snapshots outside static mode -----------------------------
140
+ # This is the original motivating failure: write-safety gates silently
141
+ # pass when they have no snapshots to inspect. In static mode,
142
+ # cli_forensic_audit intentionally leaves snapshots empty (gates read
143
+ # files on demand from disk), so we only fire outside that mode.
144
+ if not static and not ctx.file_snapshots:
145
+ findings.append(
146
+ build_finding(
147
+ check_id="context_health.empty_file_snapshots",
148
+ category=GateCategory.META,
149
+ title="Audit context has no file snapshots",
150
+ severity=GateSeverity.HIGH,
151
+ impact=GateImpact.WARN,
152
+ summary=(
153
+ "ctx.file_snapshots is empty outside static-scan mode. "
154
+ "Write-safety gates (atomic_write, empty_output, "
155
+ "contract_shape_drift, etc.) use snapshots to reason "
156
+ "about file content and will silently produce 0 findings "
157
+ "when the map is empty. A PASS verdict in that state is "
158
+ "meaningless."
159
+ ),
160
+ recommendation=(
161
+ "Populate ctx.file_snapshots in the upstream gate "
162
+ "pipeline before dispatch, OR treat this finding as a "
163
+ "hard failure of the gate infrastructure (not of the "
164
+ "project under audit)."
165
+ ),
166
+ evidence=[
167
+ EvidenceReference(
168
+ kind="context_slot",
169
+ path="ctx.file_snapshots",
170
+ detail=f"attempt_id={ctx.attempt_id!r} task_id={ctx.task_id!r}",
171
+ ok=False,
172
+ )
173
+ ],
174
+ allowlist_allowed=False,
175
+ )
176
+ )
177
+
178
+ # 2. Missing git state ----------------------------------------------------
179
+ # Gates that rely on diff/blame/authorship (authority_checks,
180
+ # contract_shape_drift) will be inconclusive. Emit MEDIUM so operators
181
+ # know findings from those gates may be incomplete.
182
+ if not static and not _has_git_state(ctx):
183
+ findings.append(
184
+ build_finding(
185
+ check_id="context_health.git_state_unavailable",
186
+ category=GateCategory.META,
187
+ title="Audit context is missing git state",
188
+ severity=GateSeverity.MEDIUM,
189
+ impact=GateImpact.WARN,
190
+ summary=(
191
+ "No git-derived state attached to context (no git keys "
192
+ "in runtime_state.extras, no git artifact refs, no "
193
+ "current_task_id). Gates depending on diff / blame / "
194
+ "authorship are inconclusive."
195
+ ),
196
+ recommendation=(
197
+ "Ensure the gate pipeline captures git state upstream "
198
+ "(git rev-parse / git show) when a git repo is "
199
+ "available. If the project is intentionally not a git "
200
+ "repo, disable git-dependent gates explicitly."
201
+ ),
202
+ evidence=[
203
+ EvidenceReference(
204
+ kind="context_slot",
205
+ path="ctx.runtime_state/artifact_refs/forensic_report",
206
+ detail="no git-prefixed key found",
207
+ ok=False,
208
+ )
209
+ ],
210
+ allowlist_allowed=True,
211
+ )
212
+ )
213
+
214
+ # 3. Session artifacts missing -------------------------------------------
215
+ # session_number is set but the expected artifact directory is absent.
216
+ # This typically means an interrupted session or out-of-band cleanup.
217
+ if ctx.session_number > 0 and not _session_dir_exists(ctx):
218
+ session_dir = ctx.project_dir / ".cortex" / "sessions" / str(ctx.session_number)
219
+ findings.append(
220
+ build_finding(
221
+ check_id="context_health.session_artifacts_missing",
222
+ category=GateCategory.META,
223
+ title="Session artifacts directory is missing",
224
+ severity=GateSeverity.MEDIUM,
225
+ impact=GateImpact.WARN,
226
+ summary=(
227
+ f"Expected session artifact dir {session_dir} does not "
228
+ f"exist, yet session_number={ctx.session_number} is set "
229
+ f"on the gate context."
230
+ ),
231
+ recommendation=(
232
+ "Investigate why the session produced no artifact "
233
+ "directory (interrupted session, cleanup race, wrong "
234
+ "project_dir). Artifact-completeness gates cannot "
235
+ "produce meaningful output without it."
236
+ ),
237
+ evidence=[
238
+ EvidenceReference(
239
+ kind="session_artifact_dir",
240
+ path=str(session_dir),
241
+ detail=f"session_number={ctx.session_number}",
242
+ ok=False,
243
+ )
244
+ ],
245
+ allowlist_allowed=True,
246
+ )
247
+ )
248
+
249
+ # 4. Empty touched_files outside static mode ------------------------------
250
+ # This is a soft signal — a genuine no-op audit can produce empty
251
+ # touched_files legitimately. Surface it as LOW so it's visible but
252
+ # doesn't block.
253
+ if not static and not ctx.touched_files:
254
+ findings.append(
255
+ build_finding(
256
+ check_id="context_health.touched_files_empty",
257
+ category=GateCategory.META,
258
+ title="Audit context reports zero touched files",
259
+ severity=GateSeverity.LOW,
260
+ impact=GateImpact.WARN,
261
+ summary=(
262
+ "ctx.touched_files is empty outside static-scan mode. "
263
+ "May be a legitimate no-op audit (metadata-only) but "
264
+ "can also mean the upstream changed-files collector "
265
+ "lost its input."
266
+ ),
267
+ recommendation=(
268
+ "Confirm the audit input is what you expect. If this "
269
+ "audit is genuinely no-op, suppress this finding via "
270
+ "allowlist; otherwise investigate the upstream "
271
+ "changed-files collector."
272
+ ),
273
+ evidence=[
274
+ EvidenceReference(
275
+ kind="context_slot",
276
+ path="ctx.touched_files",
277
+ detail=f"attempt_id={ctx.attempt_id!r}",
278
+ ok=False,
279
+ )
280
+ ],
281
+ allowlist_allowed=True,
282
+ )
283
+ )
284
+
285
+ return build_check_result(
286
+ check_id="context_health",
287
+ category=GateCategory.META,
288
+ findings=findings,
289
+ )