sourcecode 4.10.3__tar.gz → 4.10.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. {sourcecode-4.10.3 → sourcecode-4.10.4}/CHANGELOG.md +58 -0
  2. {sourcecode-4.10.3 → sourcecode-4.10.4}/PKG-INFO +6 -6
  3. {sourcecode-4.10.3 → sourcecode-4.10.4}/README.md +2 -2
  4. {sourcecode-4.10.3 → sourcecode-4.10.4}/pyproject.toml +4 -4
  5. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/architectural_baseline.py +34 -5
  7. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/cache_model.py +41 -1
  8. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/cli.py +71 -9
  9. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/provenance.py +18 -10
  10. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/release_info.py +1 -1
  11. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/spring_security_audit.py +10 -0
  12. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/verify_edit.py +31 -0
  13. {sourcecode-4.10.3 → sourcecode-4.10.4}/.github/workflows/build-windows.yml +0 -0
  14. {sourcecode-4.10.3 → sourcecode-4.10.4}/.gitignore +0 -0
  15. {sourcecode-4.10.3 → sourcecode-4.10.4}/.ruff.toml +0 -0
  16. {sourcecode-4.10.3 → sourcecode-4.10.4}/CLAUDE.md +0 -0
  17. {sourcecode-4.10.3 → sourcecode-4.10.4}/CONTRIBUTING.md +0 -0
  18. {sourcecode-4.10.3 → sourcecode-4.10.4}/LICENSE +0 -0
  19. {sourcecode-4.10.3 → sourcecode-4.10.4}/SECURITY.md +0 -0
  20. {sourcecode-4.10.3 → sourcecode-4.10.4}/raw +0 -0
  21. {sourcecode-4.10.3 → sourcecode-4.10.4}/scripts/compare_integration_engines.py +0 -0
  22. {sourcecode-4.10.3 → sourcecode-4.10.4}/scripts/customer_smoke_test.sh +0 -0
  23. {sourcecode-4.10.3 → sourcecode-4.10.4}/scripts/generate_jdk_exports.py +0 -0
  24. {sourcecode-4.10.3 → sourcecode-4.10.4}/scripts/perf_harness.py +0 -0
  25. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/adaptive_scanner.py +0 -0
  26. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/archetype.py +0 -0
  27. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/architectural_delta.py +0 -0
  28. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/architecture_analyzer.py +0 -0
  29. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/architecture_summary.py +0 -0
  30. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/ast_extractor.py +0 -0
  31. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/audit_report.py +0 -0
  32. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/baseline_autocapture.py +0 -0
  33. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/cache.py +0 -0
  34. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/call_surface.py +0 -0
  35. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/caller_metrics.py +0 -0
  36. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/caller_reach.py +0 -0
  37. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/canonical_ir.py +0 -0
  38. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/chain_rules.py +0 -0
  39. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/change_plan.py +0 -0
  40. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/cir_graphs.py +0 -0
  41. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/classifier.py +0 -0
  42. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/client_calls.py +0 -0
  43. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/code_notes_analyzer.py +0 -0
  44. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/compare.py +0 -0
  45. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/confidence_analyzer.py +0 -0
  46. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/constraint_diff.py +0 -0
  47. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/context_cache.py +0 -0
  48. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/context_graph.py +0 -0
  49. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/context_scorer.py +0 -0
  50. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/context_summarizer.py +0 -0
  51. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/contract_diff.py +0 -0
  52. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/contract_init.py +0 -0
  53. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/contract_model.py +0 -0
  54. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/contract_pipeline.py +0 -0
  55. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/coverage_parser.py +0 -0
  56. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/data_exposure.py +0 -0
  57. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/data_labels.py +0 -0
  58. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/defect_identity.py +0 -0
  59. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/degradation.py +0 -0
  60. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/dependency_analyzer.py +0 -0
  61. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/deployment_prefix.py +0 -0
  62. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/__init__.py +0 -0
  63. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/base.py +0 -0
  64. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/csproj_parser.py +0 -0
  65. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/dart.py +0 -0
  66. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/dotnet.py +0 -0
  67. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/elixir.py +0 -0
  68. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/go.py +0 -0
  69. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/heuristic.py +0 -0
  70. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/hybrid.py +0 -0
  71. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/java.py +0 -0
  72. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/jvm_ext.py +0 -0
  73. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/nodejs.py +0 -0
  74. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/parsers.py +0 -0
  75. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/php.py +0 -0
  76. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/project.py +0 -0
  77. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/python.py +0 -0
  78. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/ruby.py +0 -0
  79. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/rust.py +0 -0
  80. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/systems.py +0 -0
  81. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/terraform.py +0 -0
  82. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/detectors/tooling.py +0 -0
  83. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/doc_analyzer.py +0 -0
  84. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/dynamic_argument_surface.py +0 -0
  85. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/endpoint_literals.py +0 -0
  86. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/endpoint_metrics.py +0 -0
  87. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/entrypoint_classifier.py +0 -0
  88. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/env_analyzer.py +0 -0
  89. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/envelope.py +0 -0
  90. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/environment_resolution.py +0 -0
  91. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/error_schema.py +0 -0
  92. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/evidence_provider.py +0 -0
  93. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/explain.py +0 -0
  94. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/facts/__init__.py +0 -0
  95. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/facts/registry.json +0 -0
  96. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/file_chunker.py +0 -0
  97. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/file_classifier.py +0 -0
  98. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/filter_surface.py +0 -0
  99. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/format_contract.py +0 -0
  100. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/fqn_utils.py +0 -0
  101. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/git_analyzer.py +0 -0
  102. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/git_checkout.py +0 -0
  103. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/graph_analyzer.py +0 -0
  104. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/graph_evidence.py +0 -0
  105. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/hibernate_strat.py +0 -0
  106. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/identity_fallback.py +0 -0
  107. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/integration_coordinates.py +0 -0
  108. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/jdk_exports.py +0 -0
  109. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/license.py +0 -0
  110. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp/__init__.py +0 -0
  111. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  112. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  113. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  114. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  115. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  116. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp/orchestrator.py +0 -0
  117. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp/registry.py +0 -0
  118. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp/runner.py +0 -0
  119. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp/server.py +0 -0
  120. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/mcp_nudge.py +0 -0
  121. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/metrics_analyzer.py +0 -0
  122. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/migrate_check.py +0 -0
  123. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/migration_blast.py +0 -0
  124. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/non_coverage.py +0 -0
  125. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/openapi_surface.py +0 -0
  126. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/openrewrite_recipe.py +0 -0
  127. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/output_budget.py +0 -0
  128. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/parse_cache.py +0 -0
  129. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/path_admission.py +0 -0
  130. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/path_filters.py +0 -0
  131. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/perf.py +0 -0
  132. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/pipe_contract.py +0 -0
  133. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/posture.py +0 -0
  134. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/pr_comment_renderer.py +0 -0
  135. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/pr_impact.py +0 -0
  136. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/prepare_context.py +0 -0
  137. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/progress.py +0 -0
  138. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/ranking_engine.py +0 -0
  139. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/readiness_timeline.py +0 -0
  140. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/reconciliation.py +0 -0
  141. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/redactor.py +0 -0
  142. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/reference_facts.py +0 -0
  143. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/relevance_scorer.py +0 -0
  144. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/remedies.py +0 -0
  145. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/rename_refactor.py +0 -0
  146. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/repo_classifier.py +0 -0
  147. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/repository_ir.py +0 -0
  148. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/__init__.py +0 -0
  149. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/context.py +0 -0
  150. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/errors.py +0 -0
  151. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/executor.py +0 -0
  152. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/planner.py +0 -0
  153. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/query.py +0 -0
  154. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/request.py +0 -0
  155. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/resolution.py +0 -0
  156. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/result.py +0 -0
  157. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/retriever.py +0 -0
  158. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/runtime.py +0 -0
  159. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/steps.py +0 -0
  160. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  161. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/steps_graph.py +0 -0
  162. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/steps_impact.py +0 -0
  163. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/steps_intf.py +0 -0
  164. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/steps_struct.py +0 -0
  165. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  166. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/ris.py +0 -0
  167. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/risk.py +0 -0
  168. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/rule_catalog.py +0 -0
  169. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/runs.py +0 -0
  170. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/runtime_classifier.py +0 -0
  171. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/sarif.py +0 -0
  172. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/scanner.py +0 -0
  173. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/schema.py +0 -0
  174. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  175. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/security_config.py +0 -0
  176. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/security_config_scan.py +0 -0
  177. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/security_posture.py +0 -0
  178. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/semantic_analyzer.py +0 -0
  179. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/semantic_impact_engine.py +0 -0
  180. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/semantic_integration_engine.py +0 -0
  181. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/semantic_services.py +0 -0
  182. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/serializer.py +0 -0
  183. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/servlet_surface.py +0 -0
  184. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/source_text.py +0 -0
  185. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/spring_event_topology.py +0 -0
  186. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/spring_findings.py +0 -0
  187. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/spring_impact.py +0 -0
  188. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/spring_model.py +0 -0
  189. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/spring_profiles.py +0 -0
  190. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/spring_properties.py +0 -0
  191. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/spring_semantic.py +0 -0
  192. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/spring_tx_analyzer.py +0 -0
  193. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/summarizer.py +0 -0
  194. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/target_admission.py +0 -0
  195. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/telemetry/__init__.py +0 -0
  196. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/telemetry/config.py +0 -0
  197. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/telemetry/consent.py +0 -0
  198. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/telemetry/events.py +0 -0
  199. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/telemetry/filters.py +0 -0
  200. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/telemetry/transport.py +0 -0
  201. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/test_gap_ranking.py +0 -0
  202. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/test_sources.py +0 -0
  203. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/token_estimate.py +0 -0
  204. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/tree_utils.py +0 -0
  205. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/type_usage_surface.py +0 -0
  206. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/validation_inference.py +0 -0
  207. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/validation_surface.py +0 -0
  208. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/verify_repo.py +0 -0
  209. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/verify_rules.py +0 -0
  210. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/version_check.py +0 -0
  211. {sourcecode-4.10.3 → sourcecode-4.10.4}/src/sourcecode/workspace.py +0 -0
  212. {sourcecode-4.10.3 → sourcecode-4.10.4}/supabase/functions/README.md +0 -0
  213. {sourcecode-4.10.3 → sourcecode-4.10.4}/supabase/functions/get-license/index.ts +0 -0
  214. {sourcecode-4.10.3 → sourcecode-4.10.4}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  215. {sourcecode-4.10.3 → sourcecode-4.10.4}/supabase/functions/telemetry/index.ts +0 -0
  216. {sourcecode-4.10.3 → sourcecode-4.10.4}/supabase/sql/license_event_ordering.sql +0 -0
  217. {sourcecode-4.10.3 → sourcecode-4.10.4}/supabase/sql/licensing_schema.sql +0 -0
  218. {sourcecode-4.10.3 → sourcecode-4.10.4}/supabase/sql/telemetry_events.sql +0 -0
