sourcecode 4.10.4__tar.gz → 4.10.6__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 (224) hide show
  1. {sourcecode-4.10.4 → sourcecode-4.10.6}/CHANGELOG.md +229 -0
  2. {sourcecode-4.10.4 → sourcecode-4.10.6}/PKG-INFO +12 -6
  3. {sourcecode-4.10.4 → sourcecode-4.10.6}/README.md +11 -5
  4. {sourcecode-4.10.4 → sourcecode-4.10.6}/pyproject.toml +1 -1
  5. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/architectural_baseline.py +80 -1
  7. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/cache_model.py +11 -4
  8. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/change_plan.py +43 -8
  9. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/cli.py +376 -55
  10. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/compare.py +91 -11
  11. sourcecode-4.10.6/src/sourcecode/container_wiring.py +315 -0
  12. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/context_cache.py +63 -0
  13. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/data_exposure.py +17 -0
  14. sourcecode-4.10.6/src/sourcecode/detach.py +155 -0
  15. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/non_coverage.py +54 -0
  16. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/output_budget.py +26 -3
  17. sourcecode-4.10.6/src/sourcecode/output_encoding.py +51 -0
  18. sourcecode-4.10.6/src/sourcecode/phased_run.py +210 -0
  19. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/pr_impact.py +107 -6
  20. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/prepare_context.py +38 -4
  21. sourcecode-4.10.6/src/sourcecode/progress.py +272 -0
  22. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/repository_ir.py +113 -3
  23. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/runs.py +8 -0
  24. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/security_config_scan.py +30 -22
  25. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/security_posture.py +28 -0
  26. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/serializer.py +6 -1
  27. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/servlet_surface.py +17 -1
  28. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/spring_impact.py +38 -0
  29. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/spring_security_audit.py +21 -5
  30. sourcecode-4.10.6/src/sourcecode/text_input.py +93 -0
  31. sourcecode-4.10.4/src/sourcecode/progress.py +0 -125
  32. {sourcecode-4.10.4 → sourcecode-4.10.6}/.github/workflows/build-windows.yml +0 -0
  33. {sourcecode-4.10.4 → sourcecode-4.10.6}/.gitignore +0 -0
  34. {sourcecode-4.10.4 → sourcecode-4.10.6}/.ruff.toml +0 -0
  35. {sourcecode-4.10.4 → sourcecode-4.10.6}/CLAUDE.md +0 -0
  36. {sourcecode-4.10.4 → sourcecode-4.10.6}/CONTRIBUTING.md +0 -0
  37. {sourcecode-4.10.4 → sourcecode-4.10.6}/LICENSE +0 -0
  38. {sourcecode-4.10.4 → sourcecode-4.10.6}/SECURITY.md +0 -0
  39. {sourcecode-4.10.4 → sourcecode-4.10.6}/raw +0 -0
  40. {sourcecode-4.10.4 → sourcecode-4.10.6}/scripts/compare_integration_engines.py +0 -0
  41. {sourcecode-4.10.4 → sourcecode-4.10.6}/scripts/customer_smoke_test.sh +0 -0
  42. {sourcecode-4.10.4 → sourcecode-4.10.6}/scripts/generate_jdk_exports.py +0 -0
  43. {sourcecode-4.10.4 → sourcecode-4.10.6}/scripts/perf_harness.py +0 -0
  44. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/adaptive_scanner.py +0 -0
  45. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/archetype.py +0 -0
  46. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/architectural_delta.py +0 -0
  47. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/architecture_analyzer.py +0 -0
  48. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/architecture_summary.py +0 -0
  49. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/ast_extractor.py +0 -0
  50. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/audit_report.py +0 -0
  51. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/baseline_autocapture.py +0 -0
  52. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/cache.py +0 -0
  53. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/call_surface.py +0 -0
  54. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/caller_metrics.py +0 -0
  55. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/caller_reach.py +0 -0
  56. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/canonical_ir.py +0 -0
  57. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/chain_rules.py +0 -0
  58. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/cir_graphs.py +0 -0
  59. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/classifier.py +0 -0
  60. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/client_calls.py +0 -0
  61. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/code_notes_analyzer.py +0 -0
  62. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/confidence_analyzer.py +0 -0
  63. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/constraint_diff.py +0 -0
  64. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/context_graph.py +0 -0
  65. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/context_scorer.py +0 -0
  66. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/context_summarizer.py +0 -0
  67. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/contract_diff.py +0 -0
  68. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/contract_init.py +0 -0
  69. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/contract_model.py +0 -0
  70. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/contract_pipeline.py +0 -0
  71. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/coverage_parser.py +0 -0
  72. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/data_labels.py +0 -0
  73. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/defect_identity.py +0 -0
  74. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/degradation.py +0 -0
  75. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/dependency_analyzer.py +0 -0
  76. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/deployment_prefix.py +0 -0
  77. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/__init__.py +0 -0
  78. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/base.py +0 -0
  79. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/csproj_parser.py +0 -0
  80. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/dart.py +0 -0
  81. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/dotnet.py +0 -0
  82. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/elixir.py +0 -0
  83. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/go.py +0 -0
  84. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/heuristic.py +0 -0
  85. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/hybrid.py +0 -0
  86. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/java.py +0 -0
  87. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/jvm_ext.py +0 -0
  88. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/nodejs.py +0 -0
  89. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/parsers.py +0 -0
  90. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/php.py +0 -0
  91. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/project.py +0 -0
  92. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/python.py +0 -0
  93. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/ruby.py +0 -0
  94. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/rust.py +0 -0
  95. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/systems.py +0 -0
  96. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/terraform.py +0 -0
  97. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/detectors/tooling.py +0 -0
  98. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/doc_analyzer.py +0 -0
  99. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/dynamic_argument_surface.py +0 -0
  100. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/endpoint_literals.py +0 -0
  101. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/endpoint_metrics.py +0 -0
  102. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/entrypoint_classifier.py +0 -0
  103. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/env_analyzer.py +0 -0
  104. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/envelope.py +0 -0
  105. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/environment_resolution.py +0 -0
  106. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/error_schema.py +0 -0
  107. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/evidence_provider.py +0 -0
  108. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/explain.py +0 -0
  109. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/facts/__init__.py +0 -0
  110. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/facts/registry.json +0 -0
  111. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/file_chunker.py +0 -0
  112. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/file_classifier.py +0 -0
  113. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/filter_surface.py +0 -0
  114. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/format_contract.py +0 -0
  115. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/fqn_utils.py +0 -0
  116. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/git_analyzer.py +0 -0
  117. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/git_checkout.py +0 -0
  118. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/graph_analyzer.py +0 -0
  119. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/graph_evidence.py +0 -0
  120. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/hibernate_strat.py +0 -0
  121. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/identity_fallback.py +0 -0
  122. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/integration_coordinates.py +0 -0
  123. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/jdk_exports.py +0 -0
  124. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/license.py +0 -0
  125. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp/__init__.py +0 -0
  126. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  127. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  128. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  129. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  130. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  131. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp/orchestrator.py +0 -0
  132. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp/registry.py +0 -0
  133. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp/runner.py +0 -0
  134. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp/server.py +0 -0
  135. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/mcp_nudge.py +0 -0
  136. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/metrics_analyzer.py +0 -0
  137. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/migrate_check.py +0 -0
  138. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/migration_blast.py +0 -0
  139. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/openapi_surface.py +0 -0
  140. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/openrewrite_recipe.py +0 -0
  141. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/parse_cache.py +0 -0
  142. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/path_admission.py +0 -0
  143. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/path_filters.py +0 -0
  144. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/perf.py +0 -0
  145. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/pipe_contract.py +0 -0
  146. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/posture.py +0 -0
  147. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/pr_comment_renderer.py +0 -0
  148. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/provenance.py +0 -0
  149. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/ranking_engine.py +0 -0
  150. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/readiness_timeline.py +0 -0
  151. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/reconciliation.py +0 -0
  152. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/redactor.py +0 -0
  153. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/reference_facts.py +0 -0
  154. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/release_info.py +0 -0
  155. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/relevance_scorer.py +0 -0
  156. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/remedies.py +0 -0
  157. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/rename_refactor.py +0 -0
  158. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/repo_classifier.py +0 -0
  159. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/__init__.py +0 -0
  160. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/context.py +0 -0
  161. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/errors.py +0 -0
  162. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/executor.py +0 -0
  163. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/planner.py +0 -0
  164. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/query.py +0 -0
  165. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/request.py +0 -0
  166. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/resolution.py +0 -0
  167. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/result.py +0 -0
  168. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/retriever.py +0 -0
  169. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/runtime.py +0 -0
  170. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/steps.py +0 -0
  171. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  172. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/steps_graph.py +0 -0
  173. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/steps_impact.py +0 -0
  174. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/steps_intf.py +0 -0
  175. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/steps_struct.py +0 -0
  176. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  177. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/ris.py +0 -0
  178. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/risk.py +0 -0
  179. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/rule_catalog.py +0 -0
  180. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/runtime_classifier.py +0 -0
  181. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/sarif.py +0 -0
  182. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/scanner.py +0 -0
  183. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/schema.py +0 -0
  184. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  185. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/security_config.py +0 -0
  186. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/semantic_analyzer.py +0 -0
  187. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/semantic_impact_engine.py +0 -0
  188. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/semantic_integration_engine.py +0 -0
  189. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/semantic_services.py +0 -0
  190. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/source_text.py +0 -0
  191. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/spring_event_topology.py +0 -0
  192. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/spring_findings.py +0 -0
  193. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/spring_model.py +0 -0
  194. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/spring_profiles.py +0 -0
  195. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/spring_properties.py +0 -0
  196. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/spring_semantic.py +0 -0
  197. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/spring_tx_analyzer.py +0 -0
  198. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/summarizer.py +0 -0
  199. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/target_admission.py +0 -0
  200. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/telemetry/__init__.py +0 -0
  201. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/telemetry/config.py +0 -0
  202. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/telemetry/consent.py +0 -0
  203. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/telemetry/events.py +0 -0
  204. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/telemetry/filters.py +0 -0
  205. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/telemetry/transport.py +0 -0
  206. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/test_gap_ranking.py +0 -0
  207. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/test_sources.py +0 -0
  208. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/token_estimate.py +0 -0
  209. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/tree_utils.py +0 -0
  210. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/type_usage_surface.py +0 -0
  211. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/validation_inference.py +0 -0
  212. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/validation_surface.py +0 -0
  213. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/verify_edit.py +0 -0
  214. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/verify_repo.py +0 -0
  215. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/verify_rules.py +0 -0
  216. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/version_check.py +0 -0
  217. {sourcecode-4.10.4 → sourcecode-4.10.6}/src/sourcecode/workspace.py +0 -0
  218. {sourcecode-4.10.4 → sourcecode-4.10.6}/supabase/functions/README.md +0 -0
  219. {sourcecode-4.10.4 → sourcecode-4.10.6}/supabase/functions/get-license/index.ts +0 -0
  220. {sourcecode-4.10.4 → sourcecode-4.10.6}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  221. {sourcecode-4.10.4 → sourcecode-4.10.6}/supabase/functions/telemetry/index.ts +0 -0
  222. {sourcecode-4.10.4 → sourcecode-4.10.6}/supabase/sql/license_event_ordering.sql +0 -0
  223. {sourcecode-4.10.4 → sourcecode-4.10.6}/supabase/sql/licensing_schema.sql +0 -0
  224. {sourcecode-4.10.4 → sourcecode-4.10.6}/supabase/sql/telemetry_events.sql +0 -0
