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.
Files changed (40) hide show
  1. code_oracle/__init__.py +30 -0
  2. code_oracle/cli.py +795 -0
  3. code_oracle/config.py +145 -0
  4. code_oracle/dataset.py +5325 -0
  5. code_oracle/dead_code/__init__.py +32 -0
  6. code_oracle/dead_code/detector.py +379 -0
  7. code_oracle/dead_code/entrypoints.py +333 -0
  8. code_oracle/dead_code/models.py +255 -0
  9. code_oracle/dead_code/semantics.py +416 -0
  10. code_oracle/decision.py +906 -0
  11. code_oracle/engine.py +430 -0
  12. code_oracle/export_onnx.py +436 -0
  13. code_oracle/hook.py +531 -0
  14. code_oracle/indexer.py +894 -0
  15. code_oracle/languages/__init__.py +114 -0
  16. code_oracle/languages/common.py +127 -0
  17. code_oracle/languages/go.py +395 -0
  18. code_oracle/languages/python.py +336 -0
  19. code_oracle/languages/rust.py +474 -0
  20. code_oracle/languages/typescript.py +775 -0
  21. code_oracle/linearizer.py +166 -0
  22. code_oracle/locator.py +301 -0
  23. code_oracle/models.py +237 -0
  24. code_oracle/perf_lint/__init__.py +38 -0
  25. code_oracle/perf_lint/engine.py +234 -0
  26. code_oracle/perf_lint/models.py +229 -0
  27. code_oracle/perf_lint/rules/__init__.py +31 -0
  28. code_oracle/perf_lint/rules/async_blocking.py +143 -0
  29. code_oracle/perf_lint/rules/n_plus_one.py +232 -0
  30. code_oracle/perf_lint/rules/nested_loops.py +137 -0
  31. code_oracle/perf_lint/rules/unclosed_res.py +494 -0
  32. code_oracle/perf_lint/visitor.py +299 -0
  33. code_oracle/server.py +184 -0
  34. code_oracle/slicer.py +225 -0
  35. code_oracle/symbolic.py +459 -0
  36. code_oracle-0.1.0.dist-info/METADATA +225 -0
  37. code_oracle-0.1.0.dist-info/RECORD +40 -0
  38. code_oracle-0.1.0.dist-info/WHEEL +4 -0
  39. code_oracle-0.1.0.dist-info/entry_points.txt +2 -0
  40. 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
+