@@ -2,6 +2,64 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [4.10.4] — 2026-08-07
6
+
7
+ First release of the queue opened by field evaluation #13. Four of its rows close,
8
+ one closes largely, and the suite goes green: `master` was carrying four failing
9
+ tests before this release, none of them noticed by the release that shipped over
10
+ them.
11
+
12
+ ### Fixed
13
+
14
+ - **`baseline` no longer emits a JSON object keyed by symbol** (C3-62). PowerShell's
15
+ `ConvertFrom-Json` folds object keys case-insensitively, so two Java symbols
16
+ differing only by case — legal, Java is case-sensitive — collided and aborted
17
+ parsing of the *entire* baseline document on the default Windows consumer. `fan_in`
18
+ now serialises as a sorted array of `{symbol, fan_in}`, the shape `hotspots` already
19
+ used. Baselines captured before this release keep diffing and trending: the reader
20
+ accepts both shapes.
21
+ - **`spring-audit` no longer prints `done` before the answer exists** (C3-54, part 1).
22
+ The completion line was emitted inside the `finally` around the analysis, so a
23
+ teardown landing between it and the write left 6.8 minutes of CPU and no output
24
+ file behind a reported success. It now prints after the write it describes. The
25
+ same reorder also stops `done` from being claimed on a run whose analysis *raised*.
26
+ - **`ask cache model` answers in ~830 tokens instead of ~2 590** (C3-63). One line per
27
+ layer and per command by default; the per-command measured timings and notes move
28
+ behind `--full`. Every command and every layer is still named, and the invalidation
29
+ rule is still stated — the density fix is not a coverage cut. `--json` and
30
+ `--markdown` are unchanged.
31
+ - **The evidence manifest publishes how many claims exist, not how many fitted**
32
+ (invariant I-2). The claim builder returned the moment it reached its 200-claim cap,
33
+ so the real total was never computed and the cap was published in its place, with no
34
+ field carrying the truth. `claims_cap` now reads `{total, total_indexed, capped}`
35
+ with `total` measured before truncation. `capped` is also corrected: exactly 200
36
+ claims reported a cut that had not happened.
37
+
38
+ ### Performance
39
+
40
+ - **The `spring-audit` rule pass is 7.8× faster on a large repository** (C3-61):
41
+ 42.1 s → 5.4 s measured on keycloak (5 486 non-test sources, 59 144 symbols), with
42
+ the finding set byte-identical before and after. The cost was not spread across the
43
+ file population — one rule was 94 % of it, and inside it `_proxy_bypass` scanned
44
+ every source for every inferred gate token before establishing that the source never
45
+ mentions the token: 98.9 % of the work was provably empty. The guard is an
46
+ equivalence, not a heuristic, and no threads or processes were added, so output
47
+ determinism is untouched. The rule pass is no longer the dominant phase.
48
+
49
+ ### Tests
50
+
51
+ - The four batteries that were red on `master` before this release are green:
52
+ `test_flag_normalization` compared a wall-clock timestamp between two runs;
53
+ `test_path_admission` had no invocation row for `audit-report`; and
54
+ `test_invariant_counts_from_measurement` failed on `provenance.py` (a real defect,
55
+ fixed above) and on `audit_report.py` (a false positive from a hand-maintained key
56
+ catalogue, now a rule on the key's final token — the audit's anti-vacuous guard is
57
+ extended rather than trusted).
58
+ - Every fix in this release ships a regression battery that was mutation-checked:
59
+ reverting the fix fails the new tests. Two of them were rewritten after the first
60
+ draft passed under that mutation — a summary that exists but is never reached by the
61
+ command, and an error path that exits before the spinner is created.
62
+
5
63
  ## [4.10.3] — 2026-08-05
