sourcecode 4.9.1__tar.gz → 4.10.1__tar.gz

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 (218) hide show
  1. {sourcecode-4.9.1 → sourcecode-4.10.1}/CHANGELOG.md +50 -0
  2. {sourcecode-4.9.1 → sourcecode-4.10.1}/PKG-INFO +9 -7
  3. {sourcecode-4.9.1 → sourcecode-4.10.1}/README.md +8 -6
  4. {sourcecode-4.9.1 → sourcecode-4.10.1}/pyproject.toml +1 -1
  5. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/audit_report.py +10 -2
  7. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/cache_model.py +6 -0
  8. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/canonical_ir.py +19 -0
  9. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/cli.py +114 -4
  10. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/migrate_check.py +1 -0
  11. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/progress.py +21 -6
  12. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/provenance.py +12 -9
  13. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/release_info.py +1 -1
  14. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/repository_ir.py +21 -0
  15. sourcecode-4.10.1/src/sourcecode/runs.py +136 -0
  16. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/security_config_scan.py +55 -0
  17. {sourcecode-4.9.1 → sourcecode-4.10.1}/.github/workflows/build-windows.yml +0 -0
  18. {sourcecode-4.9.1 → sourcecode-4.10.1}/.gitignore +0 -0
  19. {sourcecode-4.9.1 → sourcecode-4.10.1}/.ruff.toml +0 -0
  20. {sourcecode-4.9.1 → sourcecode-4.10.1}/CLAUDE.md +0 -0
  21. {sourcecode-4.9.1 → sourcecode-4.10.1}/CONTRIBUTING.md +0 -0
  22. {sourcecode-4.9.1 → sourcecode-4.10.1}/LICENSE +0 -0
  23. {sourcecode-4.9.1 → sourcecode-4.10.1}/SECURITY.md +0 -0
  24. {sourcecode-4.9.1 → sourcecode-4.10.1}/raw +0 -0
  25. {sourcecode-4.9.1 → sourcecode-4.10.1}/scripts/compare_integration_engines.py +0 -0
  26. {sourcecode-4.9.1 → sourcecode-4.10.1}/scripts/customer_smoke_test.sh +0 -0
  27. {sourcecode-4.9.1 → sourcecode-4.10.1}/scripts/generate_jdk_exports.py +0 -0
  28. {sourcecode-4.9.1 → sourcecode-4.10.1}/scripts/perf_harness.py +0 -0
  29. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/adaptive_scanner.py +0 -0
  30. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/archetype.py +0 -0
  31. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/architectural_baseline.py +0 -0
  32. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/architectural_delta.py +0 -0
  33. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/architecture_analyzer.py +0 -0
  34. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/architecture_summary.py +0 -0
  35. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/ast_extractor.py +0 -0
  36. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/baseline_autocapture.py +0 -0
  37. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/cache.py +0 -0
  38. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/call_surface.py +0 -0
  39. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/caller_metrics.py +0 -0
  40. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/caller_reach.py +0 -0
  41. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/chain_rules.py +0 -0
  42. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/change_plan.py +0 -0
  43. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/cir_graphs.py +0 -0
  44. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/classifier.py +0 -0
  45. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/client_calls.py +0 -0
  46. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/code_notes_analyzer.py +0 -0
  47. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/compare.py +0 -0
  48. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/confidence_analyzer.py +0 -0
  49. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/constraint_diff.py +0 -0
  50. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/context_cache.py +0 -0
  51. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/context_graph.py +0 -0
  52. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/context_scorer.py +0 -0
  53. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/context_summarizer.py +0 -0
  54. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/contract_diff.py +0 -0
  55. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/contract_init.py +0 -0
  56. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/contract_model.py +0 -0
  57. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/contract_pipeline.py +0 -0
  58. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/coverage_parser.py +0 -0
  59. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/data_exposure.py +0 -0
  60. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/data_labels.py +0 -0
  61. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/defect_identity.py +0 -0
  62. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/degradation.py +0 -0
  63. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/dependency_analyzer.py +0 -0
  64. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/deployment_prefix.py +0 -0
  65. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/__init__.py +0 -0
  66. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/base.py +0 -0
  67. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/csproj_parser.py +0 -0
  68. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/dart.py +0 -0
  69. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/dotnet.py +0 -0
  70. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/elixir.py +0 -0
  71. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/go.py +0 -0
  72. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/heuristic.py +0 -0
  73. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/hybrid.py +0 -0
  74. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/java.py +0 -0
  75. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/jvm_ext.py +0 -0
  76. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/nodejs.py +0 -0
  77. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/parsers.py +0 -0
  78. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/php.py +0 -0
  79. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/project.py +0 -0
  80. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/python.py +0 -0
  81. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/ruby.py +0 -0
  82. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/rust.py +0 -0
  83. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/systems.py +0 -0
  84. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/terraform.py +0 -0
  85. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/detectors/tooling.py +0 -0
  86. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/doc_analyzer.py +0 -0
  87. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/dynamic_argument_surface.py +0 -0
  88. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/endpoint_literals.py +0 -0
  89. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/endpoint_metrics.py +0 -0
  90. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/entrypoint_classifier.py +0 -0
  91. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/env_analyzer.py +0 -0
  92. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/envelope.py +0 -0
  93. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/environment_resolution.py +0 -0
  94. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/error_schema.py +0 -0
  95. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/evidence_provider.py +0 -0
  96. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/explain.py +0 -0
  97. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/facts/__init__.py +0 -0
  98. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/facts/registry.json +0 -0
  99. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/file_chunker.py +0 -0
  100. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/file_classifier.py +0 -0
  101. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/filter_surface.py +0 -0
  102. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/format_contract.py +0 -0
  103. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/fqn_utils.py +0 -0
  104. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/git_analyzer.py +0 -0
  105. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/git_checkout.py +0 -0
  106. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/graph_analyzer.py +0 -0
  107. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/graph_evidence.py +0 -0
  108. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/hibernate_strat.py +0 -0
  109. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/identity_fallback.py +0 -0
  110. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/integration_coordinates.py +0 -0
  111. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/jdk_exports.py +0 -0
  112. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/license.py +0 -0
  113. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp/__init__.py +0 -0
  114. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  115. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  116. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  117. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  118. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  119. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp/orchestrator.py +0 -0
  120. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp/registry.py +0 -0
  121. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp/runner.py +0 -0
  122. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp/server.py +0 -0
  123. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/mcp_nudge.py +0 -0
  124. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/metrics_analyzer.py +0 -0
  125. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/migration_blast.py +0 -0
  126. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/non_coverage.py +0 -0
  127. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/openapi_surface.py +0 -0
  128. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/openrewrite_recipe.py +0 -0
  129. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/output_budget.py +0 -0
  130. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/parse_cache.py +0 -0
  131. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/path_admission.py +0 -0
  132. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/path_filters.py +0 -0
  133. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/perf.py +0 -0
  134. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/pipe_contract.py +0 -0
  135. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/posture.py +0 -0
  136. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/pr_comment_renderer.py +0 -0
  137. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/pr_impact.py +0 -0
  138. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/prepare_context.py +0 -0
  139. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/ranking_engine.py +0 -0
  140. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/readiness_timeline.py +0 -0
  141. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/reconciliation.py +0 -0
  142. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/redactor.py +0 -0
  143. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/reference_facts.py +0 -0
  144. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/relevance_scorer.py +0 -0
  145. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/remedies.py +0 -0
  146. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/rename_refactor.py +0 -0
  147. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/repo_classifier.py +0 -0
  148. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/__init__.py +0 -0
  149. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/context.py +0 -0
  150. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/errors.py +0 -0
  151. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/executor.py +0 -0
  152. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/planner.py +0 -0
  153. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/query.py +0 -0
  154. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/request.py +0 -0
  155. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/resolution.py +0 -0
  156. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/result.py +0 -0
  157. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/retriever.py +0 -0
  158. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/runtime.py +0 -0
  159. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/steps.py +0 -0
  160. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  161. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/steps_graph.py +0 -0
  162. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/steps_impact.py +0 -0
  163. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/steps_intf.py +0 -0
  164. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/steps_struct.py +0 -0
  165. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  166. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/ris.py +0 -0
  167. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/risk.py +0 -0
  168. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/rule_catalog.py +0 -0
  169. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/runtime_classifier.py +0 -0
  170. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/sarif.py +0 -0
  171. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/scanner.py +0 -0
  172. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/schema.py +0 -0
  173. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  174. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/security_config.py +0 -0
  175. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/security_posture.py +0 -0
  176. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/semantic_analyzer.py +0 -0
  177. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/semantic_impact_engine.py +0 -0
  178. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/semantic_integration_engine.py +0 -0
  179. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/semantic_services.py +0 -0
  180. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/serializer.py +0 -0
  181. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/servlet_surface.py +0 -0
  182. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/source_text.py +0 -0
  183. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/spring_event_topology.py +0 -0
  184. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/spring_findings.py +0 -0
  185. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/spring_impact.py +0 -0
  186. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/spring_model.py +0 -0
  187. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/spring_profiles.py +0 -0
  188. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/spring_properties.py +0 -0
  189. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/spring_security_audit.py +0 -0
  190. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/spring_semantic.py +0 -0
  191. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/spring_tx_analyzer.py +0 -0
  192. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/summarizer.py +0 -0
  193. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/target_admission.py +0 -0
  194. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/telemetry/__init__.py +0 -0
  195. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/telemetry/config.py +0 -0
  196. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/telemetry/consent.py +0 -0
  197. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/telemetry/events.py +0 -0
  198. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/telemetry/filters.py +0 -0
  199. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/telemetry/transport.py +0 -0
  200. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/test_gap_ranking.py +0 -0
  201. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/test_sources.py +0 -0
  202. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/token_estimate.py +0 -0
  203. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/tree_utils.py +0 -0
  204. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/type_usage_surface.py +0 -0
  205. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/validation_inference.py +0 -0
  206. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/validation_surface.py +0 -0
  207. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/verify_edit.py +0 -0
  208. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/verify_repo.py +0 -0
  209. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/verify_rules.py +0 -0
  210. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/version_check.py +0 -0
  211. {sourcecode-4.9.1 → sourcecode-4.10.1}/src/sourcecode/workspace.py +0 -0
  212. {sourcecode-4.9.1 → sourcecode-4.10.1}/supabase/functions/README.md +0 -0
  213. {sourcecode-4.9.1 → sourcecode-4.10.1}/supabase/functions/get-license/index.ts +0 -0
  214. {sourcecode-4.9.1 → sourcecode-4.10.1}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  215. {sourcecode-4.9.1 → sourcecode-4.10.1}/supabase/functions/telemetry/index.ts +0 -0
  216. {sourcecode-4.9.1 → sourcecode-4.10.1}/supabase/sql/license_event_ordering.sql +0 -0
  217. {sourcecode-4.9.1 → sourcecode-4.10.1}/supabase/sql/licensing_schema.sql +0 -0
  218. {sourcecode-4.9.1 → sourcecode-4.10.1}/supabase/sql/telemetry_events.sql +0 -0
