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,219 @@
|
|
|
1
|
+
"""Per-language forensics metadata for autoforensics analysis.
|
|
2
|
+
|
|
3
|
+
Provides frozen LanguageProfile dataclasses that carry forensics-relevant
|
|
4
|
+
metadata (test patterns, generic stems, flow markers, etc.) for each supported
|
|
5
|
+
language. These profiles are NOT concerned with structural map-building —
|
|
6
|
+
for that, see source_adapters. Profiles complement source_adapters by
|
|
7
|
+
covering the forensic/analytical layer.
|
|
8
|
+
|
|
9
|
+
Public API:
|
|
10
|
+
LanguageProfile -- frozen dataclass with per-language metadata
|
|
11
|
+
PYTHON_PROFILE -- Python profile
|
|
12
|
+
JAVASCRIPT_PROFILE -- JavaScript profile
|
|
13
|
+
TYPESCRIPT_PROFILE -- TypeScript profile
|
|
14
|
+
GO_PROFILE -- Go profile
|
|
15
|
+
JAVA_PROFILE -- Java profile
|
|
16
|
+
get_profile_for_extension -- lookup by file extension (e.g. ".py")
|
|
17
|
+
get_profile_for_lang -- lookup by language_id (e.g. "python")
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
import logging
|
|
23
|
+
_log = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class LanguageProfile:
|
|
28
|
+
"""Forensics metadata for a single programming language.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
language_id: Canonical language identifier (e.g. "python").
|
|
32
|
+
source_extensions: File extensions belonging to this language.
|
|
33
|
+
test_file_patterns: Prefix/suffix patterns for test file detection.
|
|
34
|
+
generic_helper_stems: Stems commonly used for utility/helper modules.
|
|
35
|
+
shared_layer_families: Canonical shared-layer module name families.
|
|
36
|
+
flow_marker_patterns: Regex patterns identifying orchestration steps.
|
|
37
|
+
comment_line_prefixes: Comment start sequences (e.g. "#", "//").
|
|
38
|
+
function_extraction_supported: True when AST-based extraction is available.
|
|
39
|
+
exclude_dir_hints: Vendor/generated directories to skip during analysis.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
language_id: str
|
|
43
|
+
source_extensions: frozenset[str]
|
|
44
|
+
test_file_patterns: tuple[str, ...]
|
|
45
|
+
generic_helper_stems: frozenset[str]
|
|
46
|
+
shared_layer_families: frozenset[str]
|
|
47
|
+
flow_marker_patterns: tuple[str, ...]
|
|
48
|
+
comment_line_prefixes: tuple[str, ...]
|
|
49
|
+
function_extraction_supported: bool
|
|
50
|
+
exclude_dir_hints: frozenset[str]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# Profiles
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
PYTHON_PROFILE = LanguageProfile(
|
|
58
|
+
language_id="python",
|
|
59
|
+
source_extensions=frozenset({".py"}),
|
|
60
|
+
test_file_patterns=("test_", "_test.py"),
|
|
61
|
+
generic_helper_stems=frozenset({
|
|
62
|
+
"utils", "helpers", "common", "shared", "misc",
|
|
63
|
+
"tools", "util", "helper", "support", "base",
|
|
64
|
+
}),
|
|
65
|
+
shared_layer_families=frozenset({
|
|
66
|
+
"shared", "utils", "common", "helpers", "base", "lib", "core",
|
|
67
|
+
}),
|
|
68
|
+
flow_marker_patterns=(
|
|
69
|
+
r"def build_",
|
|
70
|
+
r"def create_",
|
|
71
|
+
r"def run_",
|
|
72
|
+
r"def execute_",
|
|
73
|
+
r"subprocess",
|
|
74
|
+
r"json\.loads",
|
|
75
|
+
r"json\.dumps",
|
|
76
|
+
),
|
|
77
|
+
comment_line_prefixes=("#",),
|
|
78
|
+
function_extraction_supported=True,
|
|
79
|
+
exclude_dir_hints=frozenset({
|
|
80
|
+
"__pycache__", ".venv", "venv", "node_modules", "libs",
|
|
81
|
+
".git", "migrations", "__generated__", "dist", "build",
|
|
82
|
+
}),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
JAVASCRIPT_PROFILE = LanguageProfile(
|
|
86
|
+
language_id="javascript",
|
|
87
|
+
source_extensions=frozenset({".js", ".mjs", ".cjs"}),
|
|
88
|
+
test_file_patterns=(".test.js", ".spec.js", "__tests__/"),
|
|
89
|
+
generic_helper_stems=frozenset({
|
|
90
|
+
"utils", "helpers", "common", "shared", "base",
|
|
91
|
+
"lib", "index", "constants",
|
|
92
|
+
}),
|
|
93
|
+
shared_layer_families=frozenset({
|
|
94
|
+
"shared", "utils", "common", "helpers", "base",
|
|
95
|
+
"lib", "core", "services",
|
|
96
|
+
}),
|
|
97
|
+
flow_marker_patterns=(
|
|
98
|
+
r"function build",
|
|
99
|
+
r"function create",
|
|
100
|
+
r"async function",
|
|
101
|
+
r"fetch\(",
|
|
102
|
+
r"JSON\.parse",
|
|
103
|
+
r"JSON\.stringify",
|
|
104
|
+
),
|
|
105
|
+
comment_line_prefixes=("//", "/*"),
|
|
106
|
+
function_extraction_supported=False,
|
|
107
|
+
exclude_dir_hints=frozenset({
|
|
108
|
+
"node_modules", "dist", "build", ".cache",
|
|
109
|
+
"coverage", "__generated__",
|
|
110
|
+
}),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
TYPESCRIPT_PROFILE = LanguageProfile(
|
|
114
|
+
language_id="typescript",
|
|
115
|
+
source_extensions=frozenset({".ts", ".tsx"}),
|
|
116
|
+
test_file_patterns=(".test.ts", ".spec.ts", "__tests__/"),
|
|
117
|
+
# Inherit generic_helper_stems, shared_layer_families, flow_marker_patterns,
|
|
118
|
+
# comment_line_prefixes, function_extraction_supported, exclude_dir_hints
|
|
119
|
+
# from JAVASCRIPT_PROFILE — TypeScript is a strict superset for forensics purposes.
|
|
120
|
+
generic_helper_stems=JAVASCRIPT_PROFILE.generic_helper_stems,
|
|
121
|
+
shared_layer_families=JAVASCRIPT_PROFILE.shared_layer_families,
|
|
122
|
+
flow_marker_patterns=JAVASCRIPT_PROFILE.flow_marker_patterns,
|
|
123
|
+
comment_line_prefixes=JAVASCRIPT_PROFILE.comment_line_prefixes,
|
|
124
|
+
function_extraction_supported=False,
|
|
125
|
+
exclude_dir_hints=JAVASCRIPT_PROFILE.exclude_dir_hints,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
GO_PROFILE = LanguageProfile(
|
|
129
|
+
language_id="go",
|
|
130
|
+
source_extensions=frozenset({".go"}),
|
|
131
|
+
test_file_patterns=("_test.go",),
|
|
132
|
+
generic_helper_stems=frozenset({
|
|
133
|
+
"utils", "helpers", "common", "internal", "util",
|
|
134
|
+
}),
|
|
135
|
+
shared_layer_families=frozenset({
|
|
136
|
+
"internal", "common", "shared", "helpers", "util",
|
|
137
|
+
}),
|
|
138
|
+
flow_marker_patterns=(
|
|
139
|
+
r"func Build",
|
|
140
|
+
r"func Create",
|
|
141
|
+
r"func Run",
|
|
142
|
+
r"func Execute",
|
|
143
|
+
),
|
|
144
|
+
comment_line_prefixes=("//",),
|
|
145
|
+
function_extraction_supported=False,
|
|
146
|
+
exclude_dir_hints=frozenset({
|
|
147
|
+
"vendor", "testdata", "node_modules",
|
|
148
|
+
}),
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
JAVA_PROFILE = LanguageProfile(
|
|
152
|
+
language_id="java",
|
|
153
|
+
source_extensions=frozenset({".java"}),
|
|
154
|
+
test_file_patterns=("Test.java", "Tests.java", "/test/"),
|
|
155
|
+
generic_helper_stems=frozenset({
|
|
156
|
+
"Utils", "Helper", "Common", "Shared", "Base",
|
|
157
|
+
"Util", "Helpers",
|
|
158
|
+
}),
|
|
159
|
+
shared_layer_families=frozenset({
|
|
160
|
+
"utils", "common", "shared", "base", "helper",
|
|
161
|
+
}),
|
|
162
|
+
flow_marker_patterns=(
|
|
163
|
+
r"void build",
|
|
164
|
+
r"void create",
|
|
165
|
+
r"void run",
|
|
166
|
+
r"void execute",
|
|
167
|
+
r"new ObjectMapper\(",
|
|
168
|
+
r"objectMapper\.",
|
|
169
|
+
),
|
|
170
|
+
comment_line_prefixes=("//", "/*"),
|
|
171
|
+
function_extraction_supported=False,
|
|
172
|
+
exclude_dir_hints=frozenset({
|
|
173
|
+
"target", "build", ".gradle", "node_modules", "generated",
|
|
174
|
+
}),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# ---------------------------------------------------------------------------
|
|
178
|
+
# Lookup indices
|
|
179
|
+
# ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
_PROFILES_BY_EXTENSION: dict[str, LanguageProfile] = {
|
|
182
|
+
ext: profile
|
|
183
|
+
for profile in [
|
|
184
|
+
PYTHON_PROFILE,
|
|
185
|
+
JAVASCRIPT_PROFILE,
|
|
186
|
+
TYPESCRIPT_PROFILE,
|
|
187
|
+
GO_PROFILE,
|
|
188
|
+
JAVA_PROFILE,
|
|
189
|
+
]
|
|
190
|
+
for ext in profile.source_extensions
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
_PROFILES_BY_LANG: dict[str, LanguageProfile] = {
|
|
194
|
+
p.language_id: p
|
|
195
|
+
for p in [
|
|
196
|
+
PYTHON_PROFILE,
|
|
197
|
+
JAVASCRIPT_PROFILE,
|
|
198
|
+
TYPESCRIPT_PROFILE,
|
|
199
|
+
GO_PROFILE,
|
|
200
|
+
JAVA_PROFILE,
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# ---------------------------------------------------------------------------
|
|
206
|
+
# Helpers
|
|
207
|
+
# ---------------------------------------------------------------------------
|
|
208
|
+
|
|
209
|
+
def get_profile_for_extension(ext: str) -> LanguageProfile | None:
|
|
210
|
+
"""Return the LanguageProfile for *ext* (e.g. ``".py"``), or None.
|
|
211
|
+
|
|
212
|
+
Lookup is case-insensitive: ``".PY"`` resolves to the same profile as ``".py"``.
|
|
213
|
+
"""
|
|
214
|
+
return _PROFILES_BY_EXTENSION.get(ext.lower())
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def get_profile_for_lang(lang_id: str) -> LanguageProfile | None:
|
|
218
|
+
"""Return the LanguageProfile for *lang_id* (e.g. ``"python"``), or None."""
|
|
219
|
+
return _PROFILES_BY_LANG.get(lang_id)
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""Defensive meta-integrity findings collector for vigil_forensic.
|
|
2
|
+
|
|
3
|
+
Adapted from the Vigil autoforensics meta_findings.
|
|
4
|
+
All cluster gate_models imports rewritten to vigil_forensic._shared.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import threading
|
|
10
|
+
from collections import deque
|
|
11
|
+
from typing import Any, Optional
|
|
12
|
+
|
|
13
|
+
from vigil_forensic._shared import (
|
|
14
|
+
EvidenceReference,
|
|
15
|
+
GateCategory,
|
|
16
|
+
GateCheckResult,
|
|
17
|
+
GateFinding,
|
|
18
|
+
GateImpact,
|
|
19
|
+
GateSeverity,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
_log = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
_pending: deque[GateFinding] = deque()
|
|
25
|
+
_lock = threading.Lock()
|
|
26
|
+
|
|
27
|
+
META_CHECK_SPECS: dict[str, dict[str, Any]] = {
|
|
28
|
+
"meta.profile_load_failed": {
|
|
29
|
+
"severity": GateSeverity.HIGH,
|
|
30
|
+
"impact": GateImpact.WARN,
|
|
31
|
+
"title": "Gate profile failed to load",
|
|
32
|
+
"recommendation": (
|
|
33
|
+
"Inspect the referenced profile/config file for JSON syntax errors or "
|
|
34
|
+
"missing keys. Gates were executed WITHOUT profile context and may "
|
|
35
|
+
"produce incomplete or misleading results."
|
|
36
|
+
),
|
|
37
|
+
},
|
|
38
|
+
"meta.git_unavailable": {
|
|
39
|
+
"severity": GateSeverity.LOW,
|
|
40
|
+
"impact": GateImpact.WARN,
|
|
41
|
+
"title": "Git unavailable for --with-git audit",
|
|
42
|
+
"recommendation": (
|
|
43
|
+
"Install git and ensure the project directory is a git repository, "
|
|
44
|
+
"or rerun the audit without --with-git. Git-dependent gates were "
|
|
45
|
+
"skipped or produced incomplete findings."
|
|
46
|
+
),
|
|
47
|
+
},
|
|
48
|
+
"meta.artifact_corrupted": {
|
|
49
|
+
"severity": GateSeverity.HIGH,
|
|
50
|
+
"impact": GateImpact.WARN,
|
|
51
|
+
"title": "Forensic artifact is corrupted (JSON decode failed)",
|
|
52
|
+
"recommendation": (
|
|
53
|
+
"The referenced .cortex artifact exists but could not be parsed. "
|
|
54
|
+
"Inspect, repair or delete and rerun."
|
|
55
|
+
),
|
|
56
|
+
},
|
|
57
|
+
"meta.artifact_unreadable": {
|
|
58
|
+
"severity": GateSeverity.HIGH,
|
|
59
|
+
"impact": GateImpact.WARN,
|
|
60
|
+
"title": "Forensic artifact is unreadable (OS error)",
|
|
61
|
+
"recommendation": (
|
|
62
|
+
"The referenced .cortex artifact exists but could not be opened "
|
|
63
|
+
"due to a filesystem/permission error. Check file permissions and disk health."
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
"meta.syntax_parse_error": {
|
|
67
|
+
"severity": GateSeverity.HIGH,
|
|
68
|
+
"impact": GateImpact.REVISE,
|
|
69
|
+
"title": "Python syntax error blocked gate from parsing file",
|
|
70
|
+
"recommendation": (
|
|
71
|
+
"Fix the Python syntax error in the referenced file so gates can parse and audit it."
|
|
72
|
+
),
|
|
73
|
+
},
|
|
74
|
+
"meta.file_unreadable": {
|
|
75
|
+
"severity": GateSeverity.MEDIUM,
|
|
76
|
+
"impact": GateImpact.WARN,
|
|
77
|
+
"title": "Source file exists but is unreadable",
|
|
78
|
+
"recommendation": (
|
|
79
|
+
"Investigate the filesystem/permission error on the referenced path."
|
|
80
|
+
),
|
|
81
|
+
},
|
|
82
|
+
"meta.allowlist_corrupted": {
|
|
83
|
+
"severity": GateSeverity.HIGH,
|
|
84
|
+
"impact": GateImpact.WARN,
|
|
85
|
+
"title": "False-positive allowlist JSON is corrupted",
|
|
86
|
+
"recommendation": (
|
|
87
|
+
"Repair or delete .prompt-engineer/forensic_gates/false_positive_allowlist.json."
|
|
88
|
+
),
|
|
89
|
+
},
|
|
90
|
+
"context_health.empty_file_snapshots": {
|
|
91
|
+
"severity": GateSeverity.HIGH,
|
|
92
|
+
"impact": GateImpact.WARN,
|
|
93
|
+
"title": "Audit context has no file snapshots",
|
|
94
|
+
"recommendation": (
|
|
95
|
+
"Write-safety gates rely on ctx.file_snapshots. An empty snapshots map means "
|
|
96
|
+
"every such gate trivially PASSes."
|
|
97
|
+
),
|
|
98
|
+
},
|
|
99
|
+
"context_health.git_state_unavailable": {
|
|
100
|
+
"severity": GateSeverity.MEDIUM,
|
|
101
|
+
"impact": GateImpact.WARN,
|
|
102
|
+
"title": "Audit context is missing git state",
|
|
103
|
+
"recommendation": (
|
|
104
|
+
"Diff / blame / authorship-sensitive gates will be inconclusive."
|
|
105
|
+
),
|
|
106
|
+
},
|
|
107
|
+
"context_health.session_artifacts_missing": {
|
|
108
|
+
"severity": GateSeverity.MEDIUM,
|
|
109
|
+
"impact": GateImpact.WARN,
|
|
110
|
+
"title": "Session artifacts directory is missing",
|
|
111
|
+
"recommendation": (
|
|
112
|
+
"session_number was set but the expected .cortex/sessions/<N>/ directory "
|
|
113
|
+
"does not exist on disk."
|
|
114
|
+
),
|
|
115
|
+
},
|
|
116
|
+
"context_health.touched_files_empty": {
|
|
117
|
+
"severity": GateSeverity.LOW,
|
|
118
|
+
"impact": GateImpact.WARN,
|
|
119
|
+
"title": "Audit context reports zero touched files",
|
|
120
|
+
"recommendation": (
|
|
121
|
+
"ctx.touched_files is empty outside static-scan mode."
|
|
122
|
+
),
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def emit_meta_finding(
|
|
128
|
+
check_id: str,
|
|
129
|
+
*,
|
|
130
|
+
path: str = "",
|
|
131
|
+
detail: str = "",
|
|
132
|
+
summary: Optional[str] = None,
|
|
133
|
+
severity: Optional[GateSeverity] = None,
|
|
134
|
+
impact: Optional[GateImpact] = None,
|
|
135
|
+
) -> GateFinding:
|
|
136
|
+
spec = META_CHECK_SPECS.get(check_id)
|
|
137
|
+
if spec is None:
|
|
138
|
+
_log.warning("emit_meta_finding: unregistered check_id=%r — using HIGH/WARN defaults", check_id)
|
|
139
|
+
title = check_id
|
|
140
|
+
recommendation = "(unregistered meta finding — extend META_CHECK_SPECS)"
|
|
141
|
+
eff_severity = severity or GateSeverity.HIGH
|
|
142
|
+
eff_impact = impact or GateImpact.WARN
|
|
143
|
+
else:
|
|
144
|
+
title = spec["title"]
|
|
145
|
+
recommendation = spec["recommendation"]
|
|
146
|
+
eff_severity = severity or spec["severity"]
|
|
147
|
+
eff_impact = impact or spec["impact"]
|
|
148
|
+
|
|
149
|
+
if summary is None:
|
|
150
|
+
parts = [title]
|
|
151
|
+
if path:
|
|
152
|
+
parts.append(f"path={path}")
|
|
153
|
+
if detail:
|
|
154
|
+
parts.append(detail)
|
|
155
|
+
summary = " | ".join(parts)
|
|
156
|
+
|
|
157
|
+
evidence = (EvidenceReference(kind="meta_finding", path=path, detail=detail, ok=False),)
|
|
158
|
+
|
|
159
|
+
finding = GateFinding(
|
|
160
|
+
check_id=check_id,
|
|
161
|
+
category=GateCategory.META,
|
|
162
|
+
title=title,
|
|
163
|
+
severity=eff_severity,
|
|
164
|
+
impact=eff_impact,
|
|
165
|
+
summary=summary,
|
|
166
|
+
recommendation=recommendation,
|
|
167
|
+
evidence=evidence,
|
|
168
|
+
fingerprint=f"meta:{check_id}:{path}:{detail}"[:200],
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
with _lock:
|
|
172
|
+
_pending.append(finding)
|
|
173
|
+
_log.warning("[meta-finding] %s severity=%s path=%r detail=%r", check_id, eff_severity.value, path, detail)
|
|
174
|
+
return finding
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def drain_meta_findings() -> list[GateFinding]:
|
|
178
|
+
with _lock:
|
|
179
|
+
raw = list(_pending)
|
|
180
|
+
_pending.clear()
|
|
181
|
+
# Dedup by fingerprint: two gates emitting the same parse-error for the same
|
|
182
|
+
# (file, line) produce identical fingerprints — collapse to one finding.
|
|
183
|
+
seen: set[str] = set()
|
|
184
|
+
out: list[GateFinding] = []
|
|
185
|
+
for f in raw:
|
|
186
|
+
fp = getattr(f, "fingerprint", None) or ""
|
|
187
|
+
if fp and fp in seen:
|
|
188
|
+
continue
|
|
189
|
+
if fp:
|
|
190
|
+
seen.add(fp)
|
|
191
|
+
out.append(f)
|
|
192
|
+
return out
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def peek_meta_findings() -> list[GateFinding]:
|
|
196
|
+
with _lock:
|
|
197
|
+
return list(_pending)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def reset_meta_findings() -> None:
|
|
201
|
+
with _lock:
|
|
202
|
+
_pending.clear()
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def meta_runner_stub(ctx: Any) -> GateCheckResult:
|
|
206
|
+
"""No-op runner for meta.* gate registrations."""
|
|
207
|
+
return GateCheckResult(check_id="meta", category=GateCategory.META, findings=(), notes=())
|