@@ -2,6 +2,235 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [4.10.6] — 2026-08-07
6
+
7
+ The M13 sanitation queue, tiers A through C plus the claims tier: **eighteen ledger
8
+ rows close** (C1-36 · C1-37 · C1-38 · C1-39 · C1-40 · C2-27 · C2-28 · C2-29 ·
9
+ C3-53 · C3-54 part 3 · C3-55 · C3-61 · C3-65 · C3-66 · C3-67 · C3-68 · C3-69 ·
10
+ CL-16), against the two independent field evaluations of 4.10.4 and 4.10.5 that
11
+ agreed on the diagnosis and disagreed on nothing material. No feature ships here:
12
+ the owner directive for this milestone is that every bug is sanitated before the
13
+ first feature starts.
14
+
15
+ ⚠ **Five behaviour changes to read before upgrading a pipeline.** All five are the
16
+ fix working, and three of them can move a gate:
17
+
18
+ - **`pr-impact`, `impact`, `impact-chain`, `plan` and `compare` now floor the
19
+ verdict at `HIGH` when the change touches a component the container invokes** —
20
+ a `@Configuration`, an `@Aspect`, a `Filter`, a `@ControllerAdvice`, a bean
21
+ resolved through an interface something injects, `web.xml`,
22
+ `application*.y*ml`. A gate that was passing green over a security-config diff
23
+ will start failing, with the verdict that diff always deserved (C1-36).
24
+ - **`ASK_MAX_ANALYSIS_SECONDS` is honoured instead of vetoed.** A value below the
25
+ class floor no longer refuses to run: it warns and runs, and a run that spends
26
+ its budget publishes what it measured with `partial: true`. `--ci` fails on a
27
+ partial answer (C3-55, C3-54 part 3).
28
+ - **`ask endpoints --servlets` answers with the servlet population only**, plus
29
+ the endpoint total as one number. A consumer that read `endpoints[]` out of that
30
+ document has to call `ask endpoints` for it (C3-65).
31
+ - **`ask plan` publishes `null`, not `0`, for anything it did not measure** — an
32
+ unresolved target, or `covering_tests` in a repository with no test source root.
33
+ A consumer coercing those to `int` needs to handle `null` (C1-39).
34
+ - **`baseline capture` no longer records the machine by default** (`cpu`, `cores`,
35
+ `hostname_hash`); `--record-env` restores it. A comparison keyed on those fields
36
+ needs the flag (C3-68).
37
+
38
+ `posture` moves from `experimental` to `supported` (CL-16).
39
+
40
+ ### Fixed
41
+
42
+ - **A component the container calls is not a component nothing calls** (C1-36).
43
+ *The one blind spot this product did not declare, reproduced on two builds by
44
+ two evaluations.* Blast radius is projected over the call graph, so a class the
45
+ framework invokes has fan-in ~0 and every figure derived from it reads as no
46
+ impact: a diff over the filter chain, the aspect gating 935 routes and the
47
+ production password encoder published `LOW / "No high-risk signals detected" /
48
+ affected_endpoints: []`, while a plain POJO in the same repository published
49
+ `CRITICAL` with 3 382 endpoints. The fan-in figure was never wrong; using it as
50
+ a risk proxy on that population was. `container_wiring.py` answers the one
51
+ question that separates them, from facts the CIR already carries — the role it
52
+ assigns, the interception annotations and supertypes fixed by published
53
+ specifications, and one route that consults no name at all: *the class
54
+ implements an interface something injects and has zero incoming call edges*,
55
+ which catches a password encoder without anyone naming one. Wired at the single
56
+ blast-radius authority so the four commands that disagreed now answer from one
57
+ place. The verdict is floored, never lowered; the population is published with
58
+ its evidence; and **NC-010** states the limit in the payload, the README and the
59
+ help.
60
+ - **A killed analysis keeps what it measured** (C3-54 part 3). `phased_run.py`
61
+ gives a long command a declared phase list and a durable checkpoint after each
62
+ phase, written through the same fsynced, atomic seam as the final answer and
63
+ removed only once the complete answer is on disk — so a teardown leaves the
64
+ better of the two files and never neither. A checkpoint can never pass for a
65
+ finished answer: `partial: true`, `phases_completed`, `phases_pending`,
66
+ `why_stopped`. The field lost 6,8 minutes of CPU and every recoverable byte
67
+ four times.
68
+ - **A budget that is set is honoured** (C3-55). Below the class floor was refused
69
+ and above it admitted a run that outlived the session, so no value was both
70
+ accepted and survivable on the variable the help presents as the CI control.
71
+ The floor is advice printed before the work; the budget stops the run between
72
+ phases; counts in a partial answer are a floor over the phases that completed
73
+ and say so.
74
+ - **A repository-wide run can outlive its shell** (C3-53, survivable half).
75
+ `--detach` (with `--output`) starts the analysis in a process that leads its own
76
+ session and, on Windows, carries `CREATE_BREAKAWAY_FROM_JOB` — the documented
77
+ way out of the parent shell's job object, which is what killed even
78
+ `Start-Process -NoNewWindow` and all six of its children at the same second the
79
+ session died. When a platform refuses the breakaway the envelope says so rather
80
+ than implying a guarantee it does not have. **Not closed:** the field case is
81
+ Windows under an agent harness, and only a Windows re-run proves the tree
82
+ survives there.
83
+ - **`--servlets` answers with the population it names** (C3-65). The flag read as
84
+ a filter and behaved as an aggregator: 1,8 MB to stdout, the useful signal at
85
+ 0,08 % of the bytes, at line 49 622 of 49 730 — and the agent session that asked
86
+ for it died. Every servlet row also carries `population: "servlet"`, so a
87
+ consumer iterating rows finally sees a servlet after passing the flag built to
88
+ show them.
89
+ - **`compare` no longer ranks the repository first** (C3-67). A candidate that
90
+ resolves to an existing directory is published as `resolved: false` /
91
+ `not_a_candidate` and ranks last. `ask compare A B .` used to rank the `.`
92
+ first, with 3 574 endpoints and 3 342 rollback files as its cost.
93
+ - **`secured_endpoints` published a display cap as a measurement** (C1-38). The
94
+ `15` that reconciled with nothing was `min(real, 15)`: the count was `len()` of
95
+ a truncated 15-row sample. The population is counted before the cut, the list is
96
+ still sampled, and the omission is published with its `cap_effect`. All four
97
+ figures that share the name now carry their unit and name the other three.
98
+ - **Four figures called "endpoints" each say what they counted** (C1-37), and the
99
+ baseline stopped disagreeing with itself: `totals.endpoints` counts handler
100
+ mappings, `totals.endpoint_contract_pairs` counts the distinct (METHOD, path)
101
+ pairs `endpoint_surface` lists, and the battery holds the invariant between the
102
+ second and the list — an invariant that is only checkable because the two
103
+ figures finally describe two things.
104
+ - **What was not measured is `null`, never `0`** (C1-39), in `plan` and in
105
+ `covering_tests` on a repository with no test source root. The shape
106
+ `data-exposure` already had, one level up.
107
+ - **`cache model` promised a hit the command does not store** (C1-40). Measured on
108
+ keycloak: **with** a warm, `onboard` costs 0,7 s; **without** one, a second
109
+ identical run costs 9,4 s against 10,2 s cold. `repeat` is now false for
110
+ `onboard`, with the figures published at two scales.
111
+ - **A local artefact is not committable by accident** (C3-68). `.ask/` gets its own
112
+ `.gitignore` the moment it is created — self-confined, never touching the user's
113
+ own, never overwriting one they edited — and `env` records only the two fields
114
+ that decide comparability.
115
+ - **A heartbeat does not make a successful run look failed** (C3-66).
116
+ `ASK_PROGRESS` takes `1`/`stderr`, `off` and `file:<path>`; the file form leaves
117
+ stderr empty, so a PowerShell job can watch a six-minute run and still trust
118
+ `$?`. No setting writes to stdout.
119
+ - **Fix the decoder, not the bytes** (C3-69). `SetConsoleOutputCP(65001)` at entry
120
+ tells the Windows console to read the UTF-8 we were always writing; only when
121
+ that fails does the emit seam escape the payload to ASCII. One decision, made
122
+ once, read by every emit seam.
123
+ - **A budget note that contradicts the summary beside it** (C2-27). The unit is
124
+ bytes, so `29KB → 29KB` beside `total_omitted_items: 10` cannot recur, and both
125
+ the note and `_truncation_summary.applies_to` state that trimming describes the
126
+ stdout rendering — `--output` writes the answer untrimmed.
127
+ - **Git churn is not evidence about semantics** (C2-29). When the symptom names
128
+ something this repository contains, activity signals contribute nothing to the
129
+ `fix-bug` rank and are reported as context; when nothing resolves they still
130
+ rank, published as correlational rather than as triage.
131
+ - **A limit is stated once and referenced** (C2-28). `NC-007` was emitted twice,
132
+ byte for byte, in the heaviest payload the tool produces.
133
+
134
+ ### Performance
135
+
136
+ - **The rule pass was never mostly rules** (C3-61, second measurement).
137
+ Re-profiled after the pre-filter shipped: `infer_security_posture` ran **twice**
138
+ per audit — once to learn the custom-gate tokens the rules match on, once to
139
+ publish the posture block — at ~2,1 s each, each rebuilding the filter surface
140
+ and re-walking the tree underneath it; and `_proxy_bypass` scanned every
141
+ admissible source for methods twice. Both are identities and both are fixed as
142
+ identities. **keycloak 6,17 s → 4,07 s, 90 findings before and after, payload
143
+ identical except `generated_at` and `analysis_time_ms`.** No threads and no
144
+ processes: parallelising would have bought ~15 % of a wall clock now dominated
145
+ by the CIR build (12,6 s warm against 4,1 s for the whole rule pass), which is
146
+ F-AA's problem and not this row's.
147
+ - **Deciding whether a cached context is still valid no longer deserialises it**
148
+ (C1-40, second half). The validity keys live in an uncompressed sidecar beside
149
+ the blob; the field measured 1 029 ms average / 1 955 ms p95 to read three short
150
+ strings out of a 10 MB envelope, and an entry invalidated by a changed signature
151
+ paid all of it before discarding the result.
152
+
153
+ ### Changed
154
+
155
+ - `posture` is `supported`, not `experimental` (CL-16). It is the one command
156
+ neither field evaluation found a competitor for, and it produced 2 of
157
+ evaluation #15's 3 CRITICAL findings.
158
+ - `pr-impact` keeps its `core` tier because C1-36's silent half is closed: it can
159
+ now keep the promise that tier makes.
160
+
161
+ ### Still open from this queue
162
+
163
+ `C4-17` (`--output` accepted postfix on some subcommands, absent on others),
164
+ `C4-19` (a scope-aware `--help` ordered by what can actually run), `C4-20`
165
+ (suggested next commands checked against the cost model before printing), `C4-21`
166
+ (`ask config` listing the label source it resolved) and `C4-18` (`cache status`
167
+ printing `Cores:` for a cache count). All five are discoverability; none of them
168
+ publishes a false claim.
169
+
170
+ ## [4.10.5] — 2026-08-07
171
+
172
+ Second release of the field-evaluation #13 queue. Three rows close and one closes
173
+ in part; the suite is 6 691 tests green.
174
+
175
+ ⚠ **Two behaviour changes to read before upgrading a pipeline.** Both are the fix
176
+ working, and both can move a gate:
177
+
178
+ - On Windows, `pr-impact --files -` now resolves paths it previously mangled, so a
179
+ pipeline that was passing because its diff decoded to nothing can start failing —
180
+ with the verdict the diff always deserved (C3-58).
181
+ - A security finding that existed only in build output (`target/classes`) is no
182
+ longer reported, so a repository audited mid-build can report fewer findings
183
+ (C3-64).
184
+
185
+ ### Performance
186
+
187
+ - **The security configuration scan discovers its files 8.7× faster** (C3-64):
188
+ 0.61 s → 0.07 s on keycloak, 0.45 s → 0.05 s on alfresco. Five `rglob` calls
189
+ became one pruned walk. `rglob` cannot skip a subtree, so the old cost was set by
190
+ the build output and the VCS metadata rather than by the source: 48.4 % of
191
+ openmrs-core's tree entries and 53.3 % of this repository's live under directories
192
+ that hold no source at all. On a clean checkout the end-to-end scan moves 1.73 s →
193
+ 1.43 s (the rest is reading Java sources); the win grows with how built the tree
194
+ is, which is the state a developer's repository is normally in.
195
+
196
+ ### Fixed
197
+
198
+ - **A path list is decoded by its bytes, never by the platform locale** (C3-58).
199
+ `ask pr-impact --files -` read stdin through Python's locale decoding, which on
200
+ Windows is the ANSI codepage: `git diff --name-only | ask pr-impact --files -`
201
+ returned `risk: UNKNOWN` and 0 endpoints where the same list without the shell's
202
+ default byte-order mark returned `CRITICAL` and 1 144 — a green pipeline on the
203
+ gating command. The BOM was the visible half; `AñoService.java` arriving as
204
+ `AñoService.java` was the cause. The new authority `sourcecode/text_input.py`
205
+ reads `sys.stdin.buffer` and lets the bytes name the encoding, and the list-file
206
+ shape goes through it too: PowerShell 5.1 writes UTF-16-LE by default, which the
207
+ previous UTF-8 read raised on. Undecodable bytes now yield an unusable path with
208
+ encoding named as a cause, never a traceback.
209
+ - **No command claims `done` before its answer exists** (C3-54, part 2). The
210
+ completion line moved out of `Progress.finish()` entirely: `finish()` claims the
211
+ work is done, and the line is printed when the answer has actually been handed
212
+ over. A run killed in between prints nothing, and a run that raised prints
213
+ nothing — previously ~20 commands emitted `done (Xs)` while their `--output` file
214
+ did not exist yet, which in the field meant 6.8 minutes of analysis reported as
215
+ success over an absent file. Output files are also durable now: the shared writer
216
+ fsyncs data and directory entry before anything reports a write, and `repo-ir` —
217
+ the one `-o` path that bypassed that writer — goes through it.
218
+ - **A rejected progress write no longer silences progress for the rest of the run**
219
+ (C3-56, part). The heartbeat treated any write failure as terminal; it now retries
220
+ and gives up only after five consecutive failures. The reported 393 s of silence
221
+ did not reproduce: the cadence is timer-driven and measured exact on a cold
222
+ 29.9 s run (5.0 / 10.0 / 15.0 / 20.0 / 25.1 s). The progress denominator and ETA
223
+ the row also asks for are still open.
224
+ - **A build copy is no longer a second security finding** (C3-64). The configuration
225
+ scan walked the whole tree five times and could not skip a subtree, so a compiled
226
+ copy of `application.properties` under `target/classes` was reported beside the
227
+ source file it came from. It now walks once, pruning build output and VCS metadata
228
+ with the same ignore set Java discovery uses.
229
+ - **An unresolved changed file names the right thing to check** (C3-58). One message
230
+ covered both causes and pointed at the wrong one: a path that is *not on disk* is a
231
+ path problem (form and encoding), while a path that *is* there but maps to no class
232
+ is a fact about the file. They now say so separately.
233
+
5
234
  ## [4.10.4] — 2026-08-07
