sourcecode 4.9.0__tar.gz → 4.9.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 (217) hide show
  1. {sourcecode-4.9.0 → sourcecode-4.9.1}/CHANGELOG.md +12 -0
  2. {sourcecode-4.9.0 → sourcecode-4.9.1}/PKG-INFO +17 -6
  3. {sourcecode-4.9.0 → sourcecode-4.9.1}/README.md +16 -5
  4. {sourcecode-4.9.0 → sourcecode-4.9.1}/pyproject.toml +1 -1
  5. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/audit_report.py +11 -0
  7. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/cli.py +34 -6
  8. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/migrate_check.py +307 -1
  9. sourcecode-4.9.1/src/sourcecode/provenance.py +202 -0
  10. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/risk.py +11 -0
  11. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/sarif.py +12 -0
  12. {sourcecode-4.9.0 → sourcecode-4.9.1}/.github/workflows/build-windows.yml +0 -0
  13. {sourcecode-4.9.0 → sourcecode-4.9.1}/.gitignore +0 -0
  14. {sourcecode-4.9.0 → sourcecode-4.9.1}/.ruff.toml +0 -0
  15. {sourcecode-4.9.0 → sourcecode-4.9.1}/CLAUDE.md +0 -0
  16. {sourcecode-4.9.0 → sourcecode-4.9.1}/CONTRIBUTING.md +0 -0
  17. {sourcecode-4.9.0 → sourcecode-4.9.1}/LICENSE +0 -0
  18. {sourcecode-4.9.0 → sourcecode-4.9.1}/SECURITY.md +0 -0
  19. {sourcecode-4.9.0 → sourcecode-4.9.1}/raw +0 -0
  20. {sourcecode-4.9.0 → sourcecode-4.9.1}/scripts/compare_integration_engines.py +0 -0
  21. {sourcecode-4.9.0 → sourcecode-4.9.1}/scripts/customer_smoke_test.sh +0 -0
  22. {sourcecode-4.9.0 → sourcecode-4.9.1}/scripts/generate_jdk_exports.py +0 -0
  23. {sourcecode-4.9.0 → sourcecode-4.9.1}/scripts/perf_harness.py +0 -0
  24. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/adaptive_scanner.py +0 -0
  25. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/archetype.py +0 -0
  26. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/architectural_baseline.py +0 -0
  27. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/architectural_delta.py +0 -0
  28. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/architecture_analyzer.py +0 -0
  29. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/architecture_summary.py +0 -0
  30. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/ast_extractor.py +0 -0
  31. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/baseline_autocapture.py +0 -0
  32. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/cache.py +0 -0
  33. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/cache_model.py +0 -0
  34. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/call_surface.py +0 -0
  35. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/caller_metrics.py +0 -0
  36. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/caller_reach.py +0 -0
  37. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/canonical_ir.py +0 -0
  38. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/chain_rules.py +0 -0
  39. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/change_plan.py +0 -0
  40. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/cir_graphs.py +0 -0
  41. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/classifier.py +0 -0
  42. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/client_calls.py +0 -0
  43. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/code_notes_analyzer.py +0 -0
  44. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/compare.py +0 -0
  45. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/confidence_analyzer.py +0 -0
  46. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/constraint_diff.py +0 -0
  47. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/context_cache.py +0 -0
  48. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/context_graph.py +0 -0
  49. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/context_scorer.py +0 -0
  50. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/context_summarizer.py +0 -0
  51. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/contract_diff.py +0 -0
  52. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/contract_init.py +0 -0
  53. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/contract_model.py +0 -0
  54. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/contract_pipeline.py +0 -0
  55. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/coverage_parser.py +0 -0
  56. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/data_exposure.py +0 -0
  57. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/data_labels.py +0 -0
  58. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/defect_identity.py +0 -0
  59. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/degradation.py +0 -0
  60. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/dependency_analyzer.py +0 -0
  61. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/deployment_prefix.py +0 -0
  62. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/__init__.py +0 -0
  63. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/base.py +0 -0
  64. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/csproj_parser.py +0 -0
  65. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/dart.py +0 -0
  66. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/dotnet.py +0 -0
  67. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/elixir.py +0 -0
  68. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/go.py +0 -0
  69. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/heuristic.py +0 -0
  70. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/hybrid.py +0 -0
  71. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/java.py +0 -0
  72. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/jvm_ext.py +0 -0
  73. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/nodejs.py +0 -0
  74. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/parsers.py +0 -0
  75. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/php.py +0 -0
  76. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/project.py +0 -0
  77. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/python.py +0 -0
  78. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/ruby.py +0 -0
  79. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/rust.py +0 -0
  80. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/systems.py +0 -0
  81. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/terraform.py +0 -0
  82. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/detectors/tooling.py +0 -0
  83. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/doc_analyzer.py +0 -0
  84. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/dynamic_argument_surface.py +0 -0
  85. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/endpoint_literals.py +0 -0
  86. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/endpoint_metrics.py +0 -0
  87. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/entrypoint_classifier.py +0 -0
  88. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/env_analyzer.py +0 -0
  89. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/envelope.py +0 -0
  90. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/environment_resolution.py +0 -0
  91. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/error_schema.py +0 -0
  92. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/evidence_provider.py +0 -0
  93. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/explain.py +0 -0
  94. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/facts/__init__.py +0 -0
  95. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/facts/registry.json +0 -0
  96. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/file_chunker.py +0 -0
  97. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/file_classifier.py +0 -0
  98. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/filter_surface.py +0 -0
  99. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/format_contract.py +0 -0
  100. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/fqn_utils.py +0 -0
  101. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/git_analyzer.py +0 -0
  102. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/git_checkout.py +0 -0
  103. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/graph_analyzer.py +0 -0
  104. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/graph_evidence.py +0 -0
  105. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/hibernate_strat.py +0 -0
  106. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/identity_fallback.py +0 -0
  107. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/integration_coordinates.py +0 -0
  108. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/jdk_exports.py +0 -0
  109. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/license.py +0 -0
  110. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp/__init__.py +0 -0
  111. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  112. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  113. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  114. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  115. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  116. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp/orchestrator.py +0 -0
  117. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp/registry.py +0 -0
  118. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp/runner.py +0 -0
  119. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp/server.py +0 -0
  120. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/mcp_nudge.py +0 -0
  121. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/metrics_analyzer.py +0 -0
  122. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/migration_blast.py +0 -0
  123. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/non_coverage.py +0 -0
  124. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/openapi_surface.py +0 -0
  125. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/openrewrite_recipe.py +0 -0
  126. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/output_budget.py +0 -0
  127. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/parse_cache.py +0 -0
  128. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/path_admission.py +0 -0
  129. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/path_filters.py +0 -0
  130. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/perf.py +0 -0
  131. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/pipe_contract.py +0 -0
  132. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/posture.py +0 -0
  133. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/pr_comment_renderer.py +0 -0
  134. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/pr_impact.py +0 -0
  135. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/prepare_context.py +0 -0
  136. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/progress.py +0 -0
  137. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/ranking_engine.py +0 -0
  138. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/readiness_timeline.py +0 -0
  139. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/reconciliation.py +0 -0
  140. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/redactor.py +0 -0
  141. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/reference_facts.py +0 -0
  142. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/release_info.py +0 -0
  143. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/relevance_scorer.py +0 -0
  144. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/remedies.py +0 -0
  145. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/rename_refactor.py +0 -0
  146. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/repo_classifier.py +0 -0
  147. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/repository_ir.py +0 -0
  148. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/__init__.py +0 -0
  149. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/context.py +0 -0
  150. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/errors.py +0 -0
  151. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/executor.py +0 -0
  152. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/planner.py +0 -0
  153. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/query.py +0 -0
  154. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/request.py +0 -0
  155. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/resolution.py +0 -0
  156. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/result.py +0 -0
  157. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/retriever.py +0 -0
  158. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/runtime.py +0 -0
  159. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/steps.py +0 -0
  160. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  161. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/steps_graph.py +0 -0
  162. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/steps_impact.py +0 -0
  163. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/steps_intf.py +0 -0
  164. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/steps_struct.py +0 -0
  165. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  166. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/ris.py +0 -0
  167. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/rule_catalog.py +0 -0
  168. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/runtime_classifier.py +0 -0
  169. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/scanner.py +0 -0
  170. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/schema.py +0 -0
  171. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  172. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/security_config.py +0 -0
  173. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/security_config_scan.py +0 -0
  174. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/security_posture.py +0 -0
  175. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/semantic_analyzer.py +0 -0
  176. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/semantic_impact_engine.py +0 -0
  177. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/semantic_integration_engine.py +0 -0
  178. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/semantic_services.py +0 -0
  179. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/serializer.py +0 -0
  180. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/servlet_surface.py +0 -0
  181. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/source_text.py +0 -0
  182. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/spring_event_topology.py +0 -0
  183. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/spring_findings.py +0 -0
  184. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/spring_impact.py +0 -0
  185. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/spring_model.py +0 -0
  186. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/spring_profiles.py +0 -0
  187. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/spring_properties.py +0 -0
  188. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/spring_security_audit.py +0 -0
  189. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/spring_semantic.py +0 -0
  190. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/spring_tx_analyzer.py +0 -0
  191. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/summarizer.py +0 -0
  192. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/target_admission.py +0 -0
  193. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/telemetry/__init__.py +0 -0
  194. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/telemetry/config.py +0 -0
  195. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/telemetry/consent.py +0 -0
  196. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/telemetry/events.py +0 -0
  197. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/telemetry/filters.py +0 -0
  198. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/telemetry/transport.py +0 -0
  199. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/test_gap_ranking.py +0 -0
  200. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/test_sources.py +0 -0
  201. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/token_estimate.py +0 -0
  202. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/tree_utils.py +0 -0
  203. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/type_usage_surface.py +0 -0
  204. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/validation_inference.py +0 -0
  205. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/validation_surface.py +0 -0
  206. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/verify_edit.py +0 -0
  207. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/verify_repo.py +0 -0
  208. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/verify_rules.py +0 -0
  209. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/version_check.py +0 -0
  210. {sourcecode-4.9.0 → sourcecode-4.9.1}/src/sourcecode/workspace.py +0 -0
  211. {sourcecode-4.9.0 → sourcecode-4.9.1}/supabase/functions/README.md +0 -0
  212. {sourcecode-4.9.0 → sourcecode-4.9.1}/supabase/functions/get-license/index.ts +0 -0
  213. {sourcecode-4.9.0 → sourcecode-4.9.1}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  214. {sourcecode-4.9.0 → sourcecode-4.9.1}/supabase/functions/telemetry/index.ts +0 -0
  215. {sourcecode-4.9.0 → sourcecode-4.9.1}/supabase/sql/license_event_ordering.sql +0 -0
  216. {sourcecode-4.9.0 → sourcecode-4.9.1}/supabase/sql/licensing_schema.sql +0 -0
  217. {sourcecode-4.9.0 → sourcecode-4.9.1}/supabase/sql/telemetry_events.sql +0 -0
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [4.9.1] — 2026-08-04
6
+
7
+ ### Added
8
+
9
+ - **Java LTS/licensing inventory in `migrate-check`.** Reports Java 8/11/17/21/25
10
+ evidence from Maven/Gradle, local runtime hints and container files, target-LTS
11
+ blockers via `--target-jdk`, and explicit Oracle JDK licensing-review signals
12
+ without changing migration readiness scoring.
13
+ - **Claim provenance ledger.** `risk`, `enrich`, `migrate-check`, `audit-report`
14
+ and `prepare-context` now emit `evidence_manifest` with stable claim IDs,
15
+ source paths/lines, command inputs, ASK version, git HEAD and freshness metadata.
16
+
5
17
  ## [4.9.0] — 2026-08-04
