llm-sca-tooling 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 (450) hide show
  1. llm_sca_tooling/__init__.py +5 -0
  2. llm_sca_tooling/blast_radius/__init__.py +42 -0
  3. llm_sca_tooling/blast_radius/abi_impact.py +74 -0
  4. llm_sca_tooling/blast_radius/ambiguous_links.py +46 -0
  5. llm_sca_tooling/blast_radius/change_type.py +99 -0
  6. llm_sca_tooling/blast_radius/cross_repo.py +117 -0
  7. llm_sca_tooling/blast_radius/doc_spec_impact.py +70 -0
  8. llm_sca_tooling/blast_radius/generated_stub.py +60 -0
  9. llm_sca_tooling/blast_radius/graph_traversal.py +263 -0
  10. llm_sca_tooling/blast_radius/impact_groups.py +33 -0
  11. llm_sca_tooling/blast_radius/models.py +152 -0
  12. llm_sca_tooling/blast_radius/report.py +125 -0
  13. llm_sca_tooling/blast_radius/sarif_reachability.py +59 -0
  14. llm_sca_tooling/blast_radius/service.py +222 -0
  15. llm_sca_tooling/blast_radius/traversal_policy.py +192 -0
  16. llm_sca_tooling/cli/__init__.py +1 -0
  17. llm_sca_tooling/cli/indexing.py +67 -0
  18. llm_sca_tooling/cli/main.py +594 -0
  19. llm_sca_tooling/cli/setup_cmd.py +310 -0
  20. llm_sca_tooling/config.py +212 -0
  21. llm_sca_tooling/errors.py +31 -0
  22. llm_sca_tooling/evaluation/__init__.py +75 -0
  23. llm_sca_tooling/evaluation/ai_readiness.py +116 -0
  24. llm_sca_tooling/evaluation/artefact_writer.py +110 -0
  25. llm_sca_tooling/evaluation/benchmark_adapter.py +122 -0
  26. llm_sca_tooling/evaluation/contamination.py +22 -0
  27. llm_sca_tooling/evaluation/fl_metrics.py +118 -0
  28. llm_sca_tooling/evaluation/flaky_detector.py +43 -0
  29. llm_sca_tooling/evaluation/harness_condition.py +115 -0
  30. llm_sca_tooling/evaluation/maintainability_oracle.py +61 -0
  31. llm_sca_tooling/evaluation/models.py +244 -0
  32. llm_sca_tooling/evaluation/operational_metrics.py +66 -0
  33. llm_sca_tooling/evaluation/perturbation_runner.py +12 -0
  34. llm_sca_tooling/evaluation/rds_features.py +105 -0
  35. llm_sca_tooling/evaluation/regression_adapter.py +54 -0
  36. llm_sca_tooling/evaluation/replay.py +18 -0
  37. llm_sca_tooling/evaluation/smoke_adapter.py +177 -0
  38. llm_sca_tooling/evaluation/store.py +69 -0
  39. llm_sca_tooling/evaluation/t1_runner.py +213 -0
  40. llm_sca_tooling/evaluation/t2_runner.py +18 -0
  41. llm_sca_tooling/evaluation/t3_runner.py +99 -0
  42. llm_sca_tooling/evaluation/t4_runner.py +93 -0
  43. llm_sca_tooling/fl/__init__.py +28 -0
  44. llm_sca_tooling/fl/blame_prior.py +155 -0
  45. llm_sca_tooling/fl/context_assembler.py +216 -0
  46. llm_sca_tooling/fl/embedding_adapters/__init__.py +5 -0
  47. llm_sca_tooling/fl/embedding_adapters/local_adapter.py +60 -0
  48. llm_sca_tooling/fl/embedding_adapters/null_adapter.py +26 -0
  49. llm_sca_tooling/fl/embedding_adapters/openai_adapter.py +27 -0
  50. llm_sca_tooling/fl/embedding_interface.py +100 -0
  51. llm_sca_tooling/fl/graph_expansion.py +139 -0
  52. llm_sca_tooling/fl/investigate.py +167 -0
  53. llm_sca_tooling/fl/issue.py +355 -0
  54. llm_sca_tooling/fl/keyword_retrieval.py +324 -0
  55. llm_sca_tooling/fl/localisation.py +318 -0
  56. llm_sca_tooling/fl/memory_stub.py +42 -0
  57. llm_sca_tooling/fl/models.py +172 -0
  58. llm_sca_tooling/fl/ranking.py +157 -0
  59. llm_sca_tooling/fl/reasoning.py +97 -0
  60. llm_sca_tooling/fl/sarif_prior.py +182 -0
  61. llm_sca_tooling/fl/sbfl.py +214 -0
  62. llm_sca_tooling/fl/uncertainty.py +88 -0
  63. llm_sca_tooling/fl/vector_cache.py +215 -0
  64. llm_sca_tooling/governance/__init__.py +14 -0
  65. llm_sca_tooling/governance/permissions.py +73 -0
  66. llm_sca_tooling/governance/policy.py +97 -0
  67. llm_sca_tooling/graph/__init__.py +1 -0
  68. llm_sca_tooling/hardening/__init__.py +21 -0
  69. llm_sca_tooling/hardening/cache_invalidation.py +55 -0
  70. llm_sca_tooling/hardening/cumulative_risk.py +84 -0
  71. llm_sca_tooling/hardening/file_watcher.py +22 -0
  72. llm_sca_tooling/hardening/git_hooks.py +48 -0
  73. llm_sca_tooling/hardening/graph_chunker.py +37 -0
  74. llm_sca_tooling/hardening/harness_drift.py +78 -0
  75. llm_sca_tooling/hardening/manifest_regression_runner.py +28 -0
  76. llm_sca_tooling/hardening/models.py +155 -0
  77. llm_sca_tooling/hardening/permission_profiles.py +78 -0
  78. llm_sca_tooling/hardening/subscription_recovery.py +29 -0
  79. llm_sca_tooling/hardening/task_authorization.py +28 -0
  80. llm_sca_tooling/hardening/trace_redaction_audit.py +17 -0
  81. llm_sca_tooling/harness/__init__.py +5 -0
  82. llm_sca_tooling/harness/condition.py +169 -0
  83. llm_sca_tooling/indexing/__init__.py +15 -0
  84. llm_sca_tooling/indexing/backends/__init__.py +1 -0
  85. llm_sca_tooling/indexing/backends/base.py +104 -0
  86. llm_sca_tooling/indexing/backends/capability.py +9 -0
  87. llm_sca_tooling/indexing/backends/cpp/__init__.py +5 -0
  88. llm_sca_tooling/indexing/backends/cpp/abi_edge_builder.py +23 -0
  89. llm_sca_tooling/indexing/backends/cpp/clangd_adapter.py +238 -0
  90. llm_sca_tooling/indexing/backends/cpp/cmake_backend.py +26 -0
  91. llm_sca_tooling/indexing/backends/cpp/compile_commands.py +55 -0
  92. llm_sca_tooling/indexing/backends/cpp/cpp_backend.py +131 -0
  93. llm_sca_tooling/indexing/backends/cpp/ctest_detection.py +30 -0
  94. llm_sca_tooling/indexing/backends/cpp/libclang_adapter.py +272 -0
  95. llm_sca_tooling/indexing/backends/cross_check.py +77 -0
  96. llm_sca_tooling/indexing/backends/ctags.py +166 -0
  97. llm_sca_tooling/indexing/backends/fact_reconciler.py +109 -0
  98. llm_sca_tooling/indexing/backends/java/__init__.py +5 -0
  99. llm_sca_tooling/indexing/backends/java/capability.py +3 -0
  100. llm_sca_tooling/indexing/backends/java/java_backend.py +63 -0
  101. llm_sca_tooling/indexing/backends/java/jdt_adapter.py +17 -0
  102. llm_sca_tooling/indexing/backends/python/__init__.py +5 -0
  103. llm_sca_tooling/indexing/backends/python/import_resolver.py +14 -0
  104. llm_sca_tooling/indexing/backends/python/pyan3_adapter.py +172 -0
  105. llm_sca_tooling/indexing/backends/python/pyright_adapter.py +236 -0
  106. llm_sca_tooling/indexing/backends/python/python_backend.py +120 -0
  107. llm_sca_tooling/indexing/backends/python_ast.py +535 -0
  108. llm_sca_tooling/indexing/backends/registry.py +43 -0
  109. llm_sca_tooling/indexing/backends/tree_sitter.py +60 -0
  110. llm_sca_tooling/indexing/backends/typescript/__init__.py +5 -0
  111. llm_sca_tooling/indexing/backends/typescript/madge_adapter.py +82 -0
  112. llm_sca_tooling/indexing/backends/typescript/module_resolver.py +123 -0
  113. llm_sca_tooling/indexing/backends/typescript/package_meta.py +27 -0
  114. llm_sca_tooling/indexing/backends/typescript/ts_backend.py +120 -0
  115. llm_sca_tooling/indexing/backends/typescript/ts_test_detection.py +36 -0
  116. llm_sca_tooling/indexing/backends/typescript/tsmorph_adapter.py +282 -0
  117. llm_sca_tooling/indexing/backends/utils.py +118 -0
  118. llm_sca_tooling/indexing/blame.py +158 -0
  119. llm_sca_tooling/indexing/build_evidence.py +216 -0
  120. llm_sca_tooling/indexing/config.py +22 -0
  121. llm_sca_tooling/indexing/diagnostics.py +65 -0
  122. llm_sca_tooling/indexing/git_metadata.py +137 -0
  123. llm_sca_tooling/indexing/graph_slices.py +44 -0
  124. llm_sca_tooling/indexing/hashing.py +18 -0
  125. llm_sca_tooling/indexing/ignore.py +108 -0
  126. llm_sca_tooling/indexing/lsp/__init__.py +6 -0
  127. llm_sca_tooling/indexing/lsp/capabilities.py +27 -0
  128. llm_sca_tooling/indexing/lsp/client.py +90 -0
  129. llm_sca_tooling/indexing/lsp/errors.py +13 -0
  130. llm_sca_tooling/indexing/lsp/lifecycle.py +38 -0
  131. llm_sca_tooling/indexing/lsp/protocol.py +17 -0
  132. llm_sca_tooling/indexing/lsp/request_dispatcher.py +96 -0
  133. llm_sca_tooling/indexing/manifests.py +100 -0
  134. llm_sca_tooling/indexing/pipeline.py +5 -0
  135. llm_sca_tooling/indexing/provenance.py +11 -0
  136. llm_sca_tooling/indexing/result.py +47 -0
  137. llm_sca_tooling/indexing/scanner.py +321 -0
  138. llm_sca_tooling/indexing/service.py +696 -0
  139. llm_sca_tooling/indexing/snapshots.py +31 -0
  140. llm_sca_tooling/indexing/summaries.py +82 -0
  141. llm_sca_tooling/mcp_server/__init__.py +6 -0
  142. llm_sca_tooling/mcp_server/capabilities.py +42 -0
  143. llm_sca_tooling/mcp_server/config.py +36 -0
  144. llm_sca_tooling/mcp_server/context.py +22 -0
  145. llm_sca_tooling/mcp_server/dev_server.py +60 -0
  146. llm_sca_tooling/mcp_server/errors.py +86 -0
  147. llm_sca_tooling/mcp_server/notifications.py +47 -0
  148. llm_sca_tooling/mcp_server/prompt_registry.py +181 -0
  149. llm_sca_tooling/mcp_server/prompts/bug_resolve.md +67 -0
  150. llm_sca_tooling/mcp_server/prompts/evaluate.md +10 -0
  151. llm_sca_tooling/mcp_server/prompts/implementation_check.md +56 -0
  152. llm_sca_tooling/mcp_server/prompts/investigate.md +27 -0
  153. llm_sca_tooling/mcp_server/prompts/operational_review.md +18 -0
  154. llm_sca_tooling/mcp_server/prompts/patch_review.md +5 -0
  155. llm_sca_tooling/mcp_server/prompts/readiness_audit.md +17 -0
  156. llm_sca_tooling/mcp_server/prompts.py +12 -0
  157. llm_sca_tooling/mcp_server/resource_registry.py +101 -0
  158. llm_sca_tooling/mcp_server/resource_uris.py +41 -0
  159. llm_sca_tooling/mcp_server/resources/__init__.py +5 -0
  160. llm_sca_tooling/mcp_server/resources/blame.py +48 -0
  161. llm_sca_tooling/mcp_server/resources/core.py +440 -0
  162. llm_sca_tooling/mcp_server/resources/eval.py +94 -0
  163. llm_sca_tooling/mcp_server/resources/interfaces.py +104 -0
  164. llm_sca_tooling/mcp_server/resources/memory.py +101 -0
  165. llm_sca_tooling/mcp_server/resources/sarif.py +96 -0
  166. llm_sca_tooling/mcp_server/sampling.py +28 -0
  167. llm_sca_tooling/mcp_server/serialization.py +24 -0
  168. llm_sca_tooling/mcp_server/server.py +192 -0
  169. llm_sca_tooling/mcp_server/subscriptions.py +57 -0
  170. llm_sca_tooling/mcp_server/task_ids.py +9 -0
  171. llm_sca_tooling/mcp_server/task_runner.py +115 -0
  172. llm_sca_tooling/mcp_server/task_store.py +204 -0
  173. llm_sca_tooling/mcp_server/tasks.py +57 -0
  174. llm_sca_tooling/mcp_server/telemetry.py +79 -0
  175. llm_sca_tooling/mcp_server/tool_permissions.py +20 -0
  176. llm_sca_tooling/mcp_server/tool_registry.py +72 -0
  177. llm_sca_tooling/mcp_server/tools/__init__.py +5 -0
  178. llm_sca_tooling/mcp_server/tools/blame.py +90 -0
  179. llm_sca_tooling/mcp_server/tools/core.py +519 -0
  180. llm_sca_tooling/mcp_server/tools/eval.py +335 -0
  181. llm_sca_tooling/mcp_server/tools/fl.py +142 -0
  182. llm_sca_tooling/mcp_server/tools/impl_check.py +120 -0
  183. llm_sca_tooling/mcp_server/tools/interface.py +297 -0
  184. llm_sca_tooling/mcp_server/tools/issue_resolution.py +153 -0
  185. llm_sca_tooling/mcp_server/tools/memory.py +398 -0
  186. llm_sca_tooling/mcp_server/tools/operational_review.py +89 -0
  187. llm_sca_tooling/mcp_server/tools/patch_review.py +300 -0
  188. llm_sca_tooling/mcp_server/tools/qa.py +178 -0
  189. llm_sca_tooling/mcp_server/tools/readiness_audit.py +88 -0
  190. llm_sca_tooling/mcp_server/tools/sarif.py +157 -0
  191. llm_sca_tooling/mcp_server/tools/sast_repair.py +340 -0
  192. llm_sca_tooling/mcp_server/tools/traces.py +202 -0
  193. llm_sca_tooling/memory/__init__.py +31 -0
  194. llm_sca_tooling/memory/errors.py +19 -0
  195. llm_sca_tooling/memory/eviction/__init__.py +5 -0
  196. llm_sca_tooling/memory/eviction/compactor.py +140 -0
  197. llm_sca_tooling/memory/models.py +286 -0
  198. llm_sca_tooling/memory/promotion/__init__.py +5 -0
  199. llm_sca_tooling/memory/promotion/pipeline.py +77 -0
  200. llm_sca_tooling/memory/redaction.py +46 -0
  201. llm_sca_tooling/memory/relabelling/__init__.py +6 -0
  202. llm_sca_tooling/memory/relabelling/interface.py +18 -0
  203. llm_sca_tooling/memory/relabelling/null_relabeller.py +32 -0
  204. llm_sca_tooling/memory/retrieval/__init__.py +13 -0
  205. llm_sca_tooling/memory/retrieval/coarse.py +48 -0
  206. llm_sca_tooling/memory/retrieval/fine.py +65 -0
  207. llm_sca_tooling/memory/retrieval/interface.py +27 -0
  208. llm_sca_tooling/memory/retrieval/misalignment_guard.py +33 -0
  209. llm_sca_tooling/memory/ship_gate.py +31 -0
  210. llm_sca_tooling/memory/store.py +236 -0
  211. llm_sca_tooling/memory/write_path.py +97 -0
  212. llm_sca_tooling/operations/__init__.py +29 -0
  213. llm_sca_tooling/operations/budget.py +88 -0
  214. llm_sca_tooling/operations/ledger_delete.py +112 -0
  215. llm_sca_tooling/operations/ledger_export.py +84 -0
  216. llm_sca_tooling/operations/ledger_retention.py +37 -0
  217. llm_sca_tooling/operations/run_records.py +175 -0
  218. llm_sca_tooling/patch_review/__init__.py +46 -0
  219. llm_sca_tooling/patch_review/ast_diff.py +142 -0
  220. llm_sca_tooling/patch_review/diff_parser.py +129 -0
  221. llm_sca_tooling/patch_review/dryrun.py +189 -0
  222. llm_sca_tooling/patch_review/four_agent_audit.py +56 -0
  223. llm_sca_tooling/patch_review/graph_context.py +101 -0
  224. llm_sca_tooling/patch_review/interface_compat.py +109 -0
  225. llm_sca_tooling/patch_review/maintainability_gate.py +53 -0
  226. llm_sca_tooling/patch_review/merge_policy.py +48 -0
  227. llm_sca_tooling/patch_review/models.py +369 -0
  228. llm_sca_tooling/patch_review/operational_integration.py +74 -0
  229. llm_sca_tooling/patch_review/report.py +337 -0
  230. llm_sca_tooling/patch_review/risk_classifier.py +122 -0
  231. llm_sca_tooling/patch_review/risk_features.py +55 -0
  232. llm_sca_tooling/patch_review/risk_policy.py +158 -0
  233. llm_sca_tooling/patch_review/sampling_integration.py +100 -0
  234. llm_sca_tooling/patch_review/sarif_delta.py +99 -0
  235. llm_sca_tooling/patch_review/scope_audit.py +113 -0
  236. llm_sca_tooling/patch_review/symbol_detector.py +138 -0
  237. llm_sca_tooling/patch_review/test_delta.py +97 -0
  238. llm_sca_tooling/plugins/__init__.py +9 -0
  239. llm_sca_tooling/plugins/backlog/__init__.py +128 -0
  240. llm_sca_tooling/plugins/backlog/dbus_stub.py +3 -0
  241. llm_sca_tooling/plugins/backlog/grpc_stub.py +3 -0
  242. llm_sca_tooling/plugins/backlog/mqtt_stub.py +3 -0
  243. llm_sca_tooling/plugins/backlog/protobuf_stub.py +3 -0
  244. llm_sca_tooling/plugins/backlog/zeromq_stub.py +3 -0
  245. llm_sca_tooling/plugins/base.py +165 -0
  246. llm_sca_tooling/plugins/capability.py +77 -0
  247. llm_sca_tooling/plugins/errors.py +19 -0
  248. llm_sca_tooling/plugins/graph_utils.py +190 -0
  249. llm_sca_tooling/plugins/http_rest/__init__.py +5 -0
  250. llm_sca_tooling/plugins/http_rest/client_detector.py +41 -0
  251. llm_sca_tooling/plugins/http_rest/django_detector.py +28 -0
  252. llm_sca_tooling/plugins/http_rest/fastapi_detector.py +13 -0
  253. llm_sca_tooling/plugins/http_rest/flask_detector.py +13 -0
  254. llm_sca_tooling/plugins/http_rest/framework_detector.py +97 -0
  255. llm_sca_tooling/plugins/http_rest/openapi_parser.py +131 -0
  256. llm_sca_tooling/plugins/http_rest/plugin.py +395 -0
  257. llm_sca_tooling/plugins/http_rest/schema_extractor.py +12 -0
  258. llm_sca_tooling/plugins/http_rest/url_normalizer.py +57 -0
  259. llm_sca_tooling/plugins/interface_record.py +105 -0
  260. llm_sca_tooling/plugins/omniorb_idl/__init__.py +5 -0
  261. llm_sca_tooling/plugins/omniorb_idl/caller_finder.py +29 -0
  262. llm_sca_tooling/plugins/omniorb_idl/cpp_servant_linker.py +36 -0
  263. llm_sca_tooling/plugins/omniorb_idl/generated_artifact_tracker.py +19 -0
  264. llm_sca_tooling/plugins/omniorb_idl/idl_parser.py +39 -0
  265. llm_sca_tooling/plugins/omniorb_idl/plugin.py +329 -0
  266. llm_sca_tooling/plugins/omniorb_idl/python_stub_linker.py +26 -0
  267. llm_sca_tooling/plugins/registry.py +172 -0
  268. llm_sca_tooling/plugins/runner.py +76 -0
  269. llm_sca_tooling/plugins/store.py +57 -0
  270. llm_sca_tooling/plugins/traversal.py +155 -0
  271. llm_sca_tooling/plugins/traverse_edges.py +109 -0
  272. llm_sca_tooling/plugins/websocket/__init__.py +5 -0
  273. llm_sca_tooling/plugins/websocket/client_detector.py +33 -0
  274. llm_sca_tooling/plugins/websocket/event_extractor.py +21 -0
  275. llm_sca_tooling/plugins/websocket/namespace_resolver.py +16 -0
  276. llm_sca_tooling/plugins/websocket/plugin.py +321 -0
  277. llm_sca_tooling/plugins/websocket/server_detector.py +71 -0
  278. llm_sca_tooling/privacy/__init__.py +35 -0
  279. llm_sca_tooling/privacy/export_delete.py +69 -0
  280. llm_sca_tooling/privacy/redaction.py +69 -0
  281. llm_sca_tooling/privacy/retention_policy.py +95 -0
  282. llm_sca_tooling/qa/__init__.py +14 -0
  283. llm_sca_tooling/qa/answer.py +54 -0
  284. llm_sca_tooling/qa/behaviour_trace.py +204 -0
  285. llm_sca_tooling/qa/blame.py +265 -0
  286. llm_sca_tooling/qa/classifier.py +148 -0
  287. llm_sca_tooling/qa/confidence.py +63 -0
  288. llm_sca_tooling/qa/evidence_assembler.py +144 -0
  289. llm_sca_tooling/qa/graph_query.py +183 -0
  290. llm_sca_tooling/qa/interface_lookup.py +137 -0
  291. llm_sca_tooling/qa/lookup.py +386 -0
  292. llm_sca_tooling/qa/question.py +120 -0
  293. llm_sca_tooling/qa/service.py +282 -0
  294. llm_sca_tooling/qa/ship_gate.py +89 -0
  295. llm_sca_tooling/qa/synthesis.py +90 -0
  296. llm_sca_tooling/release/__init__.py +27 -0
  297. llm_sca_tooling/release/ablation.py +43 -0
  298. llm_sca_tooling/release/adversarial.py +34 -0
  299. llm_sca_tooling/release/calibration.py +101 -0
  300. llm_sca_tooling/release/models.py +171 -0
  301. llm_sca_tooling/release/operational_gates.py +71 -0
  302. llm_sca_tooling/release/production_refresh.py +42 -0
  303. llm_sca_tooling/release/release_gate.py +72 -0
  304. llm_sca_tooling/release/report_templates.py +41 -0
  305. llm_sca_tooling/sarif/__init__.py +31 -0
  306. llm_sca_tooling/sarif/adapters/__init__.py +20 -0
  307. llm_sca_tooling/sarif/adapters/bandit.py +160 -0
  308. llm_sca_tooling/sarif/adapters/base.py +41 -0
  309. llm_sca_tooling/sarif/adapters/codeql.py +46 -0
  310. llm_sca_tooling/sarif/adapters/external_import.py +52 -0
  311. llm_sca_tooling/sarif/adapters/ruleset.py +44 -0
  312. llm_sca_tooling/sarif/adapters/semgrep.py +100 -0
  313. llm_sca_tooling/sarif/adapters/sonarqube.py +71 -0
  314. llm_sca_tooling/sarif/binding.py +160 -0
  315. llm_sca_tooling/sarif/delta.py +110 -0
  316. llm_sca_tooling/sarif/errors.py +19 -0
  317. llm_sca_tooling/sarif/fingerprint.py +44 -0
  318. llm_sca_tooling/sarif/models.py +363 -0
  319. llm_sca_tooling/sarif/normalizer.py +485 -0
  320. llm_sca_tooling/sarif/parser.py +392 -0
  321. llm_sca_tooling/sarif/pipeline.py +135 -0
  322. llm_sca_tooling/sarif/resource.py +66 -0
  323. llm_sca_tooling/sarif/rulesets/__init__.py +0 -0
  324. llm_sca_tooling/sarif/rulesets/cpp_security.yaml +11 -0
  325. llm_sca_tooling/sarif/rulesets/python_security.yaml +20 -0
  326. llm_sca_tooling/sarif/rulesets/typescript_security.yaml +10 -0
  327. llm_sca_tooling/sarif/store.py +270 -0
  328. llm_sca_tooling/sarif/warned_by.py +190 -0
  329. llm_sca_tooling/sast_repair/__init__.py +91 -0
  330. llm_sca_tooling/sast_repair/alert_binding.py +122 -0
  331. llm_sca_tooling/sast_repair/alert_classification.py +92 -0
  332. llm_sca_tooling/sast_repair/analyser_rerun.py +89 -0
  333. llm_sca_tooling/sast_repair/build_test_runner.py +91 -0
  334. llm_sca_tooling/sast_repair/corpus_adapter.py +149 -0
  335. llm_sca_tooling/sast_repair/models.py +262 -0
  336. llm_sca_tooling/sast_repair/patch_generator.py +59 -0
  337. llm_sca_tooling/sast_repair/predicate_examples.py +64 -0
  338. llm_sca_tooling/sast_repair/predicate_metadata.py +132 -0
  339. llm_sca_tooling/sast_repair/remaining_risk.py +115 -0
  340. llm_sca_tooling/sast_repair/repair_context.py +101 -0
  341. llm_sca_tooling/sast_repair/report.py +286 -0
  342. llm_sca_tooling/sast_repair/rule_evolution.py +33 -0
  343. llm_sca_tooling/sast_repair/sandbox.py +134 -0
  344. llm_sca_tooling/sast_repair/sarif_delta_verifier.py +95 -0
  345. llm_sca_tooling/sast_repair/suppression.py +68 -0
  346. llm_sca_tooling/schemas/__init__.py +5 -0
  347. llm_sca_tooling/schemas/base.py +88 -0
  348. llm_sca_tooling/schemas/contracts.py +53 -0
  349. llm_sca_tooling/schemas/enums.py +254 -0
  350. llm_sca_tooling/schemas/evidence.py +69 -0
  351. llm_sca_tooling/schemas/governance.py +201 -0
  352. llm_sca_tooling/schemas/graph.py +292 -0
  353. llm_sca_tooling/schemas/harness.py +62 -0
  354. llm_sca_tooling/schemas/incidents.py +108 -0
  355. llm_sca_tooling/schemas/json_schema.py +100 -0
  356. llm_sca_tooling/schemas/memory.py +36 -0
  357. llm_sca_tooling/schemas/operations.py +190 -0
  358. llm_sca_tooling/schemas/patches.py +64 -0
  359. llm_sca_tooling/schemas/provenance.py +173 -0
  360. llm_sca_tooling/schemas/readiness.py +122 -0
  361. llm_sca_tooling/schemas/run_records.py +154 -0
  362. llm_sca_tooling/schemas/sarif.py +39 -0
  363. llm_sca_tooling/schemas/supply_chain.py +37 -0
  364. llm_sca_tooling/schemas/validation.py +90 -0
  365. llm_sca_tooling/schemas/verdicts.py +81 -0
  366. llm_sca_tooling/storage/__init__.py +9 -0
  367. llm_sca_tooling/storage/artifacts.py +140 -0
  368. llm_sca_tooling/storage/diagnostics.py +14 -0
  369. llm_sca_tooling/storage/errors.py +57 -0
  370. llm_sca_tooling/storage/export_import.py +382 -0
  371. llm_sca_tooling/storage/graph_queries.py +30 -0
  372. llm_sca_tooling/storage/graph_store.py +633 -0
  373. llm_sca_tooling/storage/harness_store.py +150 -0
  374. llm_sca_tooling/storage/ids.py +48 -0
  375. llm_sca_tooling/storage/migrations/0001_initial.sql +297 -0
  376. llm_sca_tooling/storage/migrations/0002_sarif.sql +88 -0
  377. llm_sca_tooling/storage/migrations/0003_embedding_vectors.sql +19 -0
  378. llm_sca_tooling/storage/migrations/0004_eval_runs.sql +14 -0
  379. llm_sca_tooling/storage/migrations/0005_memory.sql +58 -0
  380. llm_sca_tooling/storage/migrations/__init__.py +88 -0
  381. llm_sca_tooling/storage/operations.py +514 -0
  382. llm_sca_tooling/storage/paths.py +74 -0
  383. llm_sca_tooling/storage/registry.py +193 -0
  384. llm_sca_tooling/storage/snapshots.py +197 -0
  385. llm_sca_tooling/storage/sqlite.py +16 -0
  386. llm_sca_tooling/storage/transactions.py +19 -0
  387. llm_sca_tooling/storage/workspace.py +135 -0
  388. llm_sca_tooling/telemetry/__init__.py +6 -0
  389. llm_sca_tooling/telemetry/logging.py +41 -0
  390. llm_sca_tooling/telemetry/trace_writer.py +85 -0
  391. llm_sca_tooling/traces/__init__.py +35 -0
  392. llm_sca_tooling/traces/adapters/__init__.py +16 -0
  393. llm_sca_tooling/traces/adapters/base.py +38 -0
  394. llm_sca_tooling/traces/adapters/cpp_adapter.py +30 -0
  395. llm_sca_tooling/traces/adapters/js_adapter.py +30 -0
  396. llm_sca_tooling/traces/adapters/python_adapter.py +157 -0
  397. llm_sca_tooling/traces/adapters/python_runner.py +243 -0
  398. llm_sca_tooling/traces/adapters/registry.py +32 -0
  399. llm_sca_tooling/traces/artefact_store.py +63 -0
  400. llm_sca_tooling/traces/compression/__init__.py +17 -0
  401. llm_sca_tooling/traces/compression/divergence.py +45 -0
  402. llm_sca_tooling/traces/compression/interface.py +21 -0
  403. llm_sca_tooling/traces/compression/null_summarizer.py +84 -0
  404. llm_sca_tooling/traces/compression/state_diff.py +177 -0
  405. llm_sca_tooling/traces/contract.py +86 -0
  406. llm_sca_tooling/traces/integration/__init__.py +17 -0
  407. llm_sca_tooling/traces/integration/bug_resolve_hook.py +18 -0
  408. llm_sca_tooling/traces/integration/fl_hook.py +65 -0
  409. llm_sca_tooling/traces/integration/impl_check_hook.py +26 -0
  410. llm_sca_tooling/traces/integration/patch_review_hook.py +11 -0
  411. llm_sca_tooling/traces/models.py +213 -0
  412. llm_sca_tooling/traces/redaction.py +49 -0
  413. llm_sca_tooling/traces/scope_filter.py +96 -0
  414. llm_sca_tooling/traces/service.py +286 -0
  415. llm_sca_tooling/transport/__init__.py +13 -0
  416. llm_sca_tooling/transport/http_transport.py +58 -0
  417. llm_sca_tooling/workflows/__init__.py +1 -0
  418. llm_sca_tooling/workflows/bug_resolve/__init__.py +57 -0
  419. llm_sca_tooling/workflows/bug_resolve/blast_radius_stub.py +51 -0
  420. llm_sca_tooling/workflows/bug_resolve/candidate_patch.py +77 -0
  421. llm_sca_tooling/workflows/bug_resolve/certificate.py +60 -0
  422. llm_sca_tooling/workflows/bug_resolve/config.py +34 -0
  423. llm_sca_tooling/workflows/bug_resolve/gate_runner.py +153 -0
  424. llm_sca_tooling/workflows/bug_resolve/investigate.py +121 -0
  425. llm_sca_tooling/workflows/bug_resolve/models.py +307 -0
  426. llm_sca_tooling/workflows/bug_resolve/monitor_hooks.py +162 -0
  427. llm_sca_tooling/workflows/bug_resolve/patch_selection.py +102 -0
  428. llm_sca_tooling/workflows/bug_resolve/preconditions.py +42 -0
  429. llm_sca_tooling/workflows/bug_resolve/repair_context.py +69 -0
  430. llm_sca_tooling/workflows/bug_resolve/report.py +155 -0
  431. llm_sca_tooling/workflows/bug_resolve/reproduction_test.py +64 -0
  432. llm_sca_tooling/workflows/bug_resolve/state_machine.py +482 -0
  433. llm_sca_tooling/workflows/impl_check/__init__.py +37 -0
  434. llm_sca_tooling/workflows/impl_check/aggregator.py +159 -0
  435. llm_sca_tooling/workflows/impl_check/clause_extractor.py +118 -0
  436. llm_sca_tooling/workflows/impl_check/contract_generator.py +137 -0
  437. llm_sca_tooling/workflows/impl_check/dynamic_verdict.py +71 -0
  438. llm_sca_tooling/workflows/impl_check/grounding.py +56 -0
  439. llm_sca_tooling/workflows/impl_check/harness_policy.py +55 -0
  440. llm_sca_tooling/workflows/impl_check/ingestion.py +44 -0
  441. llm_sca_tooling/workflows/impl_check/intent_graph.py +56 -0
  442. llm_sca_tooling/workflows/impl_check/models.py +272 -0
  443. llm_sca_tooling/workflows/impl_check/operational_binding.py +33 -0
  444. llm_sca_tooling/workflows/impl_check/report.py +254 -0
  445. llm_sca_tooling/workflows/impl_check/static_verdict.py +155 -0
  446. llm_sca_tooling/workflows/impl_check/verdict_matrix.py +70 -0
  447. llm_sca_tooling-0.1.0.dist-info/METADATA +108 -0
  448. llm_sca_tooling-0.1.0.dist-info/RECORD +450 -0
  449. llm_sca_tooling-0.1.0.dist-info/WHEEL +4 -0
  450. llm_sca_tooling-0.1.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,5 @@