6
235
 
7
236
  First release of the queue opened by field evaluation #13. Four of its rows close,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 4.10.4
3
+ Version: 4.10.6
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.10.4-blue)
45
+ ![Version](https://img.shields.io/badge/version-4.10.6-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`**
@@ -106,7 +106,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
106
106
  # pip / pipx
107
107
  pipx install sourcecode # or: pip install sourcecode
108
108
 
109
- ask version # ask 4.10.4 — and, on a build that has aged,
109
+ ask version # ask 4.10.6 — and, on a build that has aged,
110
110
  # how many releases have probably shipped since
111
111
  ```
112
112
 
@@ -299,7 +299,7 @@ experimental: both are true, and they are two different facts.
299
299
  |---|---|---|
300
300
  | **core** | contract stable within a major — safe to gate CI on | `endpoints` · `spring-audit` · `migrate-check` · `impact` · `impact-chain` · `pr-impact` · `verify` |
301
301
  | **supported** | maintained; fields are added, never removed without a major | every command not named in another row |
302
- | **experimental** | shape may change in a minor — do not gate CI on it | `risk` · `enrich` · `audit-report` · `data-exposure` · `migrate-recipe` · `posture` · `archetype` |
302
+ | **experimental** | shape may change in a minor — do not gate CI on it | `risk` · `enrich` · `audit-report` · `data-exposure` · `migrate-recipe` · `archetype` |
303
303
  | **parked** | kept working, no longer developed | `retrieve` |
304
304
 
305
305
  The same table is printed by `ask --help`, and both are generated from one authority
@@ -315,7 +315,7 @@ from it.
315
315
 
316
316
  | Command | Tier | Answers | Note |
317
317
  |---|---|---|---|
318
- | `posture` | experimental | which beans a profile set wires, and how effective endpoint access differs between two sets | the most differentiated capability here |
318
+ | `posture` | supported | which beans a profile set wires, and how effective endpoint access differs between two sets | the most differentiated capability here |
319
319
  | `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` |
320
320
  | `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 |
321
321
  | `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 |
@@ -398,6 +398,12 @@ And one about who calls a route:
398
398
  |---|---|---|
399
399
  | **Whether a route is called from outside this repository — another service, a mobile app, a partner, a cron job, or a client whose URL is assembled at run time.** | The join reads the TypeScript/JavaScript checked in beside the server and only the call shapes whose verb and path are written at the call site. That is a floor on who calls a route and can never be a ceiling: the repository cannot observe consumers it does not contain. So a route with no call site here is published as a candidate for deletion, never as dead API. | Access logs or an API gateway answer who really calls a route. The opposite direction is decisive without them: a client call matching no route is a request this repository's own front end makes and its server does not serve. |
400
400
 
401
+ And one about blast radius:
402
+
403
+ | Not covered | Why | What answers it |
404
+ |---|---|---|
405
+ | **The reach of a component the container invokes rather than the code — a security filter chain, an around-advice aspect, a `@ControllerAdvice`, a bean resolved through the interface it implements.** | Blast radius here is projected over the call graph: who calls the changed symbol, and which routes reach those callers. Nothing in the repository calls a container-wired component, so its measured call-graph fan-in is ~0 and every figure derived from it — affected endpoints, direct callers, rollback surface — reads as no impact. The fan-in figure is correct; using it as a risk proxy on this population is not. Publishing that as a low verdict would be a confident zero, which is the one thing this product does not do. | Such a change is flagged in `analysis_warnings` and its verdict is floored, never rated on fan-in. The reach itself is measured elsewhere and was there all along: `ask endpoints` resolves a gate annotation to the routes carrying it, `ask explain <class>` prints the request chain a configuration declares with its source line, and `ask posture --profile <set>` states what each profile leaves open. Projecting those into the blast radius (pointcut → routes, matcher → routes) is a measurement this command does not yet make. |
406
+
401
407
  The one row above with a remedy in this build: `ask endpoints /path/to/repo --servlets`
402
408
  lists the servlet-mounted HTTP surface as a population of its own — `web.xml` mappings and
403
409
  `ServletRegistrationBean` registrations, with the file that declares each — and never merges
@@ -497,7 +503,7 @@ Matching endpoints report `policy: "custom"` and drop out of the `no_security_si
497
503
  | [USER_GUIDE.md](docs/USER_GUIDE.md) | Full command reference, flags, output schema, workflows |
498
504
  | [contracts.md](docs/contracts.md) | Declare invariants in `.ask/contracts.yml` (or derive them with `ask verify --init`), enforce them in the edit loop and in CI |
499
505
  | [baseline-ci.yml](docs/baseline-ci.yml) | Capture an architectural baseline per release, so the series exists when you want to read it |
500
- | [posture.md](docs/posture.md) | What a profile set actually wires — down to which endpoints its filter chain permits — and what could not be decided (experimental) |
506
+ | [posture.md](docs/posture.md) | What a profile set actually wires — down to which endpoints its filter chain permits — and what could not be decided |
501
507
  | [migrate-check.md](docs/migrate-check.md) | Migration rule catalogue (MIG-001..043) + Hibernate stratification |
502
508
  | [MODERNIZATION.md](docs/MODERNIZATION.md) | The modernization product: assess → understand → plan → execute |
503
509
  | [PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md) | Free vs Pro, pricing model |
@@ -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.10.4-blue)
7
+ ![Version](https://img.shields.io/badge/version-4.10.6-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`**
@@ -68,7 +68,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
68
68
  # pip / pipx
69
69
  pipx install sourcecode # or: pip install sourcecode
70
70
 
71
- ask version # ask 4.10.4 — and, on a build that has aged,
71
+ ask version # ask 4.10.6 — and, on a build that has aged,
72
72
  # how many releases have probably shipped since
73
73
  ```
74
74
 
@@ -261,7 +261,7 @@ experimental: both are true, and they are two different facts.
261
261
  |---|---|---|
262
262
  | **core** | contract stable within a major — safe to gate CI on | `endpoints` · `spring-audit` · `migrate-check` · `impact` · `impact-chain` · `pr-impact` · `verify` |
263
263
  | **supported** | maintained; fields are added, never removed without a major | every command not named in another row |
264
- | **experimental** | shape may change in a minor — do not gate CI on it | `risk` · `enrich` · `audit-report` · `data-exposure` · `migrate-recipe` · `posture` · `archetype` |
264
+ | **experimental** | shape may change in a minor — do not gate CI on it | `risk` · `enrich` · `audit-report` · `data-exposure` · `migrate-recipe` · `archetype` |
265
265
  | **parked** | kept working, no longer developed | `retrieve` |
266
266
 
267
267
  The same table is printed by `ask --help`, and both are generated from one authority
@@ -277,7 +277,7 @@ from it.
277
277
 
278
278
  | Command | Tier | Answers | Note |
279
279
  |---|---|---|---|
280
- | `posture` | experimental | which beans a profile set wires, and how effective endpoint access differs between two sets | the most differentiated capability here |
280
+ | `posture` | supported | which beans a profile set wires, and how effective endpoint access differs between two sets | the most differentiated capability here |
281
281
  | `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` |
282
282
  | `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 |
283
283
  | `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 |
@@ -360,6 +360,12 @@ And one about who calls a route:
360
360
  |---|---|---|
361
361
  | **Whether a route is called from outside this repository — another service, a mobile app, a partner, a cron job, or a client whose URL is assembled at run time.** | The join reads the TypeScript/JavaScript checked in beside the server and only the call shapes whose verb and path are written at the call site. That is a floor on who calls a route and can never be a ceiling: the repository cannot observe consumers it does not contain. So a route with no call site here is published as a candidate for deletion, never as dead API. | Access logs or an API gateway answer who really calls a route. The opposite direction is decisive without them: a client call matching no route is a request this repository's own front end makes and its server does not serve. |
362
362
 
363
+ And one about blast radius:
364
+
365
+ | Not covered | Why | What answers it |
366
+ |---|---|---|
367
+ | **The reach of a component the container invokes rather than the code — a security filter chain, an around-advice aspect, a `@ControllerAdvice`, a bean resolved through the interface it implements.** | Blast radius here is projected over the call graph: who calls the changed symbol, and which routes reach those callers. Nothing in the repository calls a container-wired component, so its measured call-graph fan-in is ~0 and every figure derived from it — affected endpoints, direct callers, rollback surface — reads as no impact. The fan-in figure is correct; using it as a risk proxy on this population is not. Publishing that as a low verdict would be a confident zero, which is the one thing this product does not do. | Such a change is flagged in `analysis_warnings` and its verdict is floored, never rated on fan-in. The reach itself is measured elsewhere and was there all along: `ask endpoints` resolves a gate annotation to the routes carrying it, `ask explain <class>` prints the request chain a configuration declares with its source line, and `ask posture --profile <set>` states what each profile leaves open. Projecting those into the blast radius (pointcut → routes, matcher → routes) is a measurement this command does not yet make. |
368
+
363
369
  The one row above with a remedy in this build: `ask endpoints /path/to/repo --servlets`
364
370
  lists the servlet-mounted HTTP surface as a population of its own — `web.xml` mappings and
365
371
  `ServletRegistrationBean` registrations, with the file that declares each — and never merges
@@ -459,7 +465,7 @@ Matching endpoints report `policy: "custom"` and drop out of the `no_security_si
459
465
  | [USER_GUIDE.md](docs/USER_GUIDE.md) | Full command reference, flags, output schema, workflows |
460
466
  | [contracts.md](docs/contracts.md) | Declare invariants in `.ask/contracts.yml` (or derive them with `ask verify --init`), enforce them in the edit loop and in CI |
461
467
  | [baseline-ci.yml](docs/baseline-ci.yml) | Capture an architectural baseline per release, so the series exists when you want to read it |
462
- | [posture.md](docs/posture.md) | What a profile set actually wires — down to which endpoints its filter chain permits — and what could not be decided (experimental) |
468
+ | [posture.md](docs/posture.md) | What a profile set actually wires — down to which endpoints its filter chain permits — and what could not be decided |
463
469
  | [migrate-check.md](docs/migrate-check.md) | Migration rule catalogue (MIG-001..043) + Hibernate stratification |
464
470
  | [MODERNIZATION.md](docs/MODERNIZATION.md) | The modernization product: assess → understand → plan → execute |
465
471
  | [PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md) | Free vs Pro, pricing model |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "4.10.4"
7
+ version = "4.10.6"
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.10.4"
7
+ __version__ = "4.10.6"
@@ -136,6 +136,19 @@ def _fan_in_from_stored(value: Any) -> dict[str, int]:
136
136
  return {}
137
137
 
138
138
 
139
+ def _comparability_env() -> dict:
140
+ """The environment fields a delta actually depends on (C3-68).
141
+
142
+ A baseline is comparable to another when the platform and the tool agree;
143
+ the CPU model, the core count and a hash of the hostname change none of
144
+ that. `--record-env` restores the full set for someone benchmarking runs
145
+ against each other on one machine.
146
+ """
147
+ import sys as _sys
148
+
149
+ return {"os": _sys.platform, "tool_version": TOOL_VERSION, "recorded": "comparability_only"}
150
+
151
+
139
152
  def build_baseline(
140
153
  cir: "CanonicalRepositoryIR",
141
154
  *,
@@ -145,6 +158,7 @@ def build_baseline(
145
158
  captured_at: str | None = None,
146
159
  env: dict | None = None,
147
160
  tool_version: str | None = None,
161
+ record_env: bool = False,
148
162
  ) -> dict:
149
163
  """Freeze a CIR's measured architectural metrics into a baseline artifact.
150
164
 
@@ -168,6 +182,21 @@ def build_baseline(
168
182
  "files": m.file_count,
169
183
  "symbols": m.symbol_count,
170
184
  "endpoints": m.endpoint_count,
185
+ # C1-37: this artefact's declared purpose is to be a replayable
186
+ # fingerprint, and it disagreed with itself — `totals.endpoints`
187
+ # 3 574 against `len(endpoint_surface)` 3 538 on the field
188
+ # repository. Neither figure was wrong: `endpoints` counts handler
189
+ # mappings and `endpoint_surface` is the set of distinct
190
+ # (METHOD, path) contract pairs, so 36 routes are declared by more
191
+ # than one handler. Two facts, two names, and the invariant the
192
+ # battery can actually hold.
193
+ "endpoint_contract_pairs": len(m.endpoints),
194
+ "endpoints_unit": (
195
+ "handler mappings declared in the repository; "
196
+ "`endpoint_contract_pairs` counts the distinct (METHOD, path) "
197
+ "pairs those mappings expose, which is what `endpoint_surface` "
198
+ "lists and what a diff compares"
199
+ ),
171
200
  "dependency_edges": m.dependency_edge_count,
172
201
  "import_cycles": len(m.cycles),
173
202
  },
@@ -175,7 +204,16 @@ def build_baseline(
175
204
  "endpoint_surface": endpoints,
176
205
  "fan_in": _fan_in_to_list(m.fan_in),
177
206
  "import_cycles": cycles,
178
- "env": env if env is not None else collect_env(),
207
+ # C3-68: this artefact is written inside the work tree, so what it
208
+ # records is what a `git add -A` publishes. `hostname_hash`, `cpu` and
209
+ # `cores` describe the developer's machine and affect nothing a delta
210
+ # compares — from a tool whose own `ask config` says telemetry is off.
211
+ # The default is now the two fields that DO decide comparability; the
212
+ # rest is opt-in per capture.
213
+ "env": (
214
+ env if env is not None
215
+ else (collect_env() if record_env else _comparability_env())
216
+ ),
179
217
  "provenance": (
180
218
  "architectural_baseline (D5): a measured structural fingerprint of one "
181
219
  "CIR, persisted per ref. Metric content is a pure function of the repo "
@@ -319,9 +357,50 @@ def _baseline_filename(baseline: dict) -> str:
319
357
  return f"{safe}.json"
320
358
 
321
359
 
360
+ #: What `.ask/` gets the moment it is created, and why.
361
+ #:
362
+ #: C3-68: `baseline capture` writes into the work tree and nothing protected it.
363
+ #: The field verified `grep -nE "^\.ask|sourcecode" .gitignore` → no matches, so
364
+ #: a `git add -A` commits the artefact — which carries a machine fingerprint and
365
+ #: a 14 587-entry structural inventory of the code. A self-confined
366
+ #: `.ask/.gitignore` is the honest form: it never touches the user's own
367
+ #: `.gitignore`, and it stops applying the moment they delete the directory.
368
+ _ASK_DIR_GITIGNORE = (
369
+ "# Written by `ask` the first time it created this directory.\n"
370
+ "# These are local analysis artefacts: a structural inventory of your code\n"
371
+ "# and, if you asked for it, the machine that measured it. Nothing here\n"
372
+ "# belongs in a commit. Delete this file to version them deliberately.\n"
373
+ "*\n"
374
+ )
375
+
376
+
377
+ def protect_ask_dir(out_dir: Path) -> None:
378
+ """Give a freshly created `.ask/` its own `.gitignore` (C3-68).
379
+
380
+ Best-effort and never overwriting: a user who edited it decided something,
381
+ and a tool that re-imposes its default on every run is worse than one that
382
+ never wrote it.
383
+ """
384
+ try:
385
+ ask_dir = Path(out_dir)
386
+ while ask_dir.name and ask_dir.name != ".ask":
387
+ if ask_dir.parent == ask_dir:
388
+ return
389
+ ask_dir = ask_dir.parent
390
+ if ask_dir.name != ".ask":
391
+ return
392
+ marker = ask_dir / ".gitignore"
393
+ if not marker.exists():
394
+ ask_dir.mkdir(parents=True, exist_ok=True)
395
+ marker.write_text(_ASK_DIR_GITIGNORE, encoding="utf-8")
396
+ except OSError:
397
+ return
398
+
399
+
322
400
  def write_baseline(baseline: dict, out_dir: Path) -> Path:
323
401
  """Write `baseline` as deterministic JSON under `out_dir`; return the file path."""
324
402
  out_dir.mkdir(parents=True, exist_ok=True)
403
+ protect_ask_dir(out_dir)
325
404
  path = out_dir / _baseline_filename(baseline)
326
405
  path.write_text(
327
406
  json.dumps(baseline, sort_keys=True, indent=2) + "\n", encoding="utf-8"
@@ -202,10 +202,17 @@ COMMANDS: tuple[CommandCache, ...] = (
202
202
  "`refactor`, `fix-bug` and `generate-tests` cache their own answer, but a warm does "
203
203
  "not run them, so their first call pays full price; `delta` and `review-pr` are "
204
204
  "diff-dependent and never cached.",
205
- "onboard 6.5 s → 0.3 s · refactor 7.7 s 7.7 s (0.3 s on repeat) · "
206
- "generate-tests 12.4 s → 11.0 s (0.3 s on repeat)"),
207
- CommandCache("onboard", ("task", "ris"), "answer", True, "Shorthand for `prepare-context onboard`.",
208
- "6.5 s → 0.3 s"),
205
+ "onboard 6.5 s → 0.3 s with a warm but a second run with NO warm recomputes "
206
+ "(10.2 s → 9.4 s at 5 486 files; C1-40) · refactor 7.7 s 7.7 s (0.3 s on "
207
+ "repeat) · generate-tests 12.4 s 11.0 s (0.3 s on repeat)"),
208
+ CommandCache("onboard", ("task", "ris"), "answer", False,
209
+ "Shorthand for `prepare-context onboard`. A warm stores its answer; the command "
210
+ "does NOT store its own, so a second run without a warm pays full price. C1-40: "
211
+ "the field read `repeat cached` here, ran it twice with no warm, and measured "
212
+ "56 s then 23,5 s — this row said it would be a hit.",
213
+ "with a warm: 6.5 s → 0.3 s (2 000 files) · 10.2 s → 0.7 s (5 486 files). "
214
+ "WITHOUT a warm, a second identical run: 10.2 s → 9.4 s (5 486 files) — "
215
+ "no answer hit"),
209
216
  CommandCache("explain", ("cir",), "shared", False, "Serves from the shared CIR a warm builds.",
210
217
  "9.8 s → 1.6 s"),
211
218
  CommandCache("export", ("parse",), "shared", False, "", "8.8 s → 3.7 s"),
@@ -93,17 +93,31 @@ def build_change_plan(
93
93
  from sourcecode.target_admission import is_unresolved
94
94
 
95
95
  if is_unresolved(resolution) or not blast.get("matched_fqns"):
96
+ # C1-39: this is a well-formed `change-plan-v1`, not an error envelope,
97
+ # so an agent reads its fields — and every cost used to be `0`. Nothing
98
+ # was measured, and `0` is the answer to a different question. The
99
+ # product already gets this right elsewhere (`data-exposure` answers
100
+ # `answered: false` rather than measuring nothing and publishing a
101
+ # confident zero); this is the same rule, one level up.
102
+ _not_measured = {
103
+ "answered": False,
104
+ "reason": f"target {target!r} did not resolve — nothing was measured",
105
+ }
96
106
  return {
97
107
  "schema": CHANGE_PLAN_SCHEMA,
98
108
  "target": target,
99
109
  "resolution": resolution,
100
110
  "message": blast.get("message", f"Target {target!r} not found."),
101
111
  "candidates": blast.get("candidates", []),
102
- "affected_components": {"count": 0, "components": []},
103
- "covering_tests": {"count": 0, "tests": []},
104
- "affected_endpoints": {"count": 0, "endpoints": []},
105
- "rollback_surface": {"file_count": 0, "files": []},
112
+ "affected_components": {"count": None, "components": [], **_not_measured},
113
+ "covering_tests": {"count": None, "tests": [], **_not_measured},
114
+ "affected_endpoints": {"count": None, "endpoints": [], **_not_measured},
115
+ "rollback_surface": {"file_count": None, "files": [], **_not_measured},
106
116
  "review_checklist": [],
117
+ "how_to_read": (
118
+ "Every cost here is `null`, not `0`: the target did not resolve, so "
119
+ "nothing was measured. A zero would say the change is free."
120
+ ),
107
121
  }
108
122
 
109
123
  seeds = list(blast["matched_fqns"])
@@ -148,10 +162,21 @@ def build_change_plan(
148
162
  checklist.append(
149
163
  f"Re-verify the contract of {len(ep_strings)} endpoint(s) in the blast radius."
150
164
  )
165
+ # C1-39, second half: on a repository with no test source root at all — the
166
+ # field one had 0 test files for 3 337 non-test Java sources — "0 covering
167
+ # tests" reads as *the change is safe* when it means *there are no tests*.
168
+ _has_test_root = any(
169
+ is_test_path(str(m.get("file") or "")) for m in meta.values()
170
+ )
151
171
  if tests:
152
172
  checklist.append(
153
173
  f"Run {len(tests)} covering test symbol(s) that reach the change."
154
174
  )
175
+ elif not _has_test_root:
176
+ checklist.append(
177
+ "Covering tests were not measured: this repository has no test source "
178
+ "root, so `covering_tests.count` is null rather than 0."
179
+ )
155
180
  else:
156
181
  checklist.append(
157
182
  "No covering tests reach the change — no existing test exercises this blast radius."
@@ -178,10 +203,20 @@ def build_change_plan(
178
203
  "count": len(prod),
179
204
  "components": prod[:_LIST_CAP],
180
205
  },
181
- "covering_tests": {
182
- "count": len(tests),
183
- "tests": tests[:_LIST_CAP],
184
- },
206
+ "covering_tests": (
207
+ {
208
+ "count": len(tests),
209
+ "tests": tests[:_LIST_CAP],
210
+ "answered": True,
211
+ }
212
+ if _has_test_root else
213
+ {
214
+ "count": None,
215
+ "tests": [],
216
+ "answered": False,
217
+ "reason": "no test source root in this repository — nothing to measure",
218
+ }
219
+ ),
185
220
  "affected_endpoints": {
186
221
  "count": len(ep_strings),
187
222
  "endpoints": ep_strings[:_LIST_CAP],