6
18
 
7
19
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 4.9.0
3
+ Version: 4.9.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.0-blue)
45
+ ![Version](https://img.shields.io/badge/version-4.9.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`**
@@ -97,7 +97,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
97
97
  # pip / pipx
98
98
  pipx install sourcecode # or: pip install sourcecode
99
99
 
100
- ask version # ask 4.9.0 — and, on a build that has aged,
100
+ ask version # ask 4.9.1 — and, on a build that has aged,
101
101
  # how many releases have probably shipped since
102
102
  ```
103
103
 
@@ -137,8 +137,9 @@ ask endpoints /path/to/repo
137
137
  # CGLIB no-op), security surface, request-body validation.
138
138
  ask spring-audit /path/to/repo
139
139
 
140
- # Spring Boot 2→3 readiness: located blockers, per-dimension score, effort estimate.
140
+ # Spring Boot 2→3 readiness plus Java LTS/licensing inventory.
141
141
  ask migrate-check /path/to/repo --compact
142
+ # Add --target-jdk 25 to make the LTS route explicit.
142
143
  ```
143
144
 
144
145
  **Then the three that finish the sentence the four above start.** A profile-set answer stays
@@ -308,12 +309,12 @@ from it.
308
309
  | `posture` | experimental | which beans a profile set wires, and how effective endpoint access differs between two sets | the most differentiated capability here |
309
310
  | `risk` | experimental | what each defect actually costs, once reach, access, write effect and the shape of the input path are in it | `defect_severity × reachability × auth_verdict × write_effect × query_construction × input_constraints`; every factor names its authority; `--table --rule SEC-008 --band high --top-n 20` gives audit triage without `jq` |
310
311
  | `enrich` | experimental | another scanner's SARIF findings, ranked by what this repository does with them | `--sarif <log>`; same composition as `risk`, with `--table --rule <id> --band high --top-n 20` for triage |
311
- | `audit-report` | experimental | human audit bundle over existing `risk` + `posture` evidence, optionally signed | `--format markdown`; `--sign-key <file>` signs canonical JSON with HMAC-SHA256 |
312
+ | `audit-report` | experimental | human audit bundle over existing `risk` + `posture` evidence, optionally signed | includes `evidence_manifest` claim provenance; `--format markdown`; `--sign-key <file>` signs canonical JSON with HMAC-SHA256 |
312
313
  | `migrate-recipe` | experimental | the migration report as the OpenRewrite recipe that applies it | only recipes a finding named; the manual remainder published beside them; writes nothing without `--write`, and never a runnable command for an empty recipe list |
313
314
  | `data-exposure` | experimental | which routes can carry the data you labelled, and who reaches them | labels declared in `sourcecode.config.json` — never inferred from a name; `signature` and `call_reach` evidence published apart, field-level flow out of scope (NC-008). With nothing declared, `ask data-exposure /path/to/repo` answers `answered: false` and hands back the file, the key and an example to declare — it never reports zero exposed routes |
314
315
  | `endpoints` | core | every REST endpoint, effective path, security policy, confidence | Spring MVC + JAX-RS (~65 % recall on JAX-RS sub-resource locators). `--compact` answers the exposure census without the rows; `--servlets` lists the servlet-mounted surface as its own population; `--client-usage` says which routes the TS/JS client in this repository actually calls |
315
316
  | `spring-audit` | core | transactional anomalies + security surface + validation gaps | `--ci`, `-f github-comment`; `--table --rule SEC-008 --top-n 20` for human-scale review |
316
- | `migrate-check` | core | Boot 2→3 readiness: located blockers, per-dimension score, effort | `--blast-radius` orders the re-test plan; `--table --rule MIG-001 --band critical --top-n 20` for blocker triage |
317
+ | `migrate-check` | core | Boot 2→3 readiness + Java LTS/licensing inventory: located blockers, per-dimension score, effort, detected Java 8/11/17/21/25 evidence and explicit licensing-review signals | `--target-jdk 25` sets the target LTS route; `--blast-radius` orders the re-test plan; `--table --rule MIG-001 --band critical --top-n 20` for blocker triage |
317
318
  | `impact` / `impact-chain` | core | blast radius of a change, to the endpoints it reaches | target the **interface**, not the `Impl` |
318
319
  | `pr-impact` | core | the same, scoped to a PR diff | gating command: `--fail-on`, exit codes |
319
320
  | `verify` | core | does the repo satisfy its declared contracts, **relative to a baseline** | `.ask/contracts.yml`; exit 0/1/2 |
@@ -401,6 +402,16 @@ and that gap is the honest description, not a defect to argue with. `migrate-che
401
402
  the OpenRewrite recipe each finding carries so the executor that does apply changes has its
402
403
  input.
403
404
 
405
+ **Java LTS/licensing inventory.** `migrate-check` emits `java_lts_inventory` beside the migration
406
+ scores. It reads Maven/Gradle, local runtime hints and container files for Java 8/11/17/21/25
407
+ evidence, reports target-LTS blockers with `--target-jdk`, and marks explicit Oracle JDK evidence
408
+ as `licensing_review_needed`. This is inventory evidence, not legal advice.
409
+
410
+ **Claim provenance ledger.** JSON outputs from `risk`, `enrich`, `migrate-check`,
411
+ `audit-report` and `prepare-context` include `evidence_manifest`: stable claim IDs mapped to
412
+ source paths/lines, command inputs, ASK version, git HEAD and freshness metadata. The manifest
413
+ indexes claims already in the payload; it does not add analysis or certify compliance.
414
+
404
415
 
405
416
  ---
406
417
 
@@ -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.0-blue)
7
+ ![Version](https://img.shields.io/badge/version-4.9.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`**
@@ -59,7 +59,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
59
59
  # pip / pipx
60
60
  pipx install sourcecode # or: pip install sourcecode
61
61
 
62
- ask version # ask 4.9.0 — and, on a build that has aged,
62
+ ask version # ask 4.9.1 — and, on a build that has aged,
63
63
  # how many releases have probably shipped since
64
64
  ```
65
65
 
@@ -99,8 +99,9 @@ ask endpoints /path/to/repo
99
99
  # CGLIB no-op), security surface, request-body validation.
100
100
  ask spring-audit /path/to/repo
101
101
 
102
- # Spring Boot 2→3 readiness: located blockers, per-dimension score, effort estimate.
102
+ # Spring Boot 2→3 readiness plus Java LTS/licensing inventory.
103
103
  ask migrate-check /path/to/repo --compact
104
+ # Add --target-jdk 25 to make the LTS route explicit.
104
105
  ```
105
106
 
106
107
  **Then the three that finish the sentence the four above start.** A profile-set answer stays
@@ -270,12 +271,12 @@ from it.
270
271
  | `posture` | experimental | which beans a profile set wires, and how effective endpoint access differs between two sets | the most differentiated capability here |
271
272
  | `risk` | experimental | what each defect actually costs, once reach, access, write effect and the shape of the input path are in it | `defect_severity × reachability × auth_verdict × write_effect × query_construction × input_constraints`; every factor names its authority; `--table --rule SEC-008 --band high --top-n 20` gives audit triage without `jq` |
272
273
  | `enrich` | experimental | another scanner's SARIF findings, ranked by what this repository does with them | `--sarif <log>`; same composition as `risk`, with `--table --rule <id> --band high --top-n 20` for triage |
273
- | `audit-report` | experimental | human audit bundle over existing `risk` + `posture` evidence, optionally signed | `--format markdown`; `--sign-key <file>` signs canonical JSON with HMAC-SHA256 |
274
+ | `audit-report` | experimental | human audit bundle over existing `risk` + `posture` evidence, optionally signed | includes `evidence_manifest` claim provenance; `--format markdown`; `--sign-key <file>` signs canonical JSON with HMAC-SHA256 |
274
275
  | `migrate-recipe` | experimental | the migration report as the OpenRewrite recipe that applies it | only recipes a finding named; the manual remainder published beside them; writes nothing without `--write`, and never a runnable command for an empty recipe list |
275
276
  | `data-exposure` | experimental | which routes can carry the data you labelled, and who reaches them | labels declared in `sourcecode.config.json` — never inferred from a name; `signature` and `call_reach` evidence published apart, field-level flow out of scope (NC-008). With nothing declared, `ask data-exposure /path/to/repo` answers `answered: false` and hands back the file, the key and an example to declare — it never reports zero exposed routes |
276
277
  | `endpoints` | core | every REST endpoint, effective path, security policy, confidence | Spring MVC + JAX-RS (~65 % recall on JAX-RS sub-resource locators). `--compact` answers the exposure census without the rows; `--servlets` lists the servlet-mounted surface as its own population; `--client-usage` says which routes the TS/JS client in this repository actually calls |
277
278
  | `spring-audit` | core | transactional anomalies + security surface + validation gaps | `--ci`, `-f github-comment`; `--table --rule SEC-008 --top-n 20` for human-scale review |
278
- | `migrate-check` | core | Boot 2→3 readiness: located blockers, per-dimension score, effort | `--blast-radius` orders the re-test plan; `--table --rule MIG-001 --band critical --top-n 20` for blocker triage |
279
+ | `migrate-check` | core | Boot 2→3 readiness + Java LTS/licensing inventory: located blockers, per-dimension score, effort, detected Java 8/11/17/21/25 evidence and explicit licensing-review signals | `--target-jdk 25` sets the target LTS route; `--blast-radius` orders the re-test plan; `--table --rule MIG-001 --band critical --top-n 20` for blocker triage |
279
280
  | `impact` / `impact-chain` | core | blast radius of a change, to the endpoints it reaches | target the **interface**, not the `Impl` |
280
281
  | `pr-impact` | core | the same, scoped to a PR diff | gating command: `--fail-on`, exit codes |
281
282
  | `verify` | core | does the repo satisfy its declared contracts, **relative to a baseline** | `.ask/contracts.yml`; exit 0/1/2 |
@@ -363,6 +364,16 @@ and that gap is the honest description, not a defect to argue with. `migrate-che
363
364
  the OpenRewrite recipe each finding carries so the executor that does apply changes has its
364
365
  input.
365
366
 
367
+ **Java LTS/licensing inventory.** `migrate-check` emits `java_lts_inventory` beside the migration
368
+ scores. It reads Maven/Gradle, local runtime hints and container files for Java 8/11/17/21/25
369
+ evidence, reports target-LTS blockers with `--target-jdk`, and marks explicit Oracle JDK evidence
370
+ as `licensing_review_needed`. This is inventory evidence, not legal advice.
371
+
372
+ **Claim provenance ledger.** JSON outputs from `risk`, `enrich`, `migrate-check`,
373
+ `audit-report` and `prepare-context` include `evidence_manifest`: stable claim IDs mapped to
374
+ source paths/lines, command inputs, ASK version, git HEAD and freshness metadata. The manifest
375
+ indexes claims already in the payload; it does not add analysis or certify compliance.
376
+
366
377
 
367
378
  ---
368
379
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "4.9.0"
7
+ version = "4.9.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.0"
7
+ __version__ = "4.9.1"
@@ -78,6 +78,17 @@ def build_audit_report(
78
78
  "non_coverage": (risk.get("non_coverage") or {}).get("items", []),
79
79
  },
80
80
  }
81
+ from sourcecode.provenance import build_evidence_manifest
82
+ unsigned["evidence_manifest"] = build_evidence_manifest(
83
+ unsigned,
84
+ command="audit-report",
85
+ root=root,
86
+ inputs={
87
+ "profiles": sorted(profiles) if profiles else None,
88
+ "risk_limit": risk_limit,
89
+ "signed": bool(sign_key),
90
+ },
91
+ )
81
92
  # Sign and publish the same JSON-normalized material. Some embedded evidence
82
93
  # may contain tuples or other JSON-coercible values; normalizing before signing
83
94
  # avoids a signature over an in-memory shape the user never receives.
@@ -345,7 +345,7 @@ of files) in minutes. Semantic analysis itself is sub-second; repo indexing domi
345
345
  posture . --diff dev:prod [dim]# effective access, two profile sets (exp.)[/dim]
346
346
  endpoints . [dim]# endpoints + effective path + policy[/dim]
347
347
  spring-audit . [dim]# TX anomalies + security surface[/dim]
348
- migrate-check . --compact [dim]# Boot 2→3: located blockers + effort[/dim]
348
+ migrate-check . --compact [dim]# Boot 2→3 + Java LTS/licensing inventory[/dim]
349
349
  risk . [dim]# defects ranked by reach × access (exp.)[/dim]
350
350
 
351
351
  {_remedy_help_block()}
@@ -3875,6 +3875,11 @@ def prepare_context_cmd(
3875
3875
  ask prepare-context review-pr . --since main --output review.json
3876
3876
  ask prepare-context onboard --llm-prompt
3877
3877
  ask prepare-context --task-help
3878
+
3879
+ \b
3880
+ Provenance:
3881
+ JSON output includes evidence_manifest: claim IDs for context files and
3882
+ source-backed rows, plus command inputs, ASK version, git HEAD and freshness.
3878
3883
  """
3879
3884
  from sourcecode.prepare_context import TASKS, TaskContextBuilder
3880
3885
 
@@ -4419,6 +4424,20 @@ def prepare_context_cmd(
4419
4424
  _skipped.append("test_gap_discovery")
4420
4425
  out["skipped_analyzers"] = _skipped
4421
4426
 
4427
+ from sourcecode.provenance import build_evidence_manifest as _build_evidence_manifest
4428
+ out["evidence_manifest"] = _build_evidence_manifest(
4429
+ out,
4430
+ command="prepare-context",
4431
+ root=target,
4432
+ inputs={
4433
+ "task": task,
4434
+ "since": since,
4435
+ "format": format or "json",
4436
+ "fast": fast,
4437
+ "llm_prompt": llm_prompt,
4438
+ },
4439
+ )
4440
+
4422
4441
  # P0-1: Apply output budget per task — safety net for large repos.
4423
4442
  _pc_budget = _prepare_context_budget(task)
4424
4443
 
@@ -8015,7 +8034,8 @@ def audit_report_cmd(
8015
8034
  This packages the same evidence `risk` and `posture` already publish into a
8016
8035
  report a buyer can read: top risks, effective runtime posture, source-linked
8017
8036
  evidence and non-coverage. It does not certify compliance, choose fixes or
8018
- assign ROI.
8037
+ assign ROI. JSON includes evidence_manifest: claim IDs mapped to source spans,
8038
+ command inputs, ASK version and freshness.
8019
8039
 
8020
8040
  \b
8021
8041
  `--sign-key` adds an HMAC-SHA256 signature over the canonical JSON payload.
@@ -8724,9 +8744,9 @@ def migrate_check_cmd(
8724
8744
  None,
8725
8745
  "--target-jdk",
8726
8746
  help=(
8727
- "The JDK you are actually moving to (e.g. 11, 17, 21). Rules for a "
8728
- "later JDK are not your blockers and are not reported. Without it, "
8729
- "every JDK axis is reported at once."
8747
+ "The JDK you are actually moving to (e.g. 11, 17, 21, 25). Rules "
8748
+ "for a later JDK are not your blockers and are not reported. Also "
8749
+ "sets java_lts_inventory.target_lts."
8730
8750
  ),
8731
8751
  ),
8732
8752
  keep_boot: bool = typer.Option(
@@ -8786,7 +8806,7 @@ def migrate_check_cmd(
8786
8806
  help="Caller BFS depth for --blast-radius (1-8, default: 4).",
8787
8807
  ),
8788
8808
  ) -> None:
8789
- """Spring Boot 2→3 migration readiness: detect javax→jakarta namespace blockers.
8809
+ """Spring Boot 2→3 migration readiness + Java LTS/licensing inventory.
8790
8810
 
8791
8811
  \b
8792
8812
  Detects:
@@ -8815,6 +8835,7 @@ def migrate_check_cmd(
8815
8835
  ask migrate-check . --compact bounded decision summary
8816
8836
  ask migrate-check /path/to/repo --format text
8817
8837
  ask migrate-check . --min-severity high
8838
+ ask migrate-check . --target-jdk 25 Java LTS target inventory
8818
8839
  ask migrate-check . --table --rule MIG-001 --band critical --top-n 20
8819
8840
  ask migrate-check . --output migration.json
8820
8841
  ask migrate-check . --snapshot --ref sprint-12 persist a readiness point
@@ -8837,6 +8858,13 @@ def migrate_check_cmd(
8837
8858
  <repo>/.ask/readiness-history). --trend reads that series and reports
8838
8859
  first→last movement — no improving/degrading label, and readiness_score is
8839
8860
  flagged not-comparable when the applicable dimension set changed.
8861
+
8862
+ \b
8863
+ Java LTS/licensing inventory:
8864
+ The JSON report includes java_lts_inventory: Java 8/11/17/21/25 evidence
8865
+ from Maven/Gradle, local runtime hints and container files; target-LTS
8866
+ blockers; and explicit Oracle JDK/licensing-review signals. This is
8867
+ inventory evidence, not legal advice, and it never changes readiness_score.
8840
8868
  """
8841
8869
  from sourcecode.repository_ir import find_java_files
8842
8870
  from sourcecode.migrate_check import run_migrate_check
@@ -860,6 +860,282 @@ def _parse_major(version: "Optional[str]") -> "Optional[int]":
860
860
  return None
861
861
  m = re.match(r"\s*(\d+)", version)
862
862
  return int(m.group(1)) if m else None
863
+
864
+
865
+ _MAVEN_JAVA_VERSION_RE: re.Pattern = re.compile(
866
+ r"<(java\.version|maven\.compiler\.(?:source|target|release))>\s*([^<${}]+)\s*</\1>",
867
+ re.IGNORECASE,
868
+ )
869
+ _MAVEN_COMPILER_TAG_RE: re.Pattern = re.compile(
870
+ r"<(?:source|target|release)>\s*([^<${}]+)\s*</(?:source|target|release)>",
871
+ re.IGNORECASE,
872
+ )
873
+ _GRADLE_JAVA_ENUM_RE: re.Pattern = re.compile(
874
+ r"JavaVersion\.VERSION_(?:1_)?(\d+)", re.IGNORECASE
875
+ )
876
+ _GRADLE_JAVA_ASSIGN_RE: re.Pattern = re.compile(
877
+ r"(?:sourceCompatibility|targetCompatibility|javaVersion|languageVersion)\s*=?\s*['\"]?(\d+(?:\.\d+)?)",
878
+ re.IGNORECASE,
879
+ )
880
+ _GRADLE_TOOLCHAIN_RE: re.Pattern = re.compile(
881
+ r"JavaLanguageVersion\.of\s*\(\s*(\d+)\s*\)", re.IGNORECASE
882
+ )
883
+ _JAVA_VERSION_LINE_RE: re.Pattern = re.compile(
884
+ r"(?:JAVA_VERSION|JDK_VERSION|java\.version)\s*[=:]\s*['\"]?(\d+(?:\.\d+)?)",
885
+ re.IGNORECASE,
886
+ )
887
+ _DOCKER_FROM_RE: re.Pattern = re.compile(
888
+ r"^\s*FROM\s+(?:--platform=\S+\s+)?(\S+)", re.IGNORECASE | re.MULTILINE
889
+ )
890
+ _DOCKER_JAVA_TAG_RE: re.Pattern = re.compile(
891
+ r"(?:^|[:\-_])(?:jdk|jre|java)?(?:-)?(1\.8|8|11|17|21|25)(?:[.\-_a-z0-9]*|$)",
892
+ re.IGNORECASE,
893
+ )
894
+ _JAVA_LTS_RELEASES: frozenset[int] = frozenset({8, 11, 17, 21, 25})
895
+ _JAVA_LTS_SUPPORT_FILE_NAMES: frozenset[str] = frozenset({
896
+ "gradle.properties", ".java-version", ".sdkmanrc", "Dockerfile",
897
+ "dockerfile", "compose.yaml", "compose.yml", "docker-compose.yaml",
898
+ "docker-compose.yml", "jvm.config",
899
+ })
900
+
901
+
902
+ def _normalize_java_major(value: str) -> Optional[int]:
903
+ """Normalize Java version spellings (`1.8`, `VERSION_17`, `21.0.2`) to major."""
904
+ value = value.strip().strip("'\"")
905
+ if value.startswith("1."):
906
+ parts = value.split(".")
907
+ if len(parts) > 1 and parts[1].isdigit():
908
+ return int(parts[1])
909
+ m = re.match(r"(\d+)", value)
910
+ if not m:
911
+ return None
912
+ return int(m.group(1))
913
+
914
+
915
+ def _java_lts_name(major: Optional[int]) -> str:
916
+ if major is None:
917
+ return "unknown"
918
+ return f"Java {major}" + (" LTS" if major in _JAVA_LTS_RELEASES else "")
919
+
920
+
921
+ def _java_distribution_from_text(text: str) -> Optional[str]:
922
+ low = text.lower()
923
+ for marker, name in (
924
+ ("eclipse-temurin", "Eclipse Temurin"),
925
+ ("adoptium", "Eclipse Temurin"),
926
+ ("amazoncorretto", "Amazon Corretto"),
927
+ ("corretto", "Amazon Corretto"),
928
+ ("liberica", "BellSoft Liberica"),
929
+ ("azul", "Azul Zulu"),
930
+ ("zulu", "Azul Zulu"),
931
+ ("graalvm", "GraalVM"),
932
+ ("oracle", "Oracle JDK"),
933
+ ("openjdk", "OpenJDK"),
934
+ ):
935
+ if marker in low:
936
+ return name
937
+ return None
938
+
939
+
940
+ def _java_evidence(
941
+ *,
942
+ source: str,
943
+ path: str,
944
+ value: str,
945
+ version: Optional[int],
946
+ distribution: Optional[str] = None,
947
+ ) -> dict:
948
+ out = {
949
+ "source": source,
950
+ "path": path,
951
+ "value": value,
952
+ "version": version,
953
+ "version_label": _java_lts_name(version),
954
+ }
955
+ if distribution:
956
+ out["distribution"] = distribution
957
+ return out
958
+
959
+
960
+ def _find_java_lts_support_files(root: Path) -> list[tuple[Path, str]]:
961
+ """Files that may declare build/runtime Java without executing the project."""
962
+ found: list[tuple[Path, str]] = []
963
+ for dirpath, dirnames, filenames in os.walk(root):
964
+ dirnames[:] = [d for d in dirnames if d not in _SKIP_DIRS or d == ".mvn"]
965
+ dp = Path(dirpath)
966
+ try:
967
+ rel_dir = dp.relative_to(root)
968
+ except ValueError:
969
+ continue
970
+ for fname in filenames:
971
+ if fname not in _JAVA_LTS_SUPPORT_FILE_NAMES:
972
+ continue
973
+ abs_path = dp / fname
974
+ rel = str(rel_dir / fname) if str(rel_dir) != "." else fname
975
+ found.append((abs_path, rel))
976
+ return found
977
+
978
+
979
+ def _java_lts_inventory(
980
+ root: Path,
981
+ build_texts: "Iterable[tuple[str, str]]",
982
+ *,
983
+ spring_boot_version: Optional[str],
984
+ target_jdk: Optional[int],
985
+ ) -> dict:
986
+ """Java LTS/licensing inventory for migration planning.
987
+
988
+ Bounded evidence only: build descriptors and local deployment hints. No build
989
+ execution, no legal verdict, and no inference from absent runtime metadata.
990
+ """
991
+ target_lts = target_jdk or 25
992
+ evidence: list[dict] = []
993
+ limitations: list[str] = []
994
+
995
+ for rel_path, raw_text in build_texts:
996
+ text = _resolve_maven_properties(raw_text) if rel_path.endswith("pom.xml") else raw_text
997
+ if rel_path.endswith("pom.xml"):
998
+ for m in _MAVEN_JAVA_VERSION_RE.finditer(text):
999
+ major = _normalize_java_major(m.group(2))
1000
+ evidence.append(_java_evidence(
1001
+ source=f"maven_property:{m.group(1)}",
1002
+ path=rel_path,
1003
+ value=m.group(2).strip(),
1004
+ version=major,
1005
+ ))
1006
+ for m in _MAVEN_COMPILER_TAG_RE.finditer(text):
1007
+ major = _normalize_java_major(m.group(1))
1008
+ evidence.append(_java_evidence(
1009
+ source="maven_compiler_plugin",
1010
+ path=rel_path,
1011
+ value=m.group(1).strip(),
1012
+ version=major,
1013
+ ))
1014
+ elif rel_path.endswith((".gradle", ".gradle.kts", "gradle.properties")):
1015
+ for pat, source in (
1016
+ (_GRADLE_JAVA_ENUM_RE, "gradle_java_version_enum"),
1017
+ (_GRADLE_JAVA_ASSIGN_RE, "gradle_java_assignment"),
1018
+ (_GRADLE_TOOLCHAIN_RE, "gradle_toolchain"),
1019
+ (_JAVA_VERSION_LINE_RE, "gradle_property"),
1020
+ ):
1021
+ for m in pat.finditer(text):
1022
+ major = _normalize_java_major(m.group(1))
1023
+ evidence.append(_java_evidence(
1024
+ source=source,
1025
+ path=rel_path,
1026
+ value=m.group(1).strip(),
1027
+ version=major,
1028
+ ))
1029
+
1030
+ for abs_path, rel_path in _find_java_lts_support_files(root):
1031
+ if rel_path.endswith(("pom.xml", "build.gradle", "build.gradle.kts")):
1032
+ continue
1033
+ try:
1034
+ text = abs_path.read_text(encoding="utf-8", errors="replace")
1035
+ except OSError:
1036
+ limitations.append(f"{rel_path}: could not be read")
1037
+ continue
1038
+ if rel_path.endswith((".java-version", ".sdkmanrc", "jvm.config", "gradle.properties")):
1039
+ for m in _JAVA_VERSION_LINE_RE.finditer(text):
1040
+ major = _normalize_java_major(m.group(1))
1041
+ evidence.append(_java_evidence(
1042
+ source="java_version_file",
1043
+ path=rel_path,
1044
+ value=m.group(1).strip(),
1045
+ version=major,
1046
+ ))
1047
+ if rel_path.endswith(".java-version"):
1048
+ major = _normalize_java_major(text.strip())
1049
+ evidence.append(_java_evidence(
1050
+ source="java_version_file",
1051
+ path=rel_path,
1052
+ value=text.strip(),
1053
+ version=major,
1054
+ ))
1055
+ if "docker" in rel_path.lower() or rel_path.lower().endswith(("compose.yml", "compose.yaml")):
1056
+ for m in _DOCKER_FROM_RE.finditer(text):
1057
+ image = m.group(1).strip()
1058
+ tag_major: Optional[int] = None
1059
+ tag_match = _DOCKER_JAVA_TAG_RE.search(image)
1060
+ if tag_match:
1061
+ tag_major = _normalize_java_major(tag_match.group(1))
1062
+ evidence.append(_java_evidence(
1063
+ source="container_base_image",
1064
+ path=rel_path,
1065
+ value=image,
1066
+ version=tag_major,
1067
+ distribution=_java_distribution_from_text(image),
1068
+ ))
1069
+ for m in _JAVA_VERSION_LINE_RE.finditer(text):
1070
+ major = _normalize_java_major(m.group(1))
1071
+ evidence.append(_java_evidence(
1072
+ source="container_java_env",
1073
+ path=rel_path,
1074
+ value=m.group(1).strip(),
1075
+ version=major,
1076
+ distribution=_java_distribution_from_text(text),
1077
+ ))
1078
+
1079
+ versions = sorted({e["version"] for e in evidence if isinstance(e.get("version"), int)})
1080
+ distributions = sorted({e["distribution"] for e in evidence if e.get("distribution")})
1081
+ target_blockers: list[dict] = []
1082
+ for version in versions:
1083
+ if version < target_lts:
1084
+ target_blockers.append({
1085
+ "kind": "version_below_target_lts",
1086
+ "version": version,
1087
+ "target_lts": target_lts,
1088
+ "reason": f"{_java_lts_name(version)} evidence is below target {_java_lts_name(target_lts)}",
1089
+ })
1090
+ if not versions:
1091
+ target_blockers.append({
1092
+ "kind": "java_version_unknown",
1093
+ "version": None,
1094
+ "target_lts": target_lts,
1095
+ "reason": "No Java source/target/runtime version evidence found in build or deployment hints.",
1096
+ })
1097
+
1098
+ licensing_reasons: list[str] = []
1099
+ oracle_evidence = [
1100
+ e for e in evidence
1101
+ if str(e.get("distribution", "")).lower() == "oracle jdk"
1102
+ or ("oracle" in str(e.get("value", "")).lower() and "jdk" in str(e.get("value", "")).lower())
1103
+ ]
1104
+ for e in oracle_evidence:
1105
+ licensing_reasons.append(
1106
+ f"{e['path']} declares {e.get('distribution', 'Oracle JDK')} "
1107
+ f"{e.get('version_label', 'unknown version')} ({e['source']}); review support/licensing terms."
1108
+ )
1109
+
1110
+ return {
1111
+ "schema_version": "java-lts-inventory-v1",
1112
+ "target_lts": target_lts,
1113
+ "target_lts_basis": (
1114
+ "--target-jdk supplied by caller" if target_jdk else
1115
+ "default planning target for the 2026 Java LTS/licensing inventory"
1116
+ ),
1117
+ "versions": {
1118
+ "detected": versions,
1119
+ "lowest": versions[0] if versions else None,
1120
+ "highest": versions[-1] if versions else None,
1121
+ "labels": [_java_lts_name(v) for v in versions],
1122
+ },
1123
+ "distributions": distributions,
1124
+ "evidence": evidence,
1125
+ "spring_boot_version_detected": spring_boot_version,
1126
+ "target_lts_blockers": target_blockers,
1127
+ "licensing_review_needed": bool(licensing_reasons),
1128
+ "licensing_review_reasons": licensing_reasons,
1129
+ "legal_disclaimer": (
1130
+ "Inventory signal only, not legal advice. ASK marks explicit Oracle JDK "
1131
+ "or unknown-version evidence for review; license entitlement must be "
1132
+ "confirmed from contracts and deployed binaries."
1133
+ ),
1134
+ "limitations": limitations + [
1135
+ "No build was executed; Maven/Gradle toolchains and transitive runtime images may be incomplete.",
1136
+ "Distribution detection is based on local build/container text only.",
1137
+ ],
1138
+ }
863
1139
  # Cap on total readiness deduction from JDK-modernization findings (medium/low),
864
1140
  # so reflection/date cleanups cannot collapse a jakarta-ready repo's headline.
865
1141
  _JDK_ADVISORY_DEDUCTION_CAP: int = 15
@@ -1527,6 +1803,10 @@ class MigrationReport:
1527
1803
  # Opt-in — attached by the CLI under --blast-radius; None when not computed,
1528
1804
  # and then absent from the payload so the default report stays byte-identical.
1529
1805
  blast_radius: Optional[dict] = None
1806
+ # F-P: Java LTS/licensing inventory, derived from build/runtime hints. Additive
1807
+ # field: it informs migration planning but does not participate in readiness_score.
1808
+ java_lts_inventory: dict = field(default_factory=dict)
1809
+ evidence_manifest: dict = field(default_factory=dict)
1530
1810
 
1531
1811
  def finalize(self) -> "MigrationReport":
1532
1812
  if not self.generated_at:
@@ -2052,6 +2332,8 @@ class MigrationReport:
2052
2332
  "spring_test_only": self.spring_test_only,
2053
2333
  "spring_boot_2_detected": self.spring_boot_2_detected,
2054
2334
  "spring_boot_version_detected": self.spring_boot_version_detected,
2335
+ "java_lts_inventory": self.java_lts_inventory,
2336
+ **({"evidence_manifest": self.evidence_manifest} if self.evidence_manifest else {}),
2055
2337
  "summary": self.summary,
2056
2338
  "findings": [f.to_dict() for f in _listed],
2057
2339
  "hibernate": self.hibernate.to_dict() if self.hibernate is not None else None,
@@ -2155,6 +2437,9 @@ class MigrationReport:
2155
2437
  f"(readiness_score reflects jakarta/Boot3 only — Hibernate is a separate rewrite axis)"]
2156
2438
  if self.headline_blocker else []),
2157
2439
  f"Spring present: {self.spring_present} Spring Boot 2 detected: {_boot}",
2440
+ f"Java LTS inventory: target {self.java_lts_inventory.get('target_lts', 'unknown')} "
2441
+ f"detected {self.java_lts_inventory.get('versions', {}).get('labels', [])} "
2442
+ f"licensing_review_needed={self.java_lts_inventory.get('licensing_review_needed', False)}",
2158
2443
  f"Migration blockers (readiness dimensions): {self.migration_blocking_count}",
2159
2444
  f"Blocking issues (product code): {self.blocking_count} "
2160
2445
  f"(critical: {main_crit}, high: {main_high})"
@@ -2534,6 +2819,7 @@ def run_migrate_check(
2534
2819
  dep_read_errors = 0
2535
2820
  raw_dep_findings: list[MigrationFinding] = []
2536
2821
  _build_texts: list[str] = []
2822
+ _build_sources: list[tuple[str, str]] = []
2537
2823
  for abs_path, rel_path in build_files:
2538
2824
  try:
2539
2825
  text = abs_path.read_text(encoding="utf-8", errors="replace")
@@ -2541,6 +2827,7 @@ def run_migrate_check(
2541
2827
  dep_read_errors += 1
2542
2828
  continue
2543
2829
  _build_texts.append(text)
2830
+ _build_sources.append((rel_path, text))
2544
2831
  dep_findings = _scan_dep_file(text, rel_path)
2545
2832
  filtered = [f for f in dep_findings if SEVERITY_ORDER.get(f.severity, 3) <= min_order]
2546
2833
  raw_dep_findings.extend(filtered)
@@ -2595,6 +2882,12 @@ def run_migrate_check(
2595
2882
  root, spring_runtime_import_seen, spring_test_import_seen
2596
2883
  )
2597
2884
  spring_boot_present = _detect_spring_boot_present(root)
2885
+ java_lts_inventory = _java_lts_inventory(
2886
+ root,
2887
+ _build_sources,
2888
+ spring_boot_version=spring_boot_version,
2889
+ target_jdk=target_jdk,
2890
+ )
2598
2891
 
2599
2892
  # BUG #8: classify each finding's code context (main / test / generated) so the
2600
2893
  # report can keep test fixtures and autogenerated sources out of blocking_count.
@@ -2645,6 +2938,7 @@ def run_migrate_check(
2645
2938
  jakarta_namespace_adopted=jakarta_import_count > 0,
2646
2939
  findings=all_findings,
2647
2940
  hibernate=hibernate_strat,
2941
+ java_lts_inventory=java_lts_inventory,
2648
2942
  limitations=limitations,
2649
2943
  metadata={
2650
2944
  "java_files_scanned": len(file_paths),
@@ -2703,7 +2997,19 @@ def run_migrate_check(
2703
2997
  except Exception:
2704
2998
  pass
2705
2999
 
2706
- return report.finalize()
3000
+ report = report.finalize()
3001
+ from sourcecode.provenance import build_evidence_manifest
3002
+ report.evidence_manifest = build_evidence_manifest(
3003
+ report.to_dict(),
3004
+ command="migrate-check",
3005
+ root=root,
3006
+ inputs={
3007
+ "min_severity": min_severity,
3008
+ "target_jdk": target_jdk,
3009
+ "keep_boot": keep_boot,
3010
+ },
3011
+ )
3012
+ return report
2707
3013
 
2708
3014
 
2709
3015
  # Remaining static limitations — things that truly require runtime analysis