code-oracle 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.
- code_oracle/__init__.py +30 -0
- code_oracle/cli.py +795 -0
- code_oracle/config.py +145 -0
- code_oracle/dataset.py +5325 -0
- code_oracle/dead_code/__init__.py +32 -0
- code_oracle/dead_code/detector.py +379 -0
- code_oracle/dead_code/entrypoints.py +333 -0
- code_oracle/dead_code/models.py +255 -0
- code_oracle/dead_code/semantics.py +416 -0
- code_oracle/decision.py +906 -0
- code_oracle/engine.py +430 -0
- code_oracle/export_onnx.py +436 -0
- code_oracle/hook.py +531 -0
- code_oracle/indexer.py +894 -0
- code_oracle/languages/__init__.py +114 -0
- code_oracle/languages/common.py +127 -0
- code_oracle/languages/go.py +395 -0
- code_oracle/languages/python.py +336 -0
- code_oracle/languages/rust.py +474 -0
- code_oracle/languages/typescript.py +775 -0
- code_oracle/linearizer.py +166 -0
- code_oracle/locator.py +301 -0
- code_oracle/models.py +237 -0
- code_oracle/perf_lint/__init__.py +38 -0
- code_oracle/perf_lint/engine.py +234 -0
- code_oracle/perf_lint/models.py +229 -0
- code_oracle/perf_lint/rules/__init__.py +31 -0
- code_oracle/perf_lint/rules/async_blocking.py +143 -0
- code_oracle/perf_lint/rules/n_plus_one.py +232 -0
- code_oracle/perf_lint/rules/nested_loops.py +137 -0
- code_oracle/perf_lint/rules/unclosed_res.py +494 -0
- code_oracle/perf_lint/visitor.py +299 -0
- code_oracle/server.py +184 -0
- code_oracle/slicer.py +225 -0
- code_oracle/symbolic.py +459 -0
- code_oracle-0.1.0.dist-info/METADATA +225 -0
- code_oracle-0.1.0.dist-info/RECORD +40 -0
- code_oracle-0.1.0.dist-info/WHEEL +4 -0
- code_oracle-0.1.0.dist-info/entry_points.txt +2 -0
- code_oracle-0.1.0.dist-info/licenses/LICENSE +190 -0
code_oracle/engine.py
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TopoSlice Verification Engine.
|
|
3
|
+
Coordinates the 5 stages of the lean neuro-symbolic verification pipeline.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import copy
|
|
7
|
+
import time
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, List, Optional, Set
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
from code_oracle.config import load_config
|
|
13
|
+
from code_oracle.decision import LayaDecisionHead
|
|
14
|
+
from code_oracle.indexer import WorkspaceIndexer
|
|
15
|
+
from code_oracle.linearizer import linearize_subgraph
|
|
16
|
+
from code_oracle.locator import extract_imports_from_ast, extract_symbols_from_ast, locate_affected_symbols
|
|
17
|
+
from code_oracle.models import EnhancedVerificationReport, PatchResult, RiskTaxonomyScores, VerificationReport
|
|
18
|
+
from code_oracle.slicer import slice_neighborhood
|
|
19
|
+
from code_oracle.symbolic import verify_symbolic_gate
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TopoSliceEngine:
|
|
23
|
+
"""
|
|
24
|
+
Sub-50ms Neuro-Symbolic Verification Engine.
|
|
25
|
+
Executes AST Diff Boundary Locating, Inverted Workspace Indexing,
|
|
26
|
+
k-Hop Slicing, Tarjan SCC Cycle Detection & Contract Checks, and Graph Linearization.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
workspace_root: Optional[Path] = None,
|
|
32
|
+
weights_path: Optional[Path] = None,
|
|
33
|
+
enable_neural: Optional[bool] = None,
|
|
34
|
+
quantize_int8: Optional[bool] = None,
|
|
35
|
+
):
|
|
36
|
+
self.workspace_root = Path(workspace_root or Path.cwd()).resolve()
|
|
37
|
+
self.indexer = WorkspaceIndexer(workspace_root=self.workspace_root)
|
|
38
|
+
|
|
39
|
+
if enable_neural is None:
|
|
40
|
+
neural_env = os.environ.get("CODE_ORACLE_NEURAL", "").strip().lower()
|
|
41
|
+
if neural_env in ("1", "true", "yes"):
|
|
42
|
+
enable_neural = True
|
|
43
|
+
else:
|
|
44
|
+
cfg = load_config(self.workspace_root)
|
|
45
|
+
enable_neural = bool(cfg.get("neural", False))
|
|
46
|
+
|
|
47
|
+
self.enable_neural = bool(enable_neural)
|
|
48
|
+
self.decision_head = LayaDecisionHead(
|
|
49
|
+
weights_path=weights_path,
|
|
50
|
+
enabled=self.enable_neural,
|
|
51
|
+
quantize_int8=quantize_int8,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def verify(
|
|
55
|
+
self,
|
|
56
|
+
file_path: str,
|
|
57
|
+
patch_content: str,
|
|
58
|
+
k: int = 1,
|
|
59
|
+
max_fanout: int = 20,
|
|
60
|
+
taxonomy_threshold: float = 0.5,
|
|
61
|
+
original_content: Optional[str] = None,
|
|
62
|
+
is_replacement: bool = False,
|
|
63
|
+
) -> EnhancedVerificationReport:
|
|
64
|
+
"""
|
|
65
|
+
Verify a code patch proposal against AST topology and contract invariants.
|
|
66
|
+
Returns an EnhancedVerificationReport with structured verdict, sub-400 token DSL,
|
|
67
|
+
Multi-Task Risk Taxonomy, and Epistemic Uncertainty Estimation.
|
|
68
|
+
"""
|
|
69
|
+
start_time = time.perf_counter()
|
|
70
|
+
|
|
71
|
+
# Path normalization relative to workspace root
|
|
72
|
+
p = Path(file_path)
|
|
73
|
+
if p.is_absolute():
|
|
74
|
+
try:
|
|
75
|
+
norm_path = str(p.resolve().relative_to(self.workspace_root.resolve())).replace("\\", "/")
|
|
76
|
+
except ValueError:
|
|
77
|
+
norm_path = str(file_path).replace("\\", "/")
|
|
78
|
+
else:
|
|
79
|
+
full_p = (self.workspace_root / file_path).resolve()
|
|
80
|
+
try:
|
|
81
|
+
norm_path = str(full_p.relative_to(self.workspace_root.resolve())).replace("\\", "/")
|
|
82
|
+
except ValueError:
|
|
83
|
+
norm_path = str(file_path).replace("\\", "/")
|
|
84
|
+
|
|
85
|
+
# Stage 1: Diff Boundary Locator
|
|
86
|
+
patch_result = locate_affected_symbols(
|
|
87
|
+
file_path=norm_path,
|
|
88
|
+
patch_content=patch_content,
|
|
89
|
+
workspace_root=self.workspace_root,
|
|
90
|
+
original_content=original_content,
|
|
91
|
+
is_replacement=is_replacement,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Immediate exit on syntax error
|
|
95
|
+
if patch_result.syntax_error:
|
|
96
|
+
elapsed_ms = (time.perf_counter() - start_time) * 1000.0
|
|
97
|
+
violation_msg = f"SYNTAX_ERROR: {patch_result.syntax_error}"
|
|
98
|
+
tax_scores = RiskTaxonomyScores(breaking_public_api=0.95, silent_logic_drift=0.90)
|
|
99
|
+
return EnhancedVerificationReport(
|
|
100
|
+
status="REJECTED",
|
|
101
|
+
confidence=1.0,
|
|
102
|
+
risk_score=1.0,
|
|
103
|
+
epistemic_uncertainty=0.01,
|
|
104
|
+
risk_taxonomy=tax_scores,
|
|
105
|
+
active_risk_categories=tax_scores.active_categories(threshold=taxonomy_threshold),
|
|
106
|
+
cycles_detected=[],
|
|
107
|
+
invariant_violations=[violation_msg],
|
|
108
|
+
linearized_subgraph=f"[DIFF_TARGET] {norm_path} (SYNTAX_ERROR)\n[GATE]\nSTATUS: REJECTED\nVIOLATIONS:\n - {violation_msg}",
|
|
109
|
+
affected_symbols=[],
|
|
110
|
+
latency_ms=elapsed_ms,
|
|
111
|
+
is_neural_calibrated=False,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Stage 2: Workspace Indexer (incremental update)
|
|
115
|
+
self.indexer.scan_workspace()
|
|
116
|
+
|
|
117
|
+
# Save existing file cache entry for zero-side-effect transient evaluation
|
|
118
|
+
cached_backup = copy.deepcopy(self.indexer._file_cache.get(norm_path))
|
|
119
|
+
backup_syms = list(self.indexer._file_symbols.get(norm_path, []))
|
|
120
|
+
backup_imps = list(self.indexer._file_imports.get(norm_path, []))
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
# If historical base content is provided, initialize base symbols from it
|
|
124
|
+
if original_content is not None:
|
|
125
|
+
orig_symbols = extract_symbols_from_ast(original_content, file_path=norm_path)
|
|
126
|
+
orig_imports = extract_imports_from_ast(original_content, file_path=norm_path)
|
|
127
|
+
self.indexer._file_symbols[norm_path] = orig_symbols
|
|
128
|
+
self.indexer._file_imports[norm_path] = orig_imports
|
|
129
|
+
|
|
130
|
+
# In-memory overlay of transient patched symbols and imports
|
|
131
|
+
if patch_result.all_patched_symbols or patch_result.deleted_symbols or patch_result.imports:
|
|
132
|
+
self.indexer.overlay_transient_symbols(
|
|
133
|
+
norm_path,
|
|
134
|
+
patch_result.all_patched_symbols,
|
|
135
|
+
imports=patch_result.imports,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Stage 3: k-Hop Neighborhood Slicer
|
|
139
|
+
all_seeds = (
|
|
140
|
+
patch_result.affected_symbols
|
|
141
|
+
+ patch_result.added_symbols
|
|
142
|
+
+ patch_result.deleted_symbols
|
|
143
|
+
)
|
|
144
|
+
seen_ids = set()
|
|
145
|
+
seed_symbols = []
|
|
146
|
+
for s in all_seeds:
|
|
147
|
+
if s.id not in seen_ids:
|
|
148
|
+
seen_ids.add(s.id)
|
|
149
|
+
seed_symbols.append(s)
|
|
150
|
+
slice_graph = slice_neighborhood(
|
|
151
|
+
seeds=seed_symbols,
|
|
152
|
+
indexer=self.indexer,
|
|
153
|
+
k=k,
|
|
154
|
+
max_fanout=max_fanout,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Stage 4: Deterministic Symbolic Gate
|
|
158
|
+
gate_result = verify_symbolic_gate(
|
|
159
|
+
patch_result=patch_result,
|
|
160
|
+
slice_graph=slice_graph,
|
|
161
|
+
indexer=self.indexer,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Stage 5: Graph Linearizer (< 400 tokens)
|
|
165
|
+
linearized_dsl = linearize_subgraph(
|
|
166
|
+
patch_result=patch_result,
|
|
167
|
+
slice_graph=slice_graph,
|
|
168
|
+
gate_result=gate_result,
|
|
169
|
+
max_tokens=400,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Stage 6: Decision Head / Risk Calibration & Multi-Task Taxonomy
|
|
173
|
+
decision_res = self.decision_head.predict_multi_task(
|
|
174
|
+
linearized_dsl=linearized_dsl,
|
|
175
|
+
symbolic_status=gate_result.status,
|
|
176
|
+
symbolic_confidence=gate_result.confidence,
|
|
177
|
+
has_violations=bool(gate_result.violations or gate_result.cycles),
|
|
178
|
+
violations=gate_result.violations,
|
|
179
|
+
cycles=gate_result.cycles,
|
|
180
|
+
taxonomy_threshold=taxonomy_threshold,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
elapsed_ms = (time.perf_counter() - start_time) * 1000.0
|
|
184
|
+
|
|
185
|
+
return EnhancedVerificationReport(
|
|
186
|
+
status=decision_res.status,
|
|
187
|
+
confidence=decision_res.confidence,
|
|
188
|
+
risk_score=decision_res.risk_score,
|
|
189
|
+
epistemic_uncertainty=decision_res.epistemic_uncertainty,
|
|
190
|
+
risk_taxonomy=decision_res.risk_taxonomy,
|
|
191
|
+
active_risk_categories=decision_res.active_risk_categories,
|
|
192
|
+
cycles_detected=gate_result.cycles,
|
|
193
|
+
invariant_violations=gate_result.violations,
|
|
194
|
+
linearized_subgraph=linearized_dsl,
|
|
195
|
+
affected_symbols=[s.qualname for s in seed_symbols],
|
|
196
|
+
latency_ms=elapsed_ms,
|
|
197
|
+
is_neural_calibrated=decision_res.is_neural_calibrated,
|
|
198
|
+
engine_mode=decision_res.engine_mode,
|
|
199
|
+
)
|
|
200
|
+
finally:
|
|
201
|
+
# Restore indexer to default disk state (Rollback Resilience)
|
|
202
|
+
self.indexer.restore_transient_symbols(
|
|
203
|
+
norm_path,
|
|
204
|
+
backup_syms,
|
|
205
|
+
backup_imps,
|
|
206
|
+
cached_backup,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def verify_batch(
|
|
210
|
+
self,
|
|
211
|
+
file_patches: List[Dict[str, Any]],
|
|
212
|
+
dirty_overlays: Optional[Dict[str, str]] = None,
|
|
213
|
+
k: int = 1,
|
|
214
|
+
max_fanout: int = 20,
|
|
215
|
+
taxonomy_threshold: float = 0.5,
|
|
216
|
+
) -> EnhancedVerificationReport:
|
|
217
|
+
"""
|
|
218
|
+
Atomically verify a batch of file patches (e.g. staged git files)
|
|
219
|
+
against AST topology and contract invariants with zero-side-effect isolation.
|
|
220
|
+
"""
|
|
221
|
+
start_time = time.perf_counter()
|
|
222
|
+
|
|
223
|
+
if not file_patches:
|
|
224
|
+
tax_scores = RiskTaxonomyScores()
|
|
225
|
+
return EnhancedVerificationReport(
|
|
226
|
+
status="APPROVED",
|
|
227
|
+
confidence=1.0,
|
|
228
|
+
risk_score=0.05,
|
|
229
|
+
epistemic_uncertainty=0.01,
|
|
230
|
+
risk_taxonomy=tax_scores,
|
|
231
|
+
active_risk_categories=[],
|
|
232
|
+
cycles_detected=[],
|
|
233
|
+
invariant_violations=[],
|
|
234
|
+
linearized_subgraph="[BATCH] No files to verify.\n[GATE]\nSTATUS: APPROVED",
|
|
235
|
+
affected_symbols=[],
|
|
236
|
+
latency_ms=0.0,
|
|
237
|
+
is_neural_calibrated=False,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Stage 1: Diff Boundary Locator for each target file
|
|
241
|
+
patch_results: Dict[str, PatchResult] = {}
|
|
242
|
+
syntax_errors: List[str] = []
|
|
243
|
+
|
|
244
|
+
for item in file_patches:
|
|
245
|
+
raw_path = item["file_path"]
|
|
246
|
+
patch_content = item["patch_content"]
|
|
247
|
+
orig_content = item.get("original_content")
|
|
248
|
+
|
|
249
|
+
p = Path(raw_path)
|
|
250
|
+
if p.is_absolute():
|
|
251
|
+
try:
|
|
252
|
+
norm_path = str(p.resolve().relative_to(self.workspace_root.resolve())).replace("\\", "/")
|
|
253
|
+
except ValueError:
|
|
254
|
+
norm_path = str(raw_path).replace("\\", "/")
|
|
255
|
+
else:
|
|
256
|
+
full_p = (self.workspace_root / raw_path).resolve()
|
|
257
|
+
try:
|
|
258
|
+
norm_path = str(full_p.relative_to(self.workspace_root.resolve())).replace("\\", "/")
|
|
259
|
+
except ValueError:
|
|
260
|
+
norm_path = str(raw_path).replace("\\", "/")
|
|
261
|
+
|
|
262
|
+
pr = locate_affected_symbols(
|
|
263
|
+
file_path=norm_path,
|
|
264
|
+
patch_content=patch_content,
|
|
265
|
+
workspace_root=self.workspace_root,
|
|
266
|
+
original_content=orig_content,
|
|
267
|
+
is_replacement=item.get("is_replacement", True),
|
|
268
|
+
)
|
|
269
|
+
if pr.syntax_error:
|
|
270
|
+
syntax_errors.append(f"SYNTAX_ERROR in '{norm_path}': {pr.syntax_error}")
|
|
271
|
+
patch_results[norm_path] = pr
|
|
272
|
+
|
|
273
|
+
# Immediate exit on syntax error in any file
|
|
274
|
+
if syntax_errors:
|
|
275
|
+
elapsed_ms = (time.perf_counter() - start_time) * 1000.0
|
|
276
|
+
tax_scores = RiskTaxonomyScores(breaking_public_api=0.95, silent_logic_drift=0.90)
|
|
277
|
+
return EnhancedVerificationReport(
|
|
278
|
+
status="REJECTED",
|
|
279
|
+
confidence=1.0,
|
|
280
|
+
risk_score=1.0,
|
|
281
|
+
epistemic_uncertainty=0.01,
|
|
282
|
+
risk_taxonomy=tax_scores,
|
|
283
|
+
active_risk_categories=tax_scores.active_categories(threshold=taxonomy_threshold),
|
|
284
|
+
cycles_detected=[],
|
|
285
|
+
invariant_violations=syntax_errors,
|
|
286
|
+
linearized_subgraph="[BATCH_DIFF] SYNTAX_ERROR\n[GATE]\nSTATUS: REJECTED\nVIOLATIONS:\n"
|
|
287
|
+
+ "\n".join(f" - {err}" for err in syntax_errors),
|
|
288
|
+
affected_symbols=[],
|
|
289
|
+
latency_ms=elapsed_ms,
|
|
290
|
+
is_neural_calibrated=False,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Stage 2: Workspace Indexer scan
|
|
294
|
+
self.indexer.scan_workspace()
|
|
295
|
+
|
|
296
|
+
# Normalize dirty_overlays keys
|
|
297
|
+
norm_dirty: Dict[str, str] = {}
|
|
298
|
+
if dirty_overlays:
|
|
299
|
+
for uf, ucontent in dirty_overlays.items():
|
|
300
|
+
p = Path(uf)
|
|
301
|
+
if p.is_absolute():
|
|
302
|
+
try:
|
|
303
|
+
n_uf = str(p.resolve().relative_to(self.workspace_root.resolve())).replace("\\", "/")
|
|
304
|
+
except ValueError:
|
|
305
|
+
n_uf = str(uf).replace("\\", "/")
|
|
306
|
+
else:
|
|
307
|
+
full_p = (self.workspace_root / uf).resolve()
|
|
308
|
+
try:
|
|
309
|
+
n_uf = str(full_p.relative_to(self.workspace_root.resolve())).replace("\\", "/")
|
|
310
|
+
except ValueError:
|
|
311
|
+
n_uf = str(uf).replace("\\", "/")
|
|
312
|
+
norm_dirty[n_uf] = ucontent
|
|
313
|
+
|
|
314
|
+
# Save existing file cache entries for clean rollback
|
|
315
|
+
all_touched_paths = set(patch_results.keys()) | set(norm_dirty.keys())
|
|
316
|
+
cached_backups = {
|
|
317
|
+
path: (
|
|
318
|
+
copy.deepcopy(self.indexer._file_cache.get(path)),
|
|
319
|
+
list(self.indexer._file_symbols.get(path, [])),
|
|
320
|
+
list(self.indexer._file_imports.get(path, [])),
|
|
321
|
+
)
|
|
322
|
+
for path in all_touched_paths
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
# Overlay unstaged dirty files (with their git index state)
|
|
327
|
+
for uf_path, uf_content in norm_dirty.items():
|
|
328
|
+
if uf_path in patch_results:
|
|
329
|
+
continue
|
|
330
|
+
syms = extract_symbols_from_ast(uf_content, file_path=uf_path)
|
|
331
|
+
imps = extract_imports_from_ast(uf_content, file_path=uf_path)
|
|
332
|
+
self.indexer.overlay_transient_symbols(uf_path, syms, imports=imps)
|
|
333
|
+
|
|
334
|
+
# Atomic Batch Overlay: overlay all staged symbols into indexer
|
|
335
|
+
for norm_path, pr in patch_results.items():
|
|
336
|
+
if pr.all_patched_symbols or pr.deleted_symbols or pr.imports:
|
|
337
|
+
self.indexer.overlay_transient_symbols(
|
|
338
|
+
norm_path,
|
|
339
|
+
pr.all_patched_symbols,
|
|
340
|
+
imports=pr.imports,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Slicing & Symbolic Gate across all staged files
|
|
344
|
+
all_violations: List[str] = []
|
|
345
|
+
all_cycles: List[List[str]] = []
|
|
346
|
+
all_affected_symbols: List[str] = []
|
|
347
|
+
seen_violations: Set[str] = set()
|
|
348
|
+
|
|
349
|
+
for norm_path, pr in patch_results.items():
|
|
350
|
+
all_seeds = pr.affected_symbols + pr.added_symbols + pr.deleted_symbols
|
|
351
|
+
seen_ids = set()
|
|
352
|
+
seed_symbols = []
|
|
353
|
+
for s in all_seeds:
|
|
354
|
+
if s.id not in seen_ids:
|
|
355
|
+
seen_ids.add(s.id)
|
|
356
|
+
seed_symbols.append(s)
|
|
357
|
+
|
|
358
|
+
all_affected_symbols.extend([s.qualname for s in seed_symbols])
|
|
359
|
+
|
|
360
|
+
slice_graph = slice_neighborhood(
|
|
361
|
+
seeds=seed_symbols,
|
|
362
|
+
indexer=self.indexer,
|
|
363
|
+
k=k,
|
|
364
|
+
max_fanout=max_fanout,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
gate_result = verify_symbolic_gate(
|
|
368
|
+
patch_result=pr,
|
|
369
|
+
slice_graph=slice_graph,
|
|
370
|
+
indexer=self.indexer,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
for v in gate_result.violations:
|
|
374
|
+
if v not in seen_violations:
|
|
375
|
+
seen_violations.add(v)
|
|
376
|
+
all_violations.append(v)
|
|
377
|
+
|
|
378
|
+
for c in gate_result.cycles:
|
|
379
|
+
if c not in all_cycles:
|
|
380
|
+
all_cycles.append(c)
|
|
381
|
+
|
|
382
|
+
elapsed_ms = (time.perf_counter() - start_time) * 1000.0
|
|
383
|
+
|
|
384
|
+
if all_violations or all_cycles:
|
|
385
|
+
status = "REJECTED"
|
|
386
|
+
confidence = 0.95
|
|
387
|
+
else:
|
|
388
|
+
status = "APPROVED"
|
|
389
|
+
confidence = 0.98
|
|
390
|
+
|
|
391
|
+
dsl = f"[BATCH_VERIFIED] {len(patch_results)} staged files.\n[GATE]\nSTATUS: {status}"
|
|
392
|
+
if all_violations:
|
|
393
|
+
dsl += "\nVIOLATIONS:\n" + "\n".join(f" - {v}" for v in all_violations)
|
|
394
|
+
|
|
395
|
+
# Stage 6: Decision Head / Risk Calibration & Multi-Task Taxonomy
|
|
396
|
+
decision_res = self.decision_head.predict_multi_task(
|
|
397
|
+
linearized_dsl=dsl,
|
|
398
|
+
symbolic_status=status,
|
|
399
|
+
symbolic_confidence=confidence,
|
|
400
|
+
has_violations=bool(all_violations or all_cycles),
|
|
401
|
+
violations=all_violations,
|
|
402
|
+
cycles=all_cycles,
|
|
403
|
+
taxonomy_threshold=taxonomy_threshold,
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
return EnhancedVerificationReport(
|
|
407
|
+
status=decision_res.status,
|
|
408
|
+
confidence=decision_res.confidence,
|
|
409
|
+
risk_score=decision_res.risk_score,
|
|
410
|
+
epistemic_uncertainty=decision_res.epistemic_uncertainty,
|
|
411
|
+
risk_taxonomy=decision_res.risk_taxonomy,
|
|
412
|
+
active_risk_categories=decision_res.active_risk_categories,
|
|
413
|
+
cycles_detected=all_cycles,
|
|
414
|
+
invariant_violations=all_violations,
|
|
415
|
+
linearized_subgraph=dsl,
|
|
416
|
+
affected_symbols=list(dict.fromkeys(all_affected_symbols)),
|
|
417
|
+
latency_ms=elapsed_ms,
|
|
418
|
+
is_neural_calibrated=decision_res.is_neural_calibrated,
|
|
419
|
+
engine_mode=decision_res.engine_mode,
|
|
420
|
+
)
|
|
421
|
+
finally:
|
|
422
|
+
# Restore indexer to default disk state (Rollback Resilience)
|
|
423
|
+
for path, (cached_backup, backup_syms, backup_imps) in cached_backups.items():
|
|
424
|
+
self.indexer.restore_transient_symbols(
|
|
425
|
+
path,
|
|
426
|
+
backup_syms,
|
|
427
|
+
backup_imps,
|
|
428
|
+
cached_backup,
|
|
429
|
+
)
|
|
430
|
+
|