@@ -2,6 +2,56 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [4.10.1] — 2026-08-05
6
+
7
+ ### Fixed
8
+
9
+ - **`cache model` now includes `audit-report`.** Warm-cache documentation and
10
+ machine output now agree that `audit-report` reuses the same structural layers
11
+ as the other deep analysis/reporting commands.
12
+ - **`migrate-check --blast-radius` no longer changes unrelated provenance.**
13
+ The optional blast-radius flag now adds only the blast-radius block; the shared
14
+ evidence manifest stays deterministic for shape-comparison consumers.
15
+ - **`impact` expands class/interface seeds to in-repository subtypes.** A base
16
+ class or interface query now reaches callers of subtype members, matching the
17
+ endpoint-reach behavior already expected from `impact-chain`.
18
+ - **Severity fields are locked to named units.** Tests now preserve the contract
19
+ that declared scanner severity, composed effective severity and risk band are
20
+ separate fields rather than contradictory values for the same claim.
21
+
22
+ ### Documentation
23
+
24
+ - README, user guide, roadmap and defect ledger now reflect the 4.10.1 patch
25
+ status and close stale rows that were already fixed by measured tests.
26
+
27
+ ## [4.10.0] — 2026-08-05
28
+
29
+ ### Fixed
30
+
31
+ - **Canonical endpoint projection rejects parser-corrupt Java handler names.**
32
+ Reserved Java keywords such as `if` can no longer reach CIR/risk as synthetic
33
+ handler symbols.
34
+ - **`DEAD-001` reports commented Java security annotations.** Java comments that
35
+ contain Spring/Jakarta/custom security annotations are now treated as disabled
36
+ controls, while active annotations stay out of dead-code findings.
37
+ - **`SEC-007` reports JavaMelody descriptor credentials.** `authorized-users`
38
+ values in deployment descriptors are detected and redacted even when they are
39
+ not written as `{SHA-*}` digest literals.
40
+ - **`audit-report` can reuse existing risk evidence.** `--from-risk risk.json`
41
+ packages a previous `ask risk` payload instead of recomputing repo-wide risk.
42
+ - **Long analysis observability improved.** `ASK_PROGRESS=1` enables non-TTY
43
+ heartbeat lines, long commands write durable `.ask/runs` records, and file
44
+ outputs are written atomically.
45
+ - **Large-repo CI budgets are explicit.** `ASK_MAX_ANALYSIS_SECONDS` can fail a
46
+ repo-wide/deep command before work starts when the configured budget is below
47
+ the command's safe floor.
48
+
49
+ ### Documentation
50
+
51
+ - README, user guide and root `ask --help` now distinguish per-symbol,
52
+ inventory and repo-wide/deep command costs instead of treating cache
53
+ benchmarks as universal runtime promises.
54
+
5
55
  ## [4.9.1] — 2026-08-04