1
+ """LLM-SCA tooling package."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ __all__ = ["__version__", "schemas"]
@@ -0,0 +1,42 @@
1
+ """Phase 15 blast-radius service — cross-language and cross-repository impact analysis."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from llm_sca_tooling.blast_radius.change_type import ChangeType, classify_change_type
6
+ from llm_sca_tooling.blast_radius.models import (
7
+ ABIChangeType,
8
+ ABIImpactNote,
9
+ AmbiguousLinkRecord,
10
+ BlastRadiusConfig,
11
+ BlastRadiusReport,
12
+ CrossRepoImpactRecord,
13
+ GeneratedStubImpactNote,
14
+ GeneratedStubImpactType,
15
+ ImpactGroup,
16
+ ImpactRecord,
17
+ MatchMethod,
18
+ )
19
+ from llm_sca_tooling.blast_radius.service import BlastRadiusService
20
+ from llm_sca_tooling.blast_radius.traversal_policy import (
21
+ TraversalPolicy,
22
+ default_policy_for,
23
+ )
24
+
25
+ __all__ = [
26
+ "ABIChangeType",
27
+ "ABIImpactNote",
28
+ "AmbiguousLinkRecord",
29
+ "BlastRadiusConfig",
30
+ "BlastRadiusReport",
31
+ "BlastRadiusService",
32
+ "ChangeType",
33
+ "CrossRepoImpactRecord",
34
+ "GeneratedStubImpactNote",
35
+ "GeneratedStubImpactType",
36
+ "ImpactGroup",
37
+ "ImpactRecord",
38
+ "MatchMethod",
39
+ "TraversalPolicy",
40
+ "classify_change_type",
41
+ "default_policy_for",
42
+ ]
@@ -0,0 +1,74 @@
1
+ """Phase 15 C/C++ ABI impact detection (with fallback when backend absent)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ from llm_sca_tooling.blast_radius.models import ABIChangeType, ABIImpactNote
8
+ from llm_sca_tooling.patch_review.models import ChangedSymbolRecord
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ _CPP_EXTENSIONS = {".cpp", ".cc", ".cxx", ".c", ".h", ".hpp", ".hxx"}
13
+
14
+
15
+ def _is_cpp_file(file_path: str) -> bool:
16
+ lower = file_path.lower()
17
+ return any(lower.endswith(ext) for ext in _CPP_EXTENSIONS)
18
+
19
+
20
+ def build_abi_impact_notes(
21
+ changed_records: list[ChangedSymbolRecord],
22
+ *,
23
+ clangd_available: bool = False,
24
+ ) -> list[ABIImpactNote]:
25
+ """Produce ABIImpactNotes for C/C++ symbol changes.
26
+
27
+ When clangd/libclang backend is unavailable, always produce a note with
28
+ abi_change_type=UNKNOWN and an explanatory diagnostic — never silently skip.
29
+ """
30
+ notes: list[ABIImpactNote] = []
31
+
32
+ for rec in changed_records:
33
+ if not _is_cpp_file(rec.file_path):
34
+ continue
35
+
36
+ if not clangd_available:
37
+ notes.append(
38
+ ABIImpactNote(
39
+ symbol_node_id=rec.graph_node_id or rec.symbol_path,
40
+ symbol_path=rec.symbol_path,
41
+ abi_change_type=ABIChangeType.UNKNOWN,
42
+ diagnostics=[
43
+ "libclang/clangd backend unavailable; ABI analysis skipped. "
44
+ "Manual ABI review required for this C/C++ change."
45
+ ],
46
+ )
47
+ )
48
+ logger.warning(
49
+ "ABI analysis unavailable for %s (clangd not present)", rec.file_path
50
+ )
51
+ continue
52
+
53
+ # When clangd is available, determine ABI change type from record metadata
54
+ from llm_sca_tooling.patch_review.models import ChangeKind # noqa: PLC0415
55
+
56
+ if rec.change_kind == ChangeKind.MODIFIED_SIGNATURE:
57
+ abi_type = ABIChangeType.SIGNATURE_CHANGED
58
+ else:
59
+ abi_type = ABIChangeType.NO_ABI_IMPACT
60
+
61
+ notes.append(
62
+ ABIImpactNote(
63
+ symbol_node_id=rec.graph_node_id or rec.symbol_path,
64
+ symbol_path=rec.symbol_path,
65
+ abi_change_type=abi_type,
66
+ confidence=0.75,
67
+ diagnostics=[],
68
+ )
69
+ )
70
+
71
+ return notes
72
+
73
+
74
+ __all__ = ["build_abi_impact_notes"]
@@ -0,0 +1,46 @@
1
+ """Phase 15 ambiguous-link bucket helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from llm_sca_tooling.blast_radius.models import AmbiguousLinkRecord, MatchMethod
6
+
7
+
8
+ def make_unresolved_cross_repo_link(
9
+ source_node_id: str,
10
+ target_node_id: str,
11
+ edge_type: str,
12
+ confidence: float,
13
+ reason: str = "",
14
+ ) -> AmbiguousLinkRecord:
15
+ return AmbiguousLinkRecord(
16
+ source_node_id=source_node_id,
17
+ target_node_id=target_node_id,
18
+ edge_type=edge_type,
19
+ confidence=confidence,
20
+ match_method=MatchMethod.CROSS_REPO_UNRESOLVED,
21
+ reason_ambiguous=reason or "Cross-repo target unresolved.",
22
+ recommended_followup="Register target repo and re-index.",
23
+ )
24
+
25
+
26
+ def make_candidate_link(
27
+ source_node_id: str,
28
+ target_node_id: str,
29
+ edge_type: str,
30
+ confidence: float,
31
+ analyser_threshold: float,
32
+ ) -> AmbiguousLinkRecord:
33
+ return AmbiguousLinkRecord(
34
+ source_node_id=source_node_id,
35
+ target_node_id=target_node_id,
36
+ edge_type=edge_type,
37
+ confidence=confidence,
38
+ match_method=MatchMethod.CANDIDATE_EDGE,
39
+ reason_ambiguous=(
40
+ f"Edge confidence {confidence:.2f} below analyser threshold {analyser_threshold:.2f}."
41
+ ),
42
+ recommended_followup="Run analyser-level pass to confirm or reject.",
43
+ )
44
+
45
+
46
+ __all__ = ["make_candidate_link", "make_unresolved_cross_repo_link"]
@@ -0,0 +1,99 @@
1
+ """Phase 15 change-type classification."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import StrEnum
6
+
7
+ from llm_sca_tooling.patch_review.models import ChangedSymbolRecord
8
+
9
+
10
+ class ChangeType(StrEnum):
11
+ INTERNAL_IMPLEMENTATION = "internal_implementation"
12
+ PUBLIC_API_CHANGE = "public_api_change"
13
+ IDL_SCHEMA_CONTRACT_CHANGE = "idl_schema_contract_change"
14
+ SECURITY_SENSITIVE_CHANGE = "security_sensitive_change"
15
+ GENERATED_FILE_CHANGE = "generated_file_change"
16
+ MIXED = "mixed"
17
+ UNKNOWN = "unknown"
18
+
19
+
20
+ _IDL_PATTERNS = (
21
+ ".proto",
22
+ ".thrift",
23
+ ".idl",
24
+ ".wsdl",
25
+ ".avsc",
26
+ ".avro",
27
+ "openapi",
28
+ "swagger",
29
+ )
30
+
31
+ _SECURITY_KEYWORDS = (
32
+ "auth",
33
+ "crypt",
34
+ "perm",
35
+ "secret",
36
+ "token",
37
+ "password",
38
+ "passwd",
39
+ "credential",
40
+ "cwe",
41
+ "sanitize",
42
+ "escape",
43
+ "jwt",
44
+ "oauth",
45
+ "acl",
46
+ "rbac",
47
+ )
48
+
49
+
50
+ def _is_idl_path(file_path: str) -> bool:
51
+ lower = file_path.lower()
52
+ return any(pat in lower for pat in _IDL_PATTERNS)
53
+
54
+
55
+ def _is_security_sensitive(record: ChangedSymbolRecord) -> bool:
56
+ lower = record.file_path.lower() + " " + record.symbol_path.lower()
57
+ return any(kw in lower for kw in _SECURITY_KEYWORDS)
58
+
59
+
60
+ def classify_change_type(
61
+ records: list[ChangedSymbolRecord],
62
+ *,
63
+ security_node_ids: set[str] | None = None,
64
+ ) -> tuple[ChangeType, list[ChangeType]]:
65
+ """Classify the change type from a list of ChangedSymbolRecord.
66
+
67
+ Returns a tuple of (primary_type, applicable_types).
68
+ If multiple change types apply, primary_type is MIXED.
69
+ """
70
+ if not records:
71
+ return ChangeType.UNKNOWN, []
72
+
73
+ applicable: set[ChangeType] = set()
74
+
75
+ for rec in records:
76
+ if rec.is_generated:
77
+ applicable.add(ChangeType.GENERATED_FILE_CHANGE)
78
+ if rec.is_public_api:
79
+ applicable.add(ChangeType.PUBLIC_API_CHANGE)
80
+ if _is_idl_path(rec.file_path):
81
+ applicable.add(ChangeType.IDL_SCHEMA_CONTRACT_CHANGE)
82
+ if _is_security_sensitive(rec) or (
83
+ security_node_ids
84
+ and rec.graph_node_id
85
+ and rec.graph_node_id in security_node_ids
86
+ ):
87
+ applicable.add(ChangeType.SECURITY_SENSITIVE_CHANGE)
88
+
89
+ if not applicable:
90
+ return ChangeType.INTERNAL_IMPLEMENTATION, [ChangeType.INTERNAL_IMPLEMENTATION]
91
+
92
+ if len(applicable) > 1:
93
+ return ChangeType.MIXED, sorted(applicable)
94
+
95
+ only = next(iter(applicable))
96
+ return only, [only]
97
+
98
+
99
+ __all__ = ["ChangeType", "classify_change_type"]
@@ -0,0 +1,117 @@
1
+ """Phase 15 cross-repository traversal with is_partial fallback."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING
7
+
8
+ from llm_sca_tooling.blast_radius.models import (
9
+ AmbiguousLinkRecord,
10
+ CrossRepoImpactRecord,
11
+ MatchMethod,
12
+ )
13
+ from llm_sca_tooling.schemas.enums import GraphEdgeType
14
+
15
+ if TYPE_CHECKING:
16
+ from llm_sca_tooling.storage.graph_store import GraphStore
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ _CROSS_REPO_EDGE_TYPES = {
21
+ GraphEdgeType.CONSUMES.value,
22
+ GraphEdgeType.EXPOSES.value,
23
+ GraphEdgeType.FFI.value,
24
+ }
25
+
26
+
27
+ def traverse_cross_repo(
28
+ changed_node_ids: list[str],
29
+ graph_store: GraphStore,
30
+ *,
31
+ registered_repo_ids: list[str] | None = None,
32
+ max_hops: int = 2,
33
+ analyser_threshold: float = 0.75,
34
+ ) -> tuple[list[CrossRepoImpactRecord], list[AmbiguousLinkRecord]]:
35
+ """Traverse cross-repo graph overlay to find consuming repos.
36
+
37
+ When the overlay is unavailable or incomplete, returns CrossRepoImpactRecord
38
+ with is_partial=True — never silently skips.
39
+
40
+ Returns (cross_repo_records, ambiguous_link_records).
41
+ """
42
+ if not registered_repo_ids:
43
+ logger.info("No registered repos provided; cross-repo traversal skipped.")
44
+ ambiguous = [
45
+ AmbiguousLinkRecord(
46
+ source_node_id=nid,
47
+ target_node_id="<unknown>",
48
+ edge_type=GraphEdgeType.CONSUMES.value,
49
+ confidence=0.0,
50
+ match_method=MatchMethod.CROSS_REPO_UNRESOLVED,
51
+ reason_ambiguous="No registered repos available for cross-repo traversal.",
52
+ recommended_followup="Register consuming repos and re-run blast radius.",
53
+ )
54
+ for nid in changed_node_ids
55
+ ]
56
+ return [], ambiguous
57
+
58
+ edge_types = [GraphEdgeType(et) for et in _CROSS_REPO_EDGE_TYPES]
59
+ cross_records: list[CrossRepoImpactRecord] = []
60
+ cross_ambiguous: list[AmbiguousLinkRecord] = []
61
+
62
+ try:
63
+ slice_ = graph_store.fetch_ego_graph(
64
+ changed_node_ids,
65
+ depth=max_hops,
66
+ edge_types=edge_types,
67
+ )
68
+
69
+ # Group consuming nodes by repo_id
70
+ repo_consumers: dict[str, list[str]] = {}
71
+ for node in slice_.nodes:
72
+ if node.node_id in set(changed_node_ids):
73
+ continue
74
+ repo_id = node.repo.repo_id
75
+ if repo_id not in registered_repo_ids:
76
+ cross_ambiguous.append(
77
+ AmbiguousLinkRecord(
78
+ source_node_id=changed_node_ids[0] if changed_node_ids else "?",
79
+ target_node_id=node.node_id,
80
+ edge_type=GraphEdgeType.CONSUMES.value,
81
+ confidence=0.0,
82
+ match_method=MatchMethod.CROSS_REPO_UNRESOLVED,
83
+ reason_ambiguous=f"Repo {repo_id!r} not in registered repo list.",
84
+ recommended_followup="Register this repo to enable full cross-repo impact.",
85
+ )
86
+ )
87
+ continue
88
+ repo_consumers.setdefault(repo_id, []).append(node.node_id)
89
+
90
+ for repo_id, consumers in repo_consumers.items():
91
+ cross_records.append(
92
+ CrossRepoImpactRecord(
93
+ repo_id=repo_id,
94
+ consuming_node_ids=consumers,
95
+ hop_distance=1,
96
+ is_partial=False,
97
+ confidence=analyser_threshold,
98
+ )
99
+ )
100
+
101
+ except Exception as exc: # noqa: BLE001
102
+ logger.warning("Cross-repo traversal failed: %s", exc)
103
+ cross_records.append(
104
+ CrossRepoImpactRecord(
105
+ repo_id="<unknown>",
106
+ consuming_node_ids=[],
107
+ hop_distance=0,
108
+ is_partial=True,
109
+ partial_reason=f"Graph overlay traversal failed: {exc}",
110
+ confidence=0.0,
111
+ )
112
+ )
113
+
114
+ return cross_records, cross_ambiguous
115
+
116
+
117
+ __all__ = ["traverse_cross_repo"]
@@ -0,0 +1,70 @@
1
+ """Phase 15 linked docs/specs impact — flag stale design clauses."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING
7
+
8
+ from llm_sca_tooling.schemas.enums import GraphEdgeType, GraphNodeType
9
+
10
+ if TYPE_CHECKING:
11
+ from llm_sca_tooling.storage.graph_store import GraphStore
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ _DOC_NODE_TYPES = {
16
+ GraphNodeType.DOCUMENT,
17
+ GraphNodeType.DESIGN_CLAUSE,
18
+ GraphNodeType.INTENT_NODE,
19
+ }
20
+
21
+ _DOC_EDGE_TYPES = [
22
+ GraphEdgeType.SATISFIES,
23
+ GraphEdgeType.VIOLATES,
24
+ GraphEdgeType.DOCUMENTS,
25
+ GraphEdgeType.DECOMPOSES_TO,
26
+ ]
27
+
28
+
29
+ def collect_linked_docs(
30
+ changed_node_ids: list[str],
31
+ graph_store: GraphStore,
32
+ *,
33
+ max_hops: int = 3,
34
+ ) -> tuple[list[dict[str, object]], str]:
35
+ """Collect design_clause / intent_node / document nodes linked to changed symbols.
36
+
37
+ Returns (doc_node_dicts, summary_string).
38
+ """
39
+ docs: list[dict[str, object]] = []
40
+
41
+ try:
42
+ slice_ = graph_store.fetch_ego_graph(
43
+ changed_node_ids,
44
+ depth=max_hops,
45
+ edge_types=_DOC_EDGE_TYPES,
46
+ )
47
+ for node in slice_.nodes:
48
+ if node.node_id in set(changed_node_ids):
49
+ continue
50
+ if node.node_type in _DOC_NODE_TYPES:
51
+ docs.append(
52
+ {
53
+ "node_id": node.node_id,
54
+ "node_type": node.node_type.value,
55
+ "label": node.label,
56
+ "potentially_stale": True,
57
+ }
58
+ )
59
+ except Exception as exc: # noqa: BLE001
60
+ logger.warning("Linked docs collection failed: %s", exc)
61
+
62
+ summary = (
63
+ f"{len(docs)} linked doc/spec node(s) may be stale after this change."
64
+ if docs
65
+ else "No linked docs/specs affected."
66
+ )
67
+ return docs, summary
68
+
69
+
70
+ __all__ = ["collect_linked_docs"]
@@ -0,0 +1,60 @@
1
+ """Phase 15 generated-stub impact detection and reporting."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ from llm_sca_tooling.blast_radius.models import (
8
+ GeneratedStubImpactNote,
9
+ GeneratedStubImpactType,
10
+ )
11
+ from llm_sca_tooling.patch_review.models import ChangedSymbolRecord
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ def build_generated_stub_notes(
17
+ diff_id: str,
18
+ changed_records: list[ChangedSymbolRecord],
19
+ *,
20
+ generated_file_allowlist: set[str] | None = None,
21
+ ) -> list[GeneratedStubImpactNote]:
22
+ """Produce GeneratedStubImpactNotes for generated files touched by this diff.
23
+
24
+ Rules:
25
+ - Every generated file produces a note.
26
+ - Direct edits to generated files set manual_edit_policy_flag=True unless in allowlist.
27
+ """
28
+ allowlist = generated_file_allowlist or set()
29
+ notes: list[GeneratedStubImpactNote] = []
30
+
31
+ for rec in changed_records:
32
+ if not rec.is_generated:
33
+ continue
34
+ is_manual_violation = rec.file_path not in allowlist
35
+ notes.append(
36
+ GeneratedStubImpactNote(
37
+ diff_id=diff_id,
38
+ generated_file_path=rec.file_path,
39
+ generator_source="",
40
+ source_contract_node_id=rec.graph_node_id,
41
+ impact_type=GeneratedStubImpactType.GENERATED_FILE_DIRECTLY_CHANGED,
42
+ manual_edit_policy_flag=is_manual_violation,
43
+ recommended_action=(
44
+ "Check policy allowlist before merging."
45
+ if is_manual_violation
46
+ else "Verify regeneration is idempotent."
47
+ ),
48
+ )
49
+ )
50
+ if is_manual_violation:
51
+ logger.warning(
52
+ "Manual edit detected on generated file %s (diff=%s)",
53
+ rec.file_path,
54
+ diff_id,
55
+ )
56
+
57
+ return notes
58
+
59
+
60
+ __all__ = ["build_generated_stub_notes"]