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,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
|
+
)
|