6
64
 
7
65
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 4.10.3
3
+ Version: 4.10.4
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
@@ -18,7 +18,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
18
  Classifier: Topic :: Utilities
19
19
  Requires-Python: >=3.9
20
20
  Requires-Dist: defusedxml>=0.7
21
- Requires-Dist: mcp>=1.0.0
21
+ Requires-Dist: mcp<2,>=1.2
22
22
  Requires-Dist: pathspec>=1.0
23
23
  Requires-Dist: ruamel-yaml>=0.18
24
24
  Requires-Dist: tomli>=2.0; python_version < '3.11'
@@ -28,12 +28,12 @@ Requires-Dist: tree-sitter-javascript>=0.21; extra == 'ast'
28
28
  Requires-Dist: tree-sitter-typescript>=0.21; extra == 'ast'
29
29
  Requires-Dist: tree-sitter>=0.21; extra == 'ast'
30
30
  Provides-Extra: dev
31
- Requires-Dist: mcp>=1.0.0; extra == 'dev'
31
+ Requires-Dist: mcp<2,>=1.2; extra == 'dev'
32
32
  Requires-Dist: mypy>=1.10; extra == 'dev'
33
33
  Requires-Dist: pytest>=8; extra == 'dev'
34
34
  Requires-Dist: ruff>=0.15; extra == 'dev'