6
56
 
7
57
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 4.9.1
3
+ Version: 4.10.1
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
42
42
 
43
43
  **Context · Impact · Migration · Architecture · Review — everything from one structural model.**
44
44
 
45
- ![Version](https://img.shields.io/badge/version-4.9.1-blue)
45
+ ![Version](https://img.shields.io/badge/version-4.10.1-blue)
46
46
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
47
47
 
48
48
  > **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
@@ -54,9 +54,9 @@ Description-Content-Type: text/markdown
54
54
 
55
55
  ## The problem
56
56
 
57
- Every time an AI coding agent starts a new session, it has to re-parse the repository from scratch. For a large Java or TypeScript monolith, that means 5–15 seconds per invocation. Multiply by dozens of agent turns per hour, and repo context acquisition becomes a real bottleneck — not just latency, but tokens, compute, and iteration velocity.
57
+ Every time an AI coding agent starts a new session, it has to re-parse the repository from scratch. On small and mid-sized repos that cost is seconds; on large Java/Spring monoliths, repo-wide semantic composition can be minutes. Multiply that by dozens of agent turns per hour, and repo context acquisition becomes a real bottleneck — not just latency, but tokens, compute, and iteration velocity.
58
58
 
59
- ASK Engine solves this with a persistent structural cache keyed on file content hashes. After the first scan, every subsequent invocation returns pre-built context in milliseconds. The repo doesn't change? The cache doesn't expire.
59
+ ASK Engine solves this with a persistent structural cache keyed on file content hashes. After the first scan, later invocations reuse pre-built context whenever their command can consume the warmed layers. The repo doesn't change? The cache doesn't expire.
60
60
 
61
61
  **The cache is not a performance optimization. It is what makes ASK Engine usable as infrastructure rather than a one-off tool.**
62
62
 
@@ -69,9 +69,11 @@ ASK Engine solves this with a persistent structural cache keyed on file content
69
69
  | Keycloak | 7,885 Java files | 10.5s | 0.6s | **~17x** |
70
70
  | BroadleafCommerce | 2,985 Java files | 2.7s | 0.3s | **~9x** |
71
71
 
72
- Cache keyed on content hashes invalidated only when source changes. On repeated agent sessions against the same codebase, nearly every invocation is a cache hit.
72
+ These numbers are cache-layer benchmarks, not a promise for every command. Per-symbol queries and compact context benefit most. Inventory commands scale with file count. Repo-wide/deep compositions such as `risk`, `audit-report` and large `spring-audit` runs can take minutes on multi-thousand-endpoint repositories and belong in a supervised or nightly job unless CI sets an explicit budget.
73
73
 
74
- At 0.3s per call, ASK Engine becomes **constant infrastructure** inside agent loops — call it before every edit, every PR review, every test run, without batching or caching manually.
74
+ With the right command class, ASK Engine becomes **constant infrastructure** inside agent loops — call the fast/context surfaces before edits and reserve deep compositions for the points where their evidence is worth the runtime.
75
+
76
+ **Large-repo controls.** Set `ASK_PROGRESS=1` to get line heartbeats in non-TTY supervised runs. Set `ASK_MAX_ANALYSIS_SECONDS=<seconds>` to fail before starting a repo-wide/deep command whose configured budget is below its safe floor. Use `ask audit-report --from-risk risk.json` to package an existing risk run instead of recomputing it.
75
77
 
76
78
  **What a warm actually covers.** `ask cache warm` runs the compact analysis: it rebuilds the
77
79
  shared structural layers (L1/L2 + the Repository Intelligence Snapshot + the shared Canonical
@@ -97,7 +99,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
97
99
  # pip / pipx
98
100
  pipx install sourcecode # or: pip install sourcecode
99
101
 
100
- ask version # ask 4.9.1 — and, on a build that has aged,
102
+ ask version # ask 4.10.1 — and, on a build that has aged,
101
103
  # how many releases have probably shipped since
102
104
  ```
103
105
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  **Context · Impact · Migration · Architecture · Review — everything from one structural model.**
6
6
 
7
- ![Version](https://img.shields.io/badge/version-4.9.1-blue)
7
+ ![Version](https://img.shields.io/badge/version-4.10.1-blue)
8
8
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
9
9
 
10
10
  > **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
@@ -16,9 +16,9 @@
16
16
 
17
17
  ## The problem
18
18
 
19
- Every time an AI coding agent starts a new session, it has to re-parse the repository from scratch. For a large Java or TypeScript monolith, that means 5–15 seconds per invocation. Multiply by dozens of agent turns per hour, and repo context acquisition becomes a real bottleneck — not just latency, but tokens, compute, and iteration velocity.
19
+ Every time an AI coding agent starts a new session, it has to re-parse the repository from scratch. On small and mid-sized repos that cost is seconds; on large Java/Spring monoliths, repo-wide semantic composition can be minutes. Multiply that by dozens of agent turns per hour, and repo context acquisition becomes a real bottleneck — not just latency, but tokens, compute, and iteration velocity.
20
20
 
21
- ASK Engine solves this with a persistent structural cache keyed on file content hashes. After the first scan, every subsequent invocation returns pre-built context in milliseconds. The repo doesn't change? The cache doesn't expire.
21
+ ASK Engine solves this with a persistent structural cache keyed on file content hashes. After the first scan, later invocations reuse pre-built context whenever their command can consume the warmed layers. The repo doesn't change? The cache doesn't expire.
22
22
 
23
23
  **The cache is not a performance optimization. It is what makes ASK Engine usable as infrastructure rather than a one-off tool.**
24
24
 
@@ -31,9 +31,11 @@ ASK Engine solves this with a persistent structural cache keyed on file content
31
31
  | Keycloak | 7,885 Java files | 10.5s | 0.6s | **~17x** |
32
32
  | BroadleafCommerce | 2,985 Java files | 2.7s | 0.3s | **~9x** |
33
33
 
34
- Cache keyed on content hashes invalidated only when source changes. On repeated agent sessions against the same codebase, nearly every invocation is a cache hit.
34
+ These numbers are cache-layer benchmarks, not a promise for every command. Per-symbol queries and compact context benefit most. Inventory commands scale with file count. Repo-wide/deep compositions such as `risk`, `audit-report` and large `spring-audit` runs can take minutes on multi-thousand-endpoint repositories and belong in a supervised or nightly job unless CI sets an explicit budget.
35
35
 
36
- At 0.3s per call, ASK Engine becomes **constant infrastructure** inside agent loops — call it before every edit, every PR review, every test run, without batching or caching manually.
36
+ With the right command class, ASK Engine becomes **constant infrastructure** inside agent loops — call the fast/context surfaces before edits and reserve deep compositions for the points where their evidence is worth the runtime.
37
+
38
+ **Large-repo controls.** Set `ASK_PROGRESS=1` to get line heartbeats in non-TTY supervised runs. Set `ASK_MAX_ANALYSIS_SECONDS=<seconds>` to fail before starting a repo-wide/deep command whose configured budget is below its safe floor. Use `ask audit-report --from-risk risk.json` to package an existing risk run instead of recomputing it.
37
39
 
38
40
  **What a warm actually covers.** `ask cache warm` runs the compact analysis: it rebuilds the
39
41
  shared structural layers (L1/L2 + the Repository Intelligence Snapshot + the shared Canonical
@@ -59,7 +61,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
59
61
  # pip / pipx
60
62
  pipx install sourcecode # or: pip install sourcecode
61
63
 
62
- ask version # ask 4.9.1 — and, on a build that has aged,
64
+ ask version # ask 4.10.1 — and, on a build that has aged,
63
65
  # how many releases have probably shipped since
64
66
  ```
65
67
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "4.9.1"
7
+ version = "4.10.1"
8
8
  description = "Persistent structural context and ultra-fast repeated analysis for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "4.9.1"
7
+ __version__ = "4.10.1"
@@ -39,13 +39,19 @@ def build_audit_report(
39
39
  profiles: Optional[set[str]] = None,
40
40
  sign_key: Optional[bytes] = None,
41
41
  risk_limit: int = 10,
42
+ risk_payload: Optional[dict[str, Any]] = None,
42
43
  ) -> dict[str, Any]:
43
44
  """Build the buyer-readable audit bundle from existing command payloads."""
44
45
  from sourcecode.posture import build_posture
45
- from sourcecode.risk import build_risk
46
46
 
47
47
  root = Path(root).resolve()
48
- risk = build_risk(root, limit=risk_limit, min_band="low", profiles=profiles)
48
+ if risk_payload is None:
49
+ from sourcecode.risk import build_risk
50
+ risk = build_risk(root, limit=risk_limit, min_band="low", profiles=profiles)
51
+ risk_source = "computed"
52
+ else:
53
+ risk = risk_payload
54
+ risk_source = "from-risk"
49
55
  posture = build_posture(root, profiles or set())
50
56
  top_risks = risk.get("risks", [])[:risk_limit]
51
57
  access = ((posture.get("endpoints") or {}).get("effective_access") or {})
@@ -58,6 +64,7 @@ def build_audit_report(
58
64
  **({"profile_set": sorted(profiles)} if profiles else {}),
59
65
  "summary": {
60
66
  "risk_model": risk.get("model"),
67
+ "risk_source": risk_source,
61
68
  "total_defects": risk.get("total_defects"),
62
69
  "total_findings": risk.get("total_findings"),
63
70
  "risk_bands": risk.get("by_band", {}),
@@ -86,6 +93,7 @@ def build_audit_report(
86
93
  inputs={
87
94
  "profiles": sorted(profiles) if profiles else None,
88
95
  "risk_limit": risk_limit,
96
+ "risk_source": risk_source,
89
97
  "signed": bool(sign_key),
90
98
  },
91
99
  )
@@ -144,6 +144,12 @@ COMMANDS: tuple[CommandCache, ...] = (
144
144
  "repository side, so what it buys is what it buys `risk`.",
145
145
  "not measured on the battery yet — bounded by the `risk` composition, plus "
146
146
  "reading one JSON file"),
147
+ CommandCache("audit-report", ("cir", "parse"), "shared", False,
148
+ "Packages `risk` and `posture` evidence. A warm helps the repository side; "
149
+ "`--from-risk risk.json` skips risk recomputation entirely and only builds "
150
+ "the posture/report projection.",
151
+ "not measured on the battery yet — bounded by `risk` + `posture`, or by "
152
+ "`posture` when `--from-risk` is supplied"),
147
153
  CommandCache("migrate-recipe", ("parse",), "shared", False,
148
154
  "Runs the same scan as `migrate-check` and projects its findings into an "
149
155
  "OpenRewrite recipe, so it buys exactly what a warm buys `migrate-check`: "
@@ -43,6 +43,23 @@ _REVERSE_EXCLUDE: frozenset[str] = frozenset({"annotated_with", "mapped_to"})
43
43
  # the CIR endpoint count reconciles with the `endpoints` command). See BUG #7.
44
44
  _FQN_PATH_RE: re.Pattern = re.compile(r"/(org|com|net|io|edu)\.[a-z][a-z0-9]*\.[a-zA-Z]")
45
45
 
46
+ _JAVA_RESERVED_HANDLER_NAMES: frozenset[str] = frozenset({
47
+ "if", "else", "for", "while", "do", "switch", "case", "break", "continue",
48
+ "return", "new", "throw", "try", "catch", "finally", "instanceof",
49
+ "this", "super", "void", "class", "interface", "enum", "extends", "implements",
50
+ "import", "package", "static", "final", "abstract", "synchronized", "native",
51
+ "true", "false", "null",
52
+ })
53
+
54
+
55
+ def _route_has_reserved_handler(route: dict) -> bool:
56
+ """Reject parser-corrupt endpoint symbols such as ``pkg.Controller#if``."""
57
+ symbol = str(route.get("symbol") or "")
58
+ if "#" not in symbol:
59
+ return False
60
+ handler = symbol.rsplit("#", 1)[1].split("(", 1)[0].strip()
61
+ return handler in _JAVA_RESERVED_HANDLER_NAMES
62
+
46
63
 
47
64
  # ---------------------------------------------------------------------------
48
65
  # CanonicalSecurity
@@ -438,6 +455,8 @@ def ir_dict_to_canonical(
438
455
  for r in route_surface:
439
456
  if r.get("scope") == "test_util":
440
457
  continue
458
+ if _route_has_reserved_handler(r):
459
+ continue
441
460
  if _FQN_PATH_RE.search(r.get("path", "") or ""):
442
461
  continue
443
462
  ep = _route_to_canonical_endpoint(r)
@@ -338,8 +338,10 @@ def _build_help_text() -> str:
338
338
  Deterministic Java/Spring semantics and reusable structural context for AI coding agents.
339
339
 
340
340
  Cache warms on first scan; later calls reuse pre-built context instead of rescanning.
341
- Scan and warm time scale with repo size small repos in seconds, large repos (thousands
342
- of files) in minutes. Semantic analysis itself is sub-second; repo indexing dominates.
341
+ Performance depends on command class and repo size: per-symbol queries are the fast path,
342
+ inventory commands scale with files, and repo-wide/deep compositions can take
343
+ minutes on multi-thousand-endpoint repositories. Use deep jobs nightly or with
344
+ ASK_MAX_ANALYSIS_SECONDS/ASK_PROGRESS when CI needs an explicit budget.
343
345
 
344
346
  [bold]Start here — Java/Spring analysis:[/bold]
345
347
  posture . --diff dev:prod [dim]# effective access, two profile sets (exp.)[/dim]
@@ -881,9 +883,24 @@ def _enforce_format(command: str, fmt: str) -> None:
881
883
 
882
884
 
883
885
  def _safe_write_file(path: "Path", content: str) -> None:
884
- """Write content to path, emitting a clean JSON error on I/O failure."""
886
+ """Atomically write content to path, emitting a clean JSON error on I/O failure."""
885
887
  try:
886
- path.write_text(content, encoding="utf-8")
888
+ path = Path(path)
889
+ path.parent.mkdir(parents=True, exist_ok=True)
890
+ tmp = path.with_name(f".{path.name}.tmp-{os.getpid()}-{time.time_ns()}")
891
+ tmp.write_text(content, encoding="utf-8")
892
+ tmp.replace(path)
893
+ try:
894
+ from sourcecode.runs import active_run
895
+ run = active_run()
896
+ if run is not None:
897
+ run.write(
898
+ status="running",
899
+ output_path=path,
900
+ output_size=path.stat().st_size,
901
+ )
902
+ except Exception:
903
+ pass
887
904
  except OSError as _exc:
888
905
  _emit_error_json(
889
906
  INVALID_INPUT_CODE,
@@ -968,6 +985,50 @@ def _active_command_context() -> "tuple[str, Optional[Path]]":
968
985
 
969
986
 
970
987
  _EXPENSIVE_ANALYSIS_TTL_SECONDS = 6 * 60 * 60
988
+ _ANALYSIS_CLASSES: dict[str, tuple[str, int, str]] = {
989
+ "spring-audit": ("repo-wide", 600, "use nightly/deep tier on large repositories"),
990
+ "risk": ("deep", 1200, "run `ask spring-audit` or a saved `risk.json` first"),
991
+ "audit-report": ("deep", 1200, "reuse `--from-risk risk.json` or schedule nightly"),
992
+ "verify --init": ("repo-wide", 600, "derive contracts outside pre-commit"),
993
+ "verify": ("core", 60, "run with a larger CI budget or narrow the contract set"),
994
+ "migrate-check": ("repo-wide", 600, "run inventory in CI and deep migration checks nightly"),
995
+ }
996
+
997
+
998
+ def _analysis_budget(command: str) -> dict[str, Any]:
999
+ cls, recommended, fallback = _ANALYSIS_CLASSES.get(
1000
+ command, ("repo-wide", 600, "run this command in a supervised or nightly job")
1001
+ )
1002
+ configured: Optional[float] = None
1003
+ raw = os.environ.get("ASK_MAX_ANALYSIS_SECONDS")
1004
+ if raw:
1005
+ try:
1006
+ configured = float(raw)
1007
+ except ValueError:
1008
+ configured = None
1009
+ return {
1010
+ "class": cls,
1011
+ "recommended_min_seconds": recommended,
1012
+ "configured_max_seconds": configured,
1013
+ "fallback": fallback,
1014
+ }
1015
+
1016
+
1017
+ def _enforce_analysis_budget(command: str, budget: dict[str, Any]) -> None:
1018
+ configured = budget.get("configured_max_seconds")
1019
+ recommended = float(budget.get("recommended_min_seconds") or 0)
1020
+ if configured is None or float(configured) >= recommended:
1021
+ return
1022
+ _emit_error_json(
1023
+ INVALID_INPUT_CODE,
1024
+ (
1025
+ f"{command} is a {budget.get('class')} analysis and the configured "
1026
+ f"budget ({configured:g}s) is below its safe floor ({recommended:g}s)."
1027
+ ),
1028
+ hint=str(budget.get("fallback") or "raise ASK_MAX_ANALYSIS_SECONDS"),
1029
+ expected=f"ASK_MAX_ANALYSIS_SECONDS >= {recommended:g}",
1030
+ )
1031
+ raise typer.Exit(code=2)
971
1032
 
972
1033
 
973
1034
  def _analysis_lock_path(path: Path) -> Path:
@@ -1051,8 +1112,14 @@ def _emit_analysis_contention_warning(command: str, path: Path, phase: str) -> N
1051
1112
  @contextlib.contextmanager
1052
1113
  def _expensive_analysis_scope(command: str, path: Path, phase: str):
1053
1114
  """Register a long repository-wide analysis and warn on concurrent runs."""
1115
+ from sourcecode.runs import RunRecord, reset_active_run, set_active_run
1116
+
1054
1117
  lock_path = _analysis_lock_path(path)
1055
1118
  _emit_analysis_contention_warning(command, path, phase)
1119
+ budget = _analysis_budget(command)
1120
+ _enforce_analysis_budget(command, budget)
1121
+ run_record = RunRecord.start(path, command, phase, budget=budget)
1122
+ run_token = set_active_run(run_record)
1056
1123
  token = f"{os.getpid()}:{time.time_ns()}"
1057
1124
  owns_lock = False
1058
1125
  if _active_analysis_record(lock_path) is None:
@@ -1077,6 +1144,12 @@ def _expensive_analysis_scope(command: str, path: Path, phase: str):
1077
1144
  owns_lock = False
1078
1145
  try:
1079
1146
  yield
1147
+ if run_record is not None:
1148
+ run_record.write(status="complete", exit_code=0)
1149
+ except BaseException as exc:
1150
+ if run_record is not None:
1151
+ run_record.write(status="failed", exit_code=1, error=str(exc))
1152
+ raise
1080
1153
  finally:
1081
1154
  if owns_lock:
1082
1155
  try:
@@ -1088,6 +1161,7 @@ def _expensive_analysis_scope(command: str, path: Path, phase: str):
1088
1161
  lock_path.unlink()
1089
1162
  except OSError:
1090
1163
  pass
1164
+ reset_active_run(run_token)
1091
1165
 
1092
1166
 
1093
1167
  def _emit_command_output(
@@ -8026,6 +8100,11 @@ def audit_report_cmd(
8026
8100
  format: str = typer.Option(
8027
8101
  "json", "--format", "-f", help="Output format: json, yaml or markdown."
8028
8102
  ),
8103
+ from_risk: Optional[Path] = typer.Option(
8104
+ None,
8105
+ "--from-risk",
8106
+ help="Reuse an existing `ask risk -o risk.json` payload instead of recomputing risk.",
8107
+ ),
8029
8108
  copy: bool = _copy_option(),
8030
8109
  ) -> None:
8031
8110
  """[EXPERIMENTAL] Human audit bundle over existing ASK evidence, optionally signed.
@@ -8044,6 +8123,7 @@ def audit_report_cmd(
8044
8123
  \b
8045
8124
  Examples:
8046
8125
  ask audit-report .
8126
+ ask risk . -o risk.json && ask audit-report . --from-risk risk.json
8047
8127
  ask audit-report . --profile prod --format markdown
8048
8128
  ask audit-report . --sign-key audit.key -o audit-report.json
8049
8129
  """
@@ -8079,12 +8159,42 @@ def audit_report_cmd(
8079
8159
  expected="a non-empty key file",
8080
8160
  )
8081
8161
  raise typer.Exit(code=1)
8162
+ risk_payload = None
8163
+ if from_risk is not None:
8164
+ risk_path = Path(from_risk).expanduser()
8165
+ if not risk_path.is_file():
8166
+ _emit_error_json(
8167
+ INVALID_INPUT_CODE,
8168
+ f"--from-risk must name a readable risk JSON file (got {str(from_risk)!r}).",
8169
+ hint="Example: ask risk . -o risk.json && ask audit-report . --from-risk risk.json",
8170
+ expected="an existing JSON file produced by `ask risk`",
8171
+ )
8172
+ raise typer.Exit(code=1)
8173
+ try:
8174
+ risk_payload = json.loads(risk_path.read_text(encoding="utf-8"))
8175
+ except Exception as exc:
8176
+ _emit_error_json(
8177
+ INVALID_INPUT_CODE,
8178
+ f"--from-risk is not valid JSON: {exc}",
8179
+ hint="Regenerate it with: ask risk . -o risk.json",
8180
+ expected="a JSON object produced by `ask risk`",
8181
+ )
8182
+ raise typer.Exit(code=1)
8183
+ if not isinstance(risk_payload, dict) or "risks" not in risk_payload:
8184
+ _emit_error_json(
8185
+ INVALID_INPUT_CODE,
8186
+ "--from-risk does not look like an `ask risk` payload.",
8187
+ hint="Regenerate it with: ask risk . -o risk.json",
8188
+ expected="a JSON object with a `risks` array",
8189
+ )
8190
+ raise typer.Exit(code=1)
8082
8191
 
8083
8192
  _prog = Progress()
8084
8193
  _prog.start("packaging audit evidence")
8085
8194
  try:
8086
8195
  data = build_audit_report(
8087
8196
  path, profiles=_profile_set(profile), sign_key=key_bytes,
8197
+ risk_payload=risk_payload,
8088
8198
  )
8089
8199
  finally:
8090
8200
  _prog.stop()
@@ -3008,6 +3008,7 @@ def run_migrate_check(
3008
3008
  "target_jdk": target_jdk,
3009
3009
  "keep_boot": keep_boot,
3010
3010
  },
3011
+ include_generated_at=False,
3011
3012
  )
3012
3013
  return report
3013
3014
 
@@ -1,6 +1,7 @@
1
- """Lightweight terminal progress indicator for long-running commands.
1
+ """Lightweight progress indicator for long-running commands.
2
2
 
3
- Writes only to stderr. Zero-cost when stderr is not a TTY or CI is detected.
3
+ Writes only to stderr. Zero-cost when stderr is not a TTY or CI is detected,
4
+ unless ASK_PROGRESS=1 explicitly requests line heartbeats for supervised jobs.
4
5
  Thread-safe; stop() is idempotent.
5
6
  """
6
7
  from __future__ import annotations
@@ -15,6 +16,8 @@ _FRAMES = ("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏")
15
16
 
16
17
 
17
18
  def _terminal_capable() -> bool:
19
+ if os.environ.get("ASK_PROGRESS", "").lower() in ("1", "true", "yes", "on"):
20
+ return True
18
21
  if not sys.stderr.isatty():
19
22
  return False
20
23
  if os.environ.get("TERM") in ("dumb", ""):
@@ -43,6 +46,11 @@ class Progress:
43
46
 
44
47
  def __init__(self) -> None:
45
48
  self._enabled = _terminal_capable()
49
+ self._line_mode = self._enabled and not sys.stderr.isatty()
50
+ try:
51
+ self._interval = max(0.5, float(os.environ.get("ASK_PROGRESS_INTERVAL", "5")))
52
+ except ValueError:
53
+ self._interval = 5.0
46
54
  self._phase = ""
47
55
  self._t0 = time.monotonic()
48
56
  self._lock = threading.Lock()
@@ -81,7 +89,8 @@ class Progress:
81
89
  self._stop_event.set()
82
90
  if self._thread:
83
91
  self._thread.join(timeout=1.0)
84
- sys.stderr.write("\r\033[K")
92
+ if not self._line_mode:
93
+ sys.stderr.write("\r\033[K")
85
94
  sys.stderr.flush()
86
95
  return elapsed
87
96
 
@@ -91,17 +100,23 @@ class Progress:
91
100
  if not self._enabled:
92
101
  return
93
102
  t = f"{elapsed:.1f}s" if elapsed < 60 else f"{elapsed / 60:.1f}m"
94
- sys.stderr.write(f"✓ done ({t})\n")
103
+ prefix = "[ask] " if self._line_mode else "✓ "
104
+ sys.stderr.write(f"{prefix}done ({t})\n")
95
105
  sys.stderr.flush()
96
106
 
97
107
  def _loop(self) -> None:
98
108
  idx = 0
99
- while not self._stop_event.wait(timeout=0.08):
109
+ interval = self._interval if self._line_mode else 0.08
110
+ while not self._stop_event.wait(timeout=interval):
100
111
  frame = _FRAMES[idx % len(_FRAMES)]
101
112
  elapsed = time.monotonic() - self._t0
102
113
  with self._lock:
103
114
  phase = self._phase
104
- line = f"\r{frame} {phase} ({elapsed:.1f}s)"
115
+ if self._line_mode:
116
+ t = f"{elapsed:.1f}s" if elapsed < 60 else f"{elapsed / 60:.1f}m"
117
+ line = f"[ask] progress phase={phase} elapsed={t}\n"
118
+ else:
119
+ line = f"\r{frame} {phase} ({elapsed:.1f}s)"
105
120
  try:
106
121
  sys.stderr.write(line)
107
122
  sys.stderr.flush()
@@ -162,6 +162,7 @@ def build_evidence_manifest(
162
162
  command: str,
163
163
  root: Path,
164
164
  inputs: Optional[dict[str, Any]] = None,
165
+ include_generated_at: bool = True,
165
166
  ) -> dict[str, Any]:
166
167
  """Build a machine-readable provenance index for an existing payload."""
167
168
  root = Path(root).resolve()
@@ -173,17 +174,19 @@ def build_evidence_manifest(
173
174
  for claim in claims
174
175
  if (claim.get("source") or {}).get("path")
175
176
  })
177
+ run: dict[str, Any] = {
178
+ "command": command,
179
+ "tool": "ask",
180
+ "tool_version": __version__,
181
+ "repository": str(root),
182
+ "git_head": _git_head(root),
183
+ "inputs": inputs or {},
184
+ }
185
+ if include_generated_at:
186
+ run["generated_at"] = datetime.now(timezone.utc).isoformat()
176
187
  return {
177
188
  "schema_version": PROVENANCE_SCHEMA,
178
- "run": {
179
- "command": command,
180
- "tool": "ask",
181
- "tool_version": __version__,
182
- "repository": str(root),
183
- "git_head": _git_head(root),
184
- "generated_at": datetime.now(timezone.utc).isoformat(),
185
- "inputs": inputs or {},
186
- },
189
+ "run": run,
187
190
  "claims": claims,
188
191
  "sources": [{"path": path} for path in sources],
189
192
  "claims_cap": (
@@ -27,7 +27,7 @@ from typing import Any, Optional
27
27
  #: The date `__version__` was released, from that version's CHANGELOG entry.
28
28
  #: `tests/test_release_info.py` fails the build if the two disagree, so this is
29
29
  #: a copy of a fact rather than a second authority for it.
30
- RELEASE_DATE = "2026-08-04"
30
+ RELEASE_DATE = "2026-08-05"
31
31
 
32
32
  #: Releases per day, measured over the 30 releases before this one (the same
33
33
  #: computation the test re-runs against the CHANGELOG). Stable across window
@@ -6975,6 +6975,27 @@ def compute_blast_radius(
6975
6975
  )
6976
6976
  except Exception:
6977
6977
  _impl_graph = None
6978
+ if _impl_graph is not None:
6979
+ _class_fqns = {
6980
+ n.get("fqn") or ""
6981
+ for n in graph_nodes
6982
+ if (n.get("symbol_kind") or n.get("type"))
6983
+ in {"class", "interface", "enum", "record", "annotation"}
6984
+ }
6985
+
6986
+ def _seed_class(_sym: str) -> str:
6987
+ return _sym if _sym in _class_fqns else _enclosing_class(_sym)
6988
+
6989
+ _seed_set = set(matched_fqns)
6990
+ _expanded = set(matched_fqns)
6991
+ _symbols_by_class: dict[str, list[str]] = {}
6992
+ for _sym in reverse_graph:
6993
+ _symbols_by_class.setdefault(_seed_class(_sym), []).append(_sym)
6994
+ for _seed in sorted(_seed_set):
6995
+ for _subtype in getattr(_impl_graph, "all_subtypes_of")(_seed_class(_seed)):
6996
+ for _sym in _symbols_by_class.get(_subtype, ()):
6997
+ _expanded.add(_sym)
6998
+ matched_fqns = _expanded
6978
6999
  for seed in matched_fqns:
6979
7000
  _seed_callers = _all_callers_from_rg(seed, reverse_graph, _rg_index, _impl_graph)
6980
7001
  if len(_seed_callers) > _HUB_CALLER_THRESHOLD and max_depth > 1: