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,316 @@
1
+ from __future__ import annotations
2
+
3
+ """Gate checks for type annotation coverage and `Any` erosion.
4
+
5
+ Sub-checks:
6
+ type_checking.missing_hints_public_api — public functions lacking param/return annotations
7
+ type_checking.any_erosion — excessive `Any` annotations in public API
8
+ """
9
+
10
+ import ast
11
+ import re
12
+ from pathlib import Path
13
+
14
+ from vigil_forensic._shared import (
15
+ EvidenceReference,
16
+ GateCategory,
17
+ GateImpact,
18
+ GateSeverity,
19
+ RepairKind,
20
+ )
21
+ from vigil_forensic.gate_models import PostExecGateContext
22
+ from ..source_analysis import is_source_file, is_test_file, get_language_id
23
+ from .common import build_check_result, build_finding, iter_touched_snapshots, normalize_path
24
+ from ._ast_helpers import parse_python_source_or_emit_finding
25
+ import logging
26
+ _log = logging.getLogger(__name__)
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Constants
30
+ # ---------------------------------------------------------------------------
31
+
32
+ _ANY_EROSION_THRESHOLD = 8 # findings emitted when Any count per-file exceeds this
33
+
34
+ # File basenames that are skipped for any_erosion: re-export / fixture files
35
+ # use Any for compatibility and that is intentional.
36
+ _ANY_EROSION_SKIP_BASENAMES = frozenset({"__init__.py", "conftest.py"})
37
+ _ANY_TS_RE = re.compile(r":\s*any\b", re.IGNORECASE) # JS/TS `: any` annotation pattern
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # AST helpers
41
+ # ---------------------------------------------------------------------------
42
+
43
+ _SKIP_NAMES = frozenset({"__init__", "__new__"})
44
+
45
+
46
+ def _is_public_function(node: ast.FunctionDef | ast.AsyncFunctionDef) -> bool:
47
+ """Return True when the function is part of the public API (name not prefixed with `_`)."""
48
+ return not node.name.startswith("_") and node.name not in _SKIP_NAMES
49
+
50
+
51
+ def _missing_hints(node: ast.FunctionDef | ast.AsyncFunctionDef) -> list[str]:
52
+ """Return a list of missing-annotation descriptions for *node*.
53
+
54
+ Checks:
55
+ - Each non-`self`/`cls` parameter must have an `annotation`.
56
+ - The function must have a `returns` annotation.
57
+ """
58
+ issues: list[str] = []
59
+
60
+ # Parameters — skip implicit self/cls (first arg of methods that have no annotation)
61
+ args = node.args
62
+ all_params = (
63
+ args.posonlyargs
64
+ + args.args
65
+ + ([] if args.vararg is None else [args.vararg])
66
+ + args.kwonlyargs
67
+ + ([] if args.kwarg is None else [args.kwarg])
68
+ )
69
+ for param in all_params:
70
+ if param.arg in ("self", "cls"):
71
+ continue
72
+ if param.annotation is None:
73
+ issues.append(f"param '{param.arg}' missing annotation")
74
+
75
+ if node.returns is None:
76
+ issues.append("return type missing")
77
+
78
+ return issues
79
+
80
+
81
+ def _count_any_in_annotations(tree: ast.Module) -> int:
82
+ """Count `Any` usages in function signatures and class-level field annotations."""
83
+ count = 0
84
+ for node in ast.walk(tree):
85
+ # Function argument and return annotations
86
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
87
+ if _is_any_node(node.returns):
88
+ count += 1
89
+ args = node.args
90
+ all_params = (
91
+ args.posonlyargs
92
+ + args.args
93
+ + ([] if args.vararg is None else [args.vararg])
94
+ + args.kwonlyargs
95
+ + ([] if args.kwarg is None else [args.kwarg])
96
+ )
97
+ for param in all_params:
98
+ if _is_any_node(param.annotation):
99
+ count += 1
100
+ # Dataclass / class-level annotated assignments
101
+ elif isinstance(node, ast.AnnAssign):
102
+ if _is_any_node(node.annotation):
103
+ count += 1
104
+ return count
105
+
106
+
107
+ def _is_any_node(annotation: ast.expr | None) -> bool:
108
+ """True when annotation is the bare Name `Any`."""
109
+ return isinstance(annotation, ast.Name) and annotation.id == "Any"
110
+
111
+
112
+ # ---------------------------------------------------------------------------
113
+ # JS/TS helper
114
+ # ---------------------------------------------------------------------------
115
+
116
+ def _count_any_ts(text: str) -> int:
117
+ """Count `: any` occurrences in JS/TS source text (case-insensitive)."""
118
+ return len(_ANY_TS_RE.findall(text))
119
+
120
+
121
+ # ---------------------------------------------------------------------------
122
+ # Public entry point
123
+ # ---------------------------------------------------------------------------
124
+
125
+ def run_type_checking_checks(ctx: PostExecGateContext):
126
+ """Run both type-checking sub-checks and return a single GateCheckResult.
127
+
128
+ Emits two sub-check kinds of findings (``type_checking.missing_hints_public_api``
129
+ and ``type_checking.any_erosion``) aggregated under one ``type_checking`` gate
130
+ result, matching the contract expected by :func:`post_exec_gate._normalize_check_results`.
131
+ """
132
+ hints_findings: list = []
133
+ erosion_findings: list = []
134
+
135
+ for snapshot in iter_touched_snapshots(ctx):
136
+ if not snapshot.exists or not is_source_file(snapshot.path):
137
+ continue
138
+
139
+ path = snapshot.path
140
+ text = snapshot.text
141
+ lang = get_language_id(path)
142
+
143
+ # ------------------------------------------------------------------
144
+ # Python: AST-based checks
145
+ # ------------------------------------------------------------------
146
+ if lang == "python":
147
+ # B4 (2026-04-23): fail-loud parse via shared helper; meta
148
+ # finding rides along with hints_findings for reporting.
149
+ tree = parse_python_source_or_emit_finding(
150
+ text,
151
+ rel_path=normalize_path(path),
152
+ emit_finding=hints_findings.append,
153
+ emitting_gate="type_checking",
154
+ )
155
+ if tree is None:
156
+ continue
157
+
158
+ # Sub-check 1: missing_hints_public_api
159
+ if not is_test_file(path):
160
+ for node in ast.walk(tree):
161
+ if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
162
+ continue
163
+ if not _is_public_function(node):
164
+ continue
165
+ issues = _missing_hints(node)
166
+ if not issues:
167
+ continue
168
+ line_no = getattr(node, "lineno", 0)
169
+ issue_str = ", ".join(issues)
170
+ hints_findings.append(
171
+ build_finding(
172
+ check_id="type_checking.missing_hints_public_api",
173
+ category=GateCategory.CONTRACT,
174
+ title=(
175
+ f"Public function '{node.name}' in {path}:{line_no} "
176
+ f"missing annotations ({issue_str})"
177
+ ),
178
+ severity=GateSeverity.MEDIUM,
179
+ impact=GateImpact.REVISE,
180
+ summary=(
181
+ f"Function '{node.name}' at {path}:{line_no} is part of the "
182
+ "public API but has incomplete type annotations: "
183
+ f"{issue_str}. "
184
+ "Missing annotations impede static analysis, IDE completion, "
185
+ "and reviewer understanding of the contract."
186
+ ),
187
+ recommendation=(
188
+ "Add type annotations to all parameters and the return type. "
189
+ "Use `from __future__ import annotations` for forward references. "
190
+ "If types are injected by a decorator, add `allowlist: true` to "
191
+ "suppress this finding."
192
+ ),
193
+ evidence=[
194
+ EvidenceReference(
195
+ kind="file",
196
+ path=path,
197
+ detail=f"line:{line_no}",
198
+ )
199
+ ],
200
+ repair_kind=RepairKind.FIX_CONTRACT.value,
201
+ executor_action=(
202
+ f"Add type hints to public function {node.name}: "
203
+ "params + return"
204
+ ),
205
+ proof_required=(
206
+ "function signature has typed params + return; "
207
+ "mypy/pyright clean"
208
+ ),
209
+ allowlist_allowed=True,
210
+ preferred_fix_shape=(
211
+ f"def {node.name}(arg: Type) -> ReturnType:"
212
+ ),
213
+ )
214
+ )
215
+
216
+ # Sub-check 2: any_erosion (Python)
217
+ # Skip re-export hubs and fixture files — Any is intentional there.
218
+ if Path(path).name in _ANY_EROSION_SKIP_BASENAMES:
219
+ continue
220
+ any_count = _count_any_in_annotations(tree)
221
+ if any_count > _ANY_EROSION_THRESHOLD:
222
+ erosion_findings.append(
223
+ build_finding(
224
+ check_id="type_checking.any_erosion",
225
+ category=GateCategory.CONTRACT,
226
+ title=(
227
+ f"Excessive `Any` annotations ({any_count}) in {path}"
228
+ ),
229
+ severity=GateSeverity.LOW,
230
+ impact=GateImpact.WARN,
231
+ summary=(
232
+ f"File {path} contains {any_count} `Any` annotations in "
233
+ "function signatures or dataclass fields. "
234
+ "High `Any` density erodes static-analysis guarantees and "
235
+ "hides type mismatches that mypy/pyright would otherwise catch."
236
+ ),
237
+ recommendation=(
238
+ "Replace `Any` with concrete types (TypedDict, Protocol, or "
239
+ "a concrete class). If a wide type is genuinely needed, use "
240
+ "`object` for contravariant positions or define a narrow Protocol."
241
+ ),
242
+ evidence=[
243
+ EvidenceReference(
244
+ kind="file",
245
+ path=path,
246
+ detail=f"any_count:{any_count}",
247
+ )
248
+ ],
249
+ repair_kind=RepairKind.FIX_CONTRACT.value,
250
+ executor_action=(
251
+ f"Replace {any_count} `Any` annotations with concrete types "
252
+ "(TypedDict, Protocol, or concrete class). "
253
+ "Reference audit in STORAGE/docs/2026-04-22-any_type_audit_*.md"
254
+ ),
255
+ proof_required=(
256
+ f"grep 'Any' in file returns ≤ {_ANY_EROSION_THRESHOLD} occurrences "
257
+ f"where {_ANY_EROSION_THRESHOLD} is new threshold; "
258
+ "ensure no `# type: ignore` added to bypass"
259
+ ),
260
+ allowlist_allowed=True,
261
+ )
262
+ )
263
+
264
+ # ------------------------------------------------------------------
265
+ # JS / TS: regex-based `any` erosion only (no AST missing-hints check)
266
+ # ------------------------------------------------------------------
267
+ elif lang in ("javascript", "typescript"):
268
+ any_count = _count_any_ts(text)
269
+ if any_count > _ANY_EROSION_THRESHOLD:
270
+ erosion_findings.append(
271
+ build_finding(
272
+ check_id="type_checking.any_erosion",
273
+ category=GateCategory.CONTRACT,
274
+ title=(
275
+ f"Excessive `: any` annotations ({any_count}) in {path}"
276
+ ),
277
+ severity=GateSeverity.LOW,
278
+ impact=GateImpact.WARN,
279
+ summary=(
280
+ f"File {path} contains {any_count} `: any` type annotations. "
281
+ "Excessive `any` defeats TypeScript's type system."
282
+ ),
283
+ recommendation=(
284
+ "Replace `: any` with concrete types or `unknown` where the "
285
+ "type is genuinely unknown."
286
+ ),
287
+ evidence=[
288
+ EvidenceReference(
289
+ kind="file",
290
+ path=path,
291
+ detail=f"any_count:{any_count}",
292
+ )
293
+ ],
294
+ repair_kind=RepairKind.FIX_CONTRACT.value,
295
+ executor_action=(
296
+ f"Replace {any_count} `: any` annotations with concrete types "
297
+ "or `unknown`. "
298
+ "Reference audit in STORAGE/docs/2026-04-22-any_type_audit_*.md"
299
+ ),
300
+ proof_required=(
301
+ f"grep ': any' in file returns ≤ {_ANY_EROSION_THRESHOLD} occurrences; "
302
+ "ensure no eslint-disable added to bypass"
303
+ ),
304
+ allowlist_allowed=True,
305
+ )
306
+ )
307
+
308
+ # Go and other languages: stub — skip without raising
309
+ # else: pass
310
+
311
+ all_findings = hints_findings + erosion_findings
312
+ return build_check_result(
313
+ check_id="type_checking",
314
+ category=GateCategory.CONTRACT,
315
+ findings=all_findings,
316
+ )