35
35
  Provides-Extra: mcp
36
- Requires-Dist: mcp>=1.0.0; extra == 'mcp'
36
+ Requires-Dist: mcp<2,>=1.2; extra == 'mcp'
37
37
  Description-Content-Type: text/markdown
38
38
 
39
39
  # ASK Engine
@@ -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.3-blue)
45
+ ![Version](https://img.shields.io/badge/version-4.10.4-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.3 — and, on a build that has aged,
109
+ ask version # ask 4.10.4 — and, on a build that has aged,
110
110
  # how many releases have probably shipped since
111
111
  ```
112
112
 
@@ -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.3-blue)
7
+ ![Version](https://img.shields.io/badge/version-4.10.4-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.3 — and, on a build that has aged,
71
+ ask version # ask 4.10.4 — and, on a build that has aged,
72
72
  # how many releases have probably shipped since
73
73
  ```
74
74
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "4.10.3"
7
+ version = "4.10.4"
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"
@@ -30,7 +30,7 @@ dependencies = [
30
30
  "pathspec>=1.0",
31
31
  "ruamel.yaml>=0.18",
32
32
  "tomli>=2.0; python_version < '3.11'",
33
- "mcp>=1.0.0",
33
+ "mcp>=1.2,<2",
34
34
  "defusedxml>=0.7",
35
35
  ]
36
36
 
@@ -43,7 +43,7 @@ dev = [
43
43
  "pytest>=8",
44
44
  "ruff>=0.15",
45
45
  "mypy>=1.10",
46
- "mcp>=1.0.0",
46
+ "mcp>=1.2,<2",
47
47
  ]
48
48
  ast = [
49
49
  "tree-sitter>=0.21",
@@ -51,7 +51,7 @@ ast = [
51
51
  "tree-sitter-javascript>=0.21",
52
52
  ]
53
53
  mcp = [
54
- "mcp>=1.0.0",
54
+ "mcp>=1.2,<2",
55
55
  ]
56
56
 
57
57
  [[tool.mypy.overrides]]
@@ -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.3"
7
+ __version__ = "4.10.4"
@@ -33,7 +33,7 @@ import json
33
33
  import subprocess
34
34
  from datetime import datetime, timezone
35
35
  from pathlib import Path
36
- from typing import TYPE_CHECKING
36
+ from typing import TYPE_CHECKING, Any
37
37
 
38
38
  from sourcecode import __version__ as TOOL_VERSION
39
39
  from sourcecode.architectural_delta import ArchMetrics, diff_metrics, extract_metrics
@@ -107,6 +107,35 @@ def _hotspots(fan_in: dict[str, int], top_n: int) -> list[dict]:
107
107
  return [{"symbol": s, "fan_in": c} for s, c in ranked[:top_n]]
108
108
 
109
109
 
110
+ def _fan_in_to_list(fan_in: dict[str, int]) -> list[dict]:
111
+ """Serialise the full fan-in map as an array of ``{symbol, fan_in}`` objects,
112
+ sorted by symbol — never a JSON object keyed by symbol.
113
+
114
+ C3-62: PowerShell's `ConvertFrom-Json` folds object keys case-insensitively, so
115
+ two distinct Java symbols differing only by case (legal — Java is case-sensitive)
116
+ collapse into one key and abort parsing the *whole* baseline document. `hotspots`
117
+ already used this array shape (`_hotspots`); the full map stored beside it did
118
+ not. An array of objects has no such ceiling on any consumer."""
119
+ return [{"symbol": s, "fan_in": c} for s, c in sorted(fan_in.items())]
120
+
121
+
122
+ def _fan_in_from_stored(value: Any) -> dict[str, int]:
123
+ """Read a stored ``fan_in`` field back into a symbol → count map.
124
+
125
+ Accepts both the array-of-objects shape (C3-62, current) and the legacy
126
+ object-keyed-by-symbol shape (baselines captured before this fix) so an older
127
+ baseline already committed to disk keeps diffing and trending correctly."""
128
+ if isinstance(value, list):
129
+ return {
130
+ str(item["symbol"]): int(item.get("fan_in", 0))
131
+ for item in value
132
+ if isinstance(item, dict) and "symbol" in item
133
+ }
134
+ if isinstance(value, dict):
135
+ return {str(k): int(v) for k, v in value.items()}
136
+ return {}
137
+
138
+
110
139
  def build_baseline(
111
140
  cir: "CanonicalRepositoryIR",
112
141
  *,
@@ -126,7 +155,6 @@ def build_baseline(
126
155
  that share them."""
127
156
  m = extract_metrics(cir)
128
157
  endpoints = sorted([meth, path] for (meth, path) in m.endpoints)
129
- fan_in = dict(sorted(m.fan_in.items()))
130
158
  cycles = sorted((sorted(c) for c in m.cycles), key=lambda mm: (len(mm), mm))
131
159
  return {
132
160
  "schema": ARCH_BASELINE_SCHEMA,
@@ -145,7 +173,7 @@ def build_baseline(
145
173
  },
146
174
  "hotspots": _hotspots(m.fan_in, hotspot_n),
147
175
  "endpoint_surface": endpoints,
148
- "fan_in": fan_in,
176
+ "fan_in": _fan_in_to_list(m.fan_in),
149
177
  "import_cycles": cycles,
150
178
  "env": env if env is not None else collect_env(),
151
179
  "provenance": (
@@ -209,7 +237,7 @@ def baseline_to_metrics(baseline: dict) -> ArchMetrics:
209
237
  endpoint_count=int(totals.get("endpoints", 0)),
210
238
  dependency_edge_count=int(totals.get("dependency_edges", 0)),
211
239
  endpoints=endpoints,
212
- fan_in={str(k): int(v) for k, v in (baseline.get("fan_in") or {}).items()},
240
+ fan_in=_fan_in_from_stored(baseline.get("fan_in")),
213
241
  cycles=frozenset(
214
242
  frozenset(str(m) for m in members)
215
243
  for members in baseline.get("import_cycles", [])
@@ -397,9 +425,10 @@ def build_trend(baselines: list[dict]) -> dict:
397
425
  if isinstance(h, dict) and h.get("symbol"):
398
426
  tracked.add(str(h["symbol"]))
399
427
 
428
+ fan_in_by_baseline = [_fan_in_from_stored(b.get("fan_in")) for b in baselines]
400
429
  hotspot_trends: list[dict] = []
401
430
  for sym in sorted(tracked):
402
- series = [int((b.get("fan_in") or {}).get(sym, 0)) for b in baselines]
431
+ series = [fmap.get(sym, 0) for fmap in fan_in_by_baseline]
403
432
  first, last = series[0], series[-1]
404
433
  hotspot_trends.append({
405
434
  "symbol": sym,
@@ -302,8 +302,48 @@ def render_markdown() -> str:
302
302
  return "\n".join(out)
303
303
 
304
304
 
305
+ def render_summary() -> str:
306
+ """The model as `ask cache model` prints it by default (C3-63).
307
+
308
+ One line per layer and one line per command — every command named (nothing
309
+ curated out, so the same completeness the battery checks against `render_text`
310
+ holds here too), but without the per-command prose (`note`) and timing
311
+ (`measured`) detail that made the default ~2 500 tokens of prose in a CLI whose
312
+ own selling argument is token economy. That detail is still one flag away.
313
+ """
314
+ lines: list[str] = []
315
+ lines.append(
316
+ "Invalidation: every layer keys on the exact tree state — any change to the "
317
+ "analysed files invalidates it, committed or not."
318
+ )
319
+ lines.append("")
320
+ lines.append("Layers")
321
+ id_width = max(len(lyr.id) for lyr in LAYERS)
322
+ for lyr in LAYERS:
323
+ lines.append(f" {lyr.id.ljust(id_width)} {lyr.stores}")
324
+ lines.append("")
325
+ lines.append(f"What a warm gives each command ({REFERENCE_REPOSITORY})")
326
+ cmd_width = max(len(c.command) for c in COMMANDS)
327
+ warm_width = max(len(label) for label in _WARM_LABEL.values())
328
+ for cmd in COMMANDS:
329
+ repeat = "repeat cached" if cmd.repeat else "recomputes"
330
+ layers = ", ".join(cmd.reads) or "none"
331
+ lines.append(
332
+ f" {cmd.command.ljust(cmd_width)} {_WARM_LABEL[cmd.warm].ljust(warm_width)} "
333
+ f"({repeat}) [{layers}]"
334
+ )
335
+ lines.append("")
336
+ lines.append(
337
+ "Full detail (measured timings, per-command notes): ask cache model --full"
338
+ )
339
+ lines.append("Tables for docs: ask cache model --markdown · machine-readable: --json")
340
+ return "\n".join(lines)
341
+
342
+
305
343
  def render_text() -> str:
306
- """The model as `ask cache model` prints it."""
344
+ """The model in full: every layer and command with its measured timing and note.
345
+
346
+ `ask cache model --full`. See `render_summary` for the default."""
307
347
  lines: list[str] = []
308
348
  lines.append("Invalidation — one rule for every layer")
309
349
  lines.append(" Every layer keys on the exact tree state. Any change to the analysed files")
@@ -15,7 +15,7 @@ from typing import Any, NoReturn, Optional, cast
15
15
  import typer
16
16
 
17
17
  from sourcecode import __version__
18
- from sourcecode.error_schema import INVALID_INPUT_CODE, build_error_envelope
18
+ from sourcecode.error_schema import EXECUTION_FAILED_CODE, INVALID_INPUT_CODE, build_error_envelope
19
19
  from sourcecode.entrypoint_classifier import is_production_entry_point, normalize_entry_point
20
20
  from sourcecode.progress import Progress
21
21
  from sourcecode import perf
@@ -7402,7 +7402,14 @@ def spring_audit_cmd(
7402
7402
  except Exception:
7403
7403
  pass
7404
7404
  finally:
7405
- _prog.finish()
7405
+ # C3-54: `done` must not claim the answer exists before it does. The
7406
+ # field log showed `[ask] done (6.8m)` printed, then the process torn
7407
+ # down before `-o` was written — a false success on 6.8 minutes of
7408
+ # CPU. `stop()` here only clears the spinner (satisfies the standing
7409
+ # rule that a raised error must never print under a live spinner
7410
+ # frame); it prints no completion line. `_prog.finish()` runs below,
7411
+ # after the render AND the write have actually happened.
7412
+ _prog.stop()
7406
7413
  if table:
7407
7414
  output = _render_spring_audit_table(
7408
7415
  combined, rule_filter=rule_filter, top_n=top_n, min_severity=min_severity
@@ -7415,6 +7422,7 @@ def spring_audit_cmd(
7415
7422
  _total = combined.summary.get("total_findings", 0)
7416
7423
  _emit_command_output(output, output_path, copy,
7417
7424
  success_msg=f"Spring audit written to {output_path} ({_total} findings)")
7425
+ _prog.finish()
7418
7426
 
7419
7427
  if ci and combined.findings:
7420
7428
  raise typer.Exit(code=1)
@@ -11569,12 +11577,33 @@ def mcp_status(copy: bool = _copy_option()) -> None:
11569
11577
  _answer.say("")
11570
11578
 
11571
11579
  # Stage 1: Dependencies
11580
+ # C3-57: check the symbol this CLI actually consumes (FastMCP), not just
11581
+ # whether the root `mcp` package imports — `mcp>=2.0` imports cleanly but
11582
+ # has no `mcp.server.fastmcp` module, so importing only the root package
11583
+ # reports a green check for a build that will fail on `mcp serve`/`mcp list-tools`.
11584
+ def _installed_mcp_version() -> str:
11585
+ # The `mcp` package does not set `__version__`; read installed
11586
+ # distribution metadata instead, and fall back gracefully off a
11587
+ # normal install (editable installs, vendored copies, etc.).
11588
+ try:
11589
+ from importlib.metadata import version as _dist_version
11590
+ return _dist_version("mcp")
11591
+ except Exception:
11592
+ return "unknown"
11593
+
11572
11594
  try:
11573
- import mcp as _mcp_pkg # noqa: F401
11574
- _answer.say("Dependencies ✓ installed")
11595
+ from mcp.server.fastmcp import FastMCP # noqa: F401
11596
+ _mcp_ver = _installed_mcp_version()
11597
+ _answer.say(f"Dependencies ✓ installed (mcp {_mcp_ver})")
11575
11598
  except ImportError:
11576
- _answer.say("Dependencies ✗ missing")
11577
- _answer.say(" Fix: pip install sourcecode[mcp]")
11599
+ try:
11600
+ import mcp as _mcp_pkg # noqa: F401
11601
+ _mcp_ver = _installed_mcp_version()
11602
+ _answer.say(f"Dependencies ✗ mcp {_mcp_ver} incompatible (requires mcp>=1.2,<2)")
11603
+ _answer.say(" Fix: pip install 'sourcecode[mcp]' --upgrade")
11604
+ except ImportError:
11605
+ _answer.say("Dependencies ✗ missing")
11606
+ _answer.say(" Fix: pip install sourcecode[mcp]")
11578
11607
  _answer.say("")
11579
11608
 
11580
11609
  clients = detect_clients()
@@ -11789,9 +11818,32 @@ def mcp_list_tools(
11789
11818
  import asyncio
11790
11819
  import json as _json
11791
11820
 
11792
- from sourcecode.mcp.server import mcp as _mcp
11821
+ # C3-57: this command previously let a raw Python traceback through when
11822
+ # the installed `mcp` package was incompatible (e.g. mcp>=2.0, which has
11823
+ # no mcp.server.fastmcp) — the only command in the catalogue to do so.
11824
+ # Route failures through the same structured error contract as every
11825
+ # other command instead.
11826
+ try:
11827
+ from sourcecode.mcp.server import mcp as _mcp
11828
+ tools = asyncio.run(_mcp.list_tools())
11829
+ except ImportError as exc:
11830
+ _emit_error_json(
11831
+ EXECUTION_FAILED_CODE,
11832
+ f"MCP server dependencies are not importable: {exc}",
11833
+ hint="Run 'ask mcp status' to check the installed mcp package version — "
11834
+ "this CLI requires mcp>=1.2,<2. Fix: pip install 'sourcecode[mcp]' --upgrade",
11835
+ expected="mcp>=1.2,<2 installed with mcp.server.fastmcp.FastMCP importable.",
11836
+ )
11837
+ raise typer.Exit(code=1)
11838
+ except Exception as exc:
11839
+ _emit_error_json(
11840
+ EXECUTION_FAILED_CODE,
11841
+ f"Failed to list MCP tools: {type(exc).__name__}: {exc}",
11842
+ hint="Run 'ask mcp status' to check MCP server health.",
11843
+ expected="A list of registered MCP tools.",
11844
+ )
11845
+ raise typer.Exit(code=1)
11793
11846
 
11794
- tools = asyncio.run(_mcp.list_tools())
11795
11847
  tools_sorted = sorted(tools, key=lambda t: t.name)
11796
11848
 
11797
11849
  if json_output:
@@ -12057,6 +12109,11 @@ def cache_model_cmd(
12057
12109
  path: Path = typer.Argument(Path("."), help="Repository path for CLI consistency (model is global)."),
12058
12110
  json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
12059
12111
  markdown: bool = typer.Option(False, "--markdown", help="Output the tables published in the user guide."),
12112
+ full: bool = typer.Option(
12113
+ False, "--full",
12114
+ help="Full prose: every command's measured timing and note (~2.5k tokens). "
12115
+ "Default is a compact one-line-per-command summary.",
12116
+ ),
12060
12117
  copy: bool = _copy_option(),
12061
12118
  ) -> None:
12062
12119
  """What each cache layer stores, what invalidates it, and what a warm helps.
@@ -12070,6 +12127,10 @@ def cache_model_cmd(
12070
12127
 
12071
12128
  One rule covers invalidation: every layer keys on the exact tree state, so any
12072
12129
  change to the analysed files invalidates it, committed or not.
12130
+
12131
+ \b
12132
+ Default output is a compact summary (C3-63) — every command named, without the
12133
+ per-command measured timing and note. Pass --full for that detail.
12073
12134
  """
12074
12135
  from sourcecode import cache_model as _cmodel
12075
12136
 
@@ -12089,7 +12150,8 @@ def cache_model_cmd(
12089
12150
  f"Scope: {Path(path).resolve()} (model is global; path accepted for "
12090
12151
  "cache command grammar consistency)\n\n"
12091
12152
  )
12092
- _emit_text_output(_prefix + _cmodel.render_text(), copy)
12153
+ _body = _cmodel.render_text() if full else _cmodel.render_summary()
12154
+ _emit_text_output(_prefix + _body, copy)
12093
12155
 
12094
12156
 
12095
12157
  @cache_app.command("freshness", short_help="RIS freshness relative to the current git HEAD")
@@ -105,8 +105,6 @@ def _claims_from_rows(command: str, payload: dict[str, Any]) -> list[dict[str, A
105
105
  if row.get("rule_ids"):
106
106
  claim["rule_ids"] = row["rule_ids"]
107
107
  claims.append(claim)
108
- if len(claims) >= _MAX_CLAIMS:
109
- return claims
110
108
  return claims
111
109
 
112
110
 
@@ -166,9 +164,16 @@ def build_evidence_manifest(
166
164
  ) -> dict[str, Any]:
167
165
  """Build a machine-readable provenance index for an existing payload."""
168
166
  root = Path(root).resolve()
169
- claims = _claims_from_rows(command, payload)
170
- claims.extend(_claims_from_java_lts(command, payload))
171
- claims = claims[:_MAX_CLAIMS]
167
+ # I-2: measure the whole population, THEN cut it. `_claims_from_rows` used to
168
+ # return the moment it reached the cap, so the number of claims the payload
169
+ # actually carries was never computed and could not be published — the manifest
170
+ # reported the cap itself as `total_indexed`, which is the exact shape ("the
171
+ # published count is the size of the rendering") this module exists to make
172
+ # auditable. Building every claim is arithmetic over rows already in memory.
173
+ all_claims = _claims_from_rows(command, payload)
174
+ all_claims.extend(_claims_from_java_lts(command, payload))
175
+ total_claims = len(all_claims)
176
+ claims = all_claims[:_MAX_CLAIMS]
172
177
  sources = sorted({
173
178
  str((claim.get("source") or {}).get("path"))
174
179
  for claim in claims
@@ -189,11 +194,14 @@ def build_evidence_manifest(
189
194
  "run": run,
190
195
  "claims": claims,
191
196
  "sources": [{"path": path} for path in sources],
192
- "claims_cap": (
193
- {"total_indexed": _MAX_CLAIMS, "capped": True}
194
- if len(claims) >= _MAX_CLAIMS else
195
- {"total_indexed": len(claims), "capped": False}
196
- ),
197
+ # `total` is the measurement; `total_indexed` is the size of this manifest.
198
+ # `capped` is strictly `total > cap`: a payload with exactly `_MAX_CLAIMS`
199
+ # claims had nothing removed and must not report that it did.
200
+ "claims_cap": {
201
+ "total": total_claims,
202
+ "total_indexed": min(total_claims, _MAX_CLAIMS),
203
+ "capped": total_claims > _MAX_CLAIMS,
204
+ },
197
205
  "embedded_manifests": _embedded_manifests(payload),
198
206
  "limits": {
199
207
  "statement": (
@@ -27,7 +27,7 @@ from typing import Any, Optional
27
27
  #: The date `__version__` was released, from that version's CHANGELOG entry.
28
28
  #: `tests/test_release_info.py` fails the build if the two disagree, so this is
29
29
  #: a copy of a fact rather than a second authority for it.
30
- RELEASE_DATE = "2026-08-05"
30
+ RELEASE_DATE = "2026-08-07"
31
31
 
32
32
  #: Releases per day, measured over the 30 releases before this one (the same
33
33
  #: computation the test re-runs against the CHANGELOG). Stable across window
@@ -621,6 +621,14 @@ class _GATE00XCustomGateAudit:
621
621
  findings: list[SpringFinding] = []
622
622
  ann_re = _annotation_call_re(token)
623
623
  for rel, source in sources:
624
+ # C3-61: `ann_re` is `@<token>\b…`, so it cannot match a source that does
625
+ # not contain the literal token, and neither can any prefix slice of it.
626
+ # Without this line every source pays a full method `finditer` plus one
627
+ # prefix regex per method before proving it had nothing to find — 98,9 %
628
+ # of (source, token) pairs on the measured repository. Same guard
629
+ # `_advice_body_rules` already carries.
630
+ if token not in source:
631
+ continue
624
632
  class_fqn = _java_fqn(source)
625
633
  annotated_methods: dict[str, tuple[int, str]] = {}
626
634
  for method in _JAVA_METHOD_RE.finditer(source):
@@ -712,6 +720,8 @@ class _GATE00XCustomGateAudit:
712
720
  findings: list[SpringFinding] = []
713
721
  ann_re = _annotation_call_re(token)
714
722
  for rel, source in sources:
723
+ if token not in source: # C3-61, same equivalence as `_proxy_bypass`
724
+ continue
715
725
  class_fqn = _java_fqn(source)
716
726
  for ann in ann_re.finditer(source):
717
727
  args = (ann.group("args") or "").strip()
@@ -685,6 +685,37 @@ def verdict(hvw: HeadVsWorking, root: Path) -> VerifyVerdict:
685
685
  exit code: 0=pass, 1=break (a blocking axis proved a regression), 2=unverified
686
686
  (a blocking axis could not be computed — honest, never a silent pass)."""
687
687
  root = _resolve_repo_root(Path(root))
688
+
689
+ # C3-60: an empty change set has one correct answer and it needs none of the
690
+ # six full-repository analyzers below. `working_cir_reused` is only True when
691
+ # `head_vs_working` positively confirmed nothing changed (C3-32's
692
+ # `git_answered and not all_changed`) — never a guess — in which case
693
+ # `cir_working IS cir_head` (the same object) and `changed_files` /
694
+ # `changed_build_files` are both empty by construction. Every axis diffs
695
+ # cir_head against cir_working, so diffing an object against itself can only
696
+ # ever prove "no change"; running the tx/security audits (and everything
697
+ # else) twice over the whole tree to demonstrate that took 8+ minutes on a
698
+ # clean 3 342-file monolith while `cache freshness` already had the answer
699
+ # in under a second. Short-circuit to the answer every axis would compute.
700
+ if hvw.working_cir_reused:
701
+ clean_axes = {
702
+ name: AxisResult(False)
703
+ for name in (
704
+ "contract_broken", "tx_boundary_changed", "security_delta",
705
+ "new_orphan_bean", "custom_rule_violated", "build_config_changed",
706
+ )
707
+ }
708
+ return VerifyVerdict(
709
+ verdict="pass",
710
+ axes=clean_axes,
711
+ changed_files=hvw.changed_files,
712
+ changed_build_files=hvw.changed_build_files,
713
+ blast_radius={"affected_entry_points": 0, "confidence": "high", "note": "no changed files"},
714
+ reasons=(),
715
+ exit_code=0,
716
+ head_sha=hvw.head_sha,
717
+ )
718
+
688
719
  axes: dict[str, AxisResult] = {
689
720
  "contract_broken": _contract_axis(hvw.cir_head, hvw.cir_working),
690
721
  "tx_boundary_changed": _tx_axis(hvw.cir_head, hvw.cir_working, root),
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes