sourcecode 4.12.0__tar.gz → 4.14.0__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 (231) hide show
  1. sourcecode-4.14.0/.github/workflows/perf-gate.yml +109 -0
  2. {sourcecode-4.12.0 → sourcecode-4.14.0}/CHANGELOG.md +127 -0
  3. {sourcecode-4.12.0 → sourcecode-4.14.0}/PKG-INFO +3 -3
  4. {sourcecode-4.12.0 → sourcecode-4.14.0}/README.md +2 -2
  5. {sourcecode-4.12.0 → sourcecode-4.14.0}/pyproject.toml +1 -1
  6. sourcecode-4.14.0/scripts/perf_gate.py +176 -0
  7. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/__init__.py +1 -1
  8. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/architectural_baseline.py +5 -0
  9. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/cache_model.py +53 -1
  10. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/cli.py +203 -4
  11. sourcecode-4.14.0/src/sourcecode/parallel.py +241 -0
  12. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/parse_cache.py +183 -1
  13. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/perf.py +268 -4
  14. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/progress.py +99 -46
  15. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/readiness_timeline.py +2 -0
  16. sourcecode-4.14.0/src/sourcecode/readonly.py +171 -0
  17. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/release_info.py +1 -1
  18. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/repository_ir.py +24 -1
  19. sourcecode-4.14.0/src/sourcecode/rule_pass.py +236 -0
  20. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/runs.py +3 -0
  21. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/security_config_scan.py +36 -5
  22. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_findings.py +82 -0
  23. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_security_audit.py +103 -13
  24. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_tx_analyzer.py +82 -14
  25. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/verify_edit.py +3 -0
  26. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/verify_repo.py +3 -0
  27. sourcecode-4.12.0/src/sourcecode/rule_pass.py +0 -112
  28. {sourcecode-4.12.0 → sourcecode-4.14.0}/.github/workflows/build-windows.yml +0 -0
  29. {sourcecode-4.12.0 → sourcecode-4.14.0}/.gitignore +0 -0
  30. {sourcecode-4.12.0 → sourcecode-4.14.0}/.ruff.toml +0 -0
  31. {sourcecode-4.12.0 → sourcecode-4.14.0}/CLAUDE.md +0 -0
  32. {sourcecode-4.12.0 → sourcecode-4.14.0}/CONTRIBUTING.md +0 -0
  33. {sourcecode-4.12.0 → sourcecode-4.14.0}/LICENSE +0 -0
  34. {sourcecode-4.12.0 → sourcecode-4.14.0}/SECURITY.md +0 -0
  35. {sourcecode-4.12.0 → sourcecode-4.14.0}/raw +0 -0
  36. {sourcecode-4.12.0 → sourcecode-4.14.0}/scripts/compare_integration_engines.py +0 -0
  37. {sourcecode-4.12.0 → sourcecode-4.14.0}/scripts/customer_smoke_test.sh +0 -0
  38. {sourcecode-4.12.0 → sourcecode-4.14.0}/scripts/generate_jdk_exports.py +0 -0
  39. {sourcecode-4.12.0 → sourcecode-4.14.0}/scripts/perf_harness.py +0 -0
  40. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/adaptive_scanner.py +0 -0
  41. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/archetype.py +0 -0
  42. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/architectural_delta.py +0 -0
  43. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/architecture_analyzer.py +0 -0
  44. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/architecture_summary.py +0 -0
  45. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/ast_extractor.py +0 -0
  46. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/audit_report.py +0 -0
  47. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/baseline_autocapture.py +0 -0
  48. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/cache.py +0 -0
  49. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/call_surface.py +0 -0
  50. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/caller_metrics.py +0 -0
  51. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/caller_reach.py +0 -0
  52. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/canonical_ir.py +0 -0
  53. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/chain_rules.py +0 -0
  54. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/change_plan.py +0 -0
  55. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/cir_graphs.py +0 -0
  56. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/classifier.py +0 -0
  57. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/client_calls.py +0 -0
  58. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  59. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/compare.py +0 -0
  60. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/confidence_analyzer.py +0 -0
  61. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/constraint_diff.py +0 -0
  62. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/container_wiring.py +0 -0
  63. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/context_cache.py +0 -0
  64. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/context_graph.py +0 -0
  65. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/context_scorer.py +0 -0
  66. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/context_summarizer.py +0 -0
  67. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/contract_diff.py +0 -0
  68. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/contract_init.py +0 -0
  69. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/contract_model.py +0 -0
  70. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/contract_pipeline.py +0 -0
  71. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/coverage_parser.py +0 -0
  72. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/data_exposure.py +0 -0
  73. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/data_labels.py +0 -0
  74. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/defect_identity.py +0 -0
  75. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/degradation.py +0 -0
  76. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/dependency_analyzer.py +0 -0
  77. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/deployment_prefix.py +0 -0
  78. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detach.py +0 -0
  79. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/__init__.py +0 -0
  80. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/base.py +0 -0
  81. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  82. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/dart.py +0 -0
  83. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/dotnet.py +0 -0
  84. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/elixir.py +0 -0
  85. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/go.py +0 -0
  86. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/heuristic.py +0 -0
  87. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/hybrid.py +0 -0
  88. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/java.py +0 -0
  89. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  90. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/nodejs.py +0 -0
  91. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/parsers.py +0 -0
  92. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/php.py +0 -0
  93. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/project.py +0 -0
  94. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/python.py +0 -0
  95. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/ruby.py +0 -0
  96. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/rust.py +0 -0
  97. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/systems.py +0 -0
  98. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/terraform.py +0 -0
  99. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/tooling.py +0 -0
  100. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/doc_analyzer.py +0 -0
  101. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/dynamic_argument_surface.py +0 -0
  102. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/endpoint_literals.py +0 -0
  103. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/endpoint_metrics.py +0 -0
  104. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  105. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/env_analyzer.py +0 -0
  106. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/envelope.py +0 -0
  107. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/environment_resolution.py +0 -0
  108. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/error_schema.py +0 -0
  109. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/evidence_provider.py +0 -0
  110. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/execution_plan.py +0 -0
  111. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/explain.py +0 -0
  112. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/facts/__init__.py +0 -0
  113. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/facts/registry.json +0 -0
  114. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/file_chunker.py +0 -0
  115. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/file_classifier.py +0 -0
  116. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/filter_surface.py +0 -0
  117. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/format_contract.py +0 -0
  118. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/fqn_utils.py +0 -0
  119. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/git_analyzer.py +0 -0
  120. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/git_checkout.py +0 -0
  121. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/graph_analyzer.py +0 -0
  122. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/graph_evidence.py +0 -0
  123. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/hibernate_strat.py +0 -0
  124. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/identity_fallback.py +0 -0
  125. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/integration_coordinates.py +0 -0
  126. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/jdk_exports.py +0 -0
  127. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/license.py +0 -0
  128. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/__init__.py +0 -0
  129. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/dependency_import.py +0 -0
  130. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  131. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  132. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  133. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  134. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  135. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/orchestrator.py +0 -0
  136. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/registry.py +0 -0
  137. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/runner.py +0 -0
  138. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/server.py +0 -0
  139. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp_nudge.py +0 -0
  140. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/metrics_analyzer.py +0 -0
  141. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/migrate_check.py +0 -0
  142. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/migration_blast.py +0 -0
  143. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/non_coverage.py +0 -0
  144. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/openapi_surface.py +0 -0
  145. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/openrewrite_recipe.py +0 -0
  146. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/output_budget.py +0 -0
  147. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/output_encoding.py +0 -0
  148. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/path_admission.py +0 -0
  149. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/path_filters.py +0 -0
  150. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/phased_run.py +0 -0
  151. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/pipe_contract.py +0 -0
  152. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/posture.py +0 -0
  153. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/pr_comment_renderer.py +0 -0
  154. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/pr_impact.py +0 -0
  155. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/prepare_context.py +0 -0
  156. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/provenance.py +0 -0
  157. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/ranking_engine.py +0 -0
  158. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/reconciliation.py +0 -0
  159. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/redactor.py +0 -0
  160. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/reference_facts.py +0 -0
  161. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/relevance_scorer.py +0 -0
  162. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/remedies.py +0 -0
  163. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/rename_refactor.py +0 -0
  164. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/repo_classifier.py +0 -0
  165. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/__init__.py +0 -0
  166. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/context.py +0 -0
  167. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/errors.py +0 -0
  168. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/executor.py +0 -0
  169. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/planner.py +0 -0
  170. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/query.py +0 -0
  171. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/request.py +0 -0
  172. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/resolution.py +0 -0
  173. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/result.py +0 -0
  174. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/retriever.py +0 -0
  175. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/runtime.py +0 -0
  176. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps.py +0 -0
  177. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  178. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_graph.py +0 -0
  179. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_impact.py +0 -0
  180. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_intf.py +0 -0
  181. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_struct.py +0 -0
  182. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  183. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/ris.py +0 -0
  184. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/risk.py +0 -0
  185. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/rule_catalog.py +0 -0
  186. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/runtime_classifier.py +0 -0
  187. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/sarif.py +0 -0
  188. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/scanner.py +0 -0
  189. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/schema.py +0 -0
  190. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  191. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/security_config.py +0 -0
  192. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/security_posture.py +0 -0
  193. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/semantic_analyzer.py +0 -0
  194. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/semantic_impact_engine.py +0 -0
  195. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/semantic_integration_engine.py +0 -0
  196. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/semantic_services.py +0 -0
  197. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/serializer.py +0 -0
  198. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/servlet_surface.py +0 -0
  199. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/source_text.py +0 -0
  200. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_event_topology.py +0 -0
  201. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_impact.py +0 -0
  202. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_model.py +0 -0
  203. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_profiles.py +0 -0
  204. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_properties.py +0 -0
  205. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_semantic.py +0 -0
  206. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/summarizer.py +0 -0
  207. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/target_admission.py +0 -0
  208. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/__init__.py +0 -0
  209. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/config.py +0 -0
  210. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/consent.py +0 -0
  211. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/events.py +0 -0
  212. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/filters.py +0 -0
  213. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/transport.py +0 -0
  214. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/test_gap_ranking.py +0 -0
  215. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/test_sources.py +0 -0
  216. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/text_input.py +0 -0
  217. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/token_estimate.py +0 -0
  218. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/tree_utils.py +0 -0
  219. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/type_usage_surface.py +0 -0
  220. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/validation_inference.py +0 -0
  221. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/validation_surface.py +0 -0
  222. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/verify_rules.py +0 -0
  223. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/version_check.py +0 -0
  224. {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/workspace.py +0 -0
  225. {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/functions/README.md +0 -0
  226. {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/functions/get-license/index.ts +0 -0
  227. {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  228. {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/functions/telemetry/index.ts +0 -0
  229. {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/sql/license_event_ordering.sql +0 -0
  230. {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/sql/licensing_schema.sql +0 -0
  231. {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/sql/telemetry_events.sql +0 -0
@@ -0,0 +1,109 @@
1
+ name: Performance regression gate
2
+
3
+ # F-AP. Field evaluation #16 measured seven commands 2-3.4x slower on a minor
4
+ # release while every payload stayed byte-identical, and named the cause:
5
+ # "no es que haya regresiones - es que no hay nada que las detecte".
6
+ # This is the detector. The threshold, the repository and the gated command
7
+ # population are declared in `sourcecode.perf` and documented in
8
+ # `docs/perf/REGRESSION-GATE.md`; nothing here carries a number of its own.
9
+
10
+ on:
11
+ schedule:
12
+ # Nightly, after the day's merges have settled.
13
+ - cron: "0 3 * * *"
14
+ workflow_dispatch:
15
+ inputs:
16
+ baseline_ref:
17
+ description: "Baseline directory under docs/perf/baselines (default: gate-latest)"
18
+ required: false
19
+ default: "gate-latest"
20
+
21
+ concurrency:
22
+ group: perf-gate
23
+ cancel-in-progress: false
24
+
25
+ jobs:
26
+ gate:
27
+ runs-on: ubuntu-latest
28
+ timeout-minutes: 120
29
+ env:
30
+ # Declares the host class so nightly runs on ephemeral runners are
31
+ # comparable at all. Recorded in every artifact as `env.host_id_declared`,
32
+ # so the declaration is visible rather than silent.
33
+ SOURCECODE_PERF_HOST_ID: github-ubuntu-latest
34
+ BASELINE_DIR: docs/perf/baselines/${{ github.event.inputs.baseline_ref || 'gate-latest' }}
35
+
36
+ steps:
37
+ - name: Checkout
38
+ uses: actions/checkout@v4
39
+
40
+ - name: Setup Python
41
+ uses: actions/setup-python@v5
42
+ with:
43
+ python-version: "3.11"
44
+
45
+ - name: Install
46
+ run: |
47
+ python -m pip install --upgrade pip
48
+ pip install .
49
+
50
+ - name: Prove the gate is armed
51
+ # A green gate is evidence only when a red one is reachable in the same
52
+ # job with the same constants. This step fails if a synthetic 3x-slower
53
+ # cell would have passed.
54
+ run: python3 scripts/perf_gate.py --self-test
55
+
56
+ - name: Read the declared gate configuration
57
+ id: cfg
58
+ run: |
59
+ python3 scripts/perf_gate.py --print-config | tee gate-config.json
60
+ echo "repo_url=$(python3 -c "import json;print(json.load(open('gate-config.json'))['repository_url'])")" >> "$GITHUB_OUTPUT"
61
+ echo "repo=$(python3 -c "import json;print(json.load(open('gate-config.json'))['repository'])")" >> "$GITHUB_OUTPUT"
62
+ echo "mode=$(python3 -c "import json;print(json.load(open('gate-config.json'))['mode'])")" >> "$GITHUB_OUTPUT"
63
+ echo "runs=$(python3 -c "import json;print(json.load(open('gate-config.json'))['min_runs'])")" >> "$GITHUB_OUTPUT"
64
+ echo "commands=$(python3 -c "import json;print(' '.join(repr(c) for c in json.load(open('gate-config.json'))['commands']))")" >> "$GITHUB_OUTPUT"
65
+
66
+ - name: Clone the gate repository
67
+ run: |
68
+ mkdir -p fleet
69
+ git clone --depth 1 "${{ steps.cfg.outputs.repo_url }}" "fleet/${{ steps.cfg.outputs.repo }}"
70
+
71
+ - name: Measure
72
+ env:
73
+ PERF_FLEET_ROOT: fleet
74
+ run: |
75
+ python3 scripts/perf_harness.py \
76
+ --repos "${{ steps.cfg.outputs.repo }}" \
77
+ --commands ${{ steps.cfg.outputs.commands }} \
78
+ --modes "${{ steps.cfg.outputs.mode }}" \
79
+ --runs "${{ steps.cfg.outputs.runs }}" \
80
+ --fleet-root fleet \
81
+ --out perf-run
82
+
83
+ - name: Gate
84
+ run: |
85
+ python3 scripts/perf_gate.py \
86
+ --baseline "$BASELINE_DIR" \
87
+ --current perf-run \
88
+ --report perf-run/gate.json \
89
+ --table perf-run/RELEASE-TABLE.md
90
+
91
+ - name: Publish the measured table
92
+ if: always()
93
+ run: |
94
+ if [ -f perf-run/RELEASE-TABLE.md ]; then
95
+ {
96
+ echo "## Measured wall time per command"
97
+ echo
98
+ cat perf-run/RELEASE-TABLE.md
99
+ } >> "$GITHUB_STEP_SUMMARY"
100
+ fi
101
+
102
+ - name: Upload artifacts
103
+ if: always()
104
+ uses: actions/upload-artifact@v4
105
+ with:
106
+ name: perf-gate-${{ github.run_number }}
107
+ path: |
108
+ perf-run/**
109
+ gate-config.json
@@ -2,6 +2,133 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [4.14.0] — 2026-08-09
6
+
7
+ **The Part 1 queue of Amendment N — the four rows field evaluation #17 opened,
8
+ one commit each.** The fourth measurement of the same repository at the same
9
+ commit scored **7,5/10**, the highest of the series, and verified six of the
10
+ previous release's fixes from the outside. Both rows it opened are in the fields
11
+ that describe ASK's *own* reliability, which is where this product can least
12
+ afford to be wrong.
13
+
14
+ **Fixed in this release:**
15
+
16
+ - **A truncated answer no longer raises its own confidence (C2-31).** Three
17
+ complete runs published 395 findings / 98 defects at `confidence_level:
18
+ medium`; the run the budget cut published 289 / 15 at **`high`**, because the
19
+ level is derived from the confidence of the high/critical findings *that
20
+ exist* and the family contributing the medium-confidence ones is the family
21
+ that did not run. `SpringAuditResult.partial` is now one flag — set by the
22
+ scopes, by the CLI for a phase that never started, and carried through
23
+ `merge` — and a partial answer is capped at `low` with `confidence_basis`
24
+ saying the cut is the reason. `summary` gains `partial`, `counts_are_floor`
25
+ and `counts_basis`, so the mark lives where the numbers a consumer reads are,
26
+ not only in `_partial` at the root. The cap is a ceiling and never a floor: a
27
+ complete run keeps its level and gains no fields.
28
+ - **The rule inventory names rules, and both scopes' (C1-42).** `rules_run`
29
+ listed four SEC ids in a payload whose findings carried DEAD-001, SEC-008,
30
+ TX-006, TX-001, SEC-005 and SEC-006, and `rules_not_run` said `[GATE-001]`
31
+ where the arithmetic proves GATE-004 did not run either. The lists were keyed
32
+ on the pattern *object's* identity, so a family emitting six rules stood for
33
+ itself, and `merge` overwrote the TX scope's inventory with the security
34
+ scope's. `rule_pass.rule_ids_of()` is the one authority, the two lists are
35
+ unioned across scopes, and `rules_run_count` / `rules_total` sit beside them.
36
+ Three invariants are asserted over a real audit: every rule that fired is in
37
+ `rules_run`, the lists never overlap, and a complete `--scope all` run
38
+ inventories exactly `rule_catalog.ids()`.
39
+ - **The heartbeat is emitted by the work, not only by a thread (C3-80).** Four
40
+ versions with the same shape: one line at `elapsed=20.1s`, 39 minutes of
41
+ nothing, then the same stage text seconds before the end. A line-mode
42
+ heartbeat printed only by a daemon thread depends on that thread being
43
+ scheduled during exactly the CPU-bound stretch the operator is asking about.
44
+ `Progress._emit()` is now one seam with one throttle and two callers — the
45
+ loop on its timer and the analysis in-band — sharing one `_last_emit` stamp,
46
+ so the line rate is unchanged and a running analysis is always heard from.
47
+ - **The budget cuts inside a rule family (C3-79).** Budget 420 s, elapsed
48
+ 2 376 s: `exhausted()` was asked on a whole-family boundary and one family
49
+ lasts 39 minutes. `RulePassProgress.tick()` reports counted work and answers
50
+ *"is the time spent"* in one call, sampling the clock once per 64 units; five
51
+ families that walk a population take a `FamilyBudget` and stop on a batch
52
+ boundary. A family cut in flight is in neither list — it is
53
+ `rules_partially_run` with the count it reached, and the findings it produced
54
+ are kept, because they were measured. Measured on BroadleafCommerce: a 4,5 s
55
+ budget stops at 1 408 of 2 766 Java files and ends at 5,1 s.
56
+
57
+ **Verified in the field, not changed here:** `verify` exiting 2 without contracts
58
+ is C3-75's deliberate half (the waiver `--allow-unverified` and the named
59
+ `unverified_reason` are the fix; a distinct code is a contract change, F-AV), and
60
+ the 4.10.7 performance that never came back is C3-76, which does not reproduce on
61
+ this platform and waits on the Windows runner (F-AW).
62
+
63
+ Suite 7 787.
64
+
65
+ ## [4.13.0] — 2026-08-08
66
+
67
+ **The capability queue of Amendment M — the four features field evaluation #16
68
+ asked for, one commit each, after Part 1 closed in 4.12.0.** The evaluation's
69
+ verdict on engineering was *"no es que haya regresiones — es que no hay nada que
70
+ las detecte"*, and its "if you could only do two things" was F-AP and F-AQ, in
71
+ that order. Both ship here, with F-AR and F-AS beside them.
72
+
73
+ **New in this release:**
74
+
75
+ - **A release performance gate (F-AP).** `perf.gate_report()` turns a whole run
76
+ into one verdict against a whole baseline, and three of its four outcomes are
77
+ not a pass: a cell over the threshold **fails**, a baselined cell this run did
78
+ not measure **fails** (silence is how "nothing detects a regression" happens),
79
+ and a cell under the ADR-0006 sample floor **fails**. `scripts/perf_gate.py
80
+ --self-test` proves the gate is armed by failing a synthetic 3×-slower cell —
81
+ CI runs it *before* it runs the measurement, because a green gate is evidence
82
+ only when a red one is reachable on the same constants. Threshold **20 %**,
83
+ warm mode, one ~3 000-file repository, six repository-wide commands, and the
84
+ three exclusions published with every verdict. All of it lives in
85
+ `sourcecode.perf`; `docs/perf/REGRESSION-GATE.md` embeds `--print-config`'s
86
+ output verbatim and a test asserts they are the same bytes.
87
+ - **The measured times are published, generated (F-AP, second rider).**
88
+ `perf.release_table()` builds the per-command / per-repository-scale table a
89
+ release note carries, from the run's own artifacts. An axis that was not
90
+ measured prints `unknown`, never `0`. And `cache model`'s figures now name the
91
+ build they were measured on (3.2.2) beside the build you are running, so a
92
+ table nine releases old cannot read as this build's measurement.
93
+ - **`--jobs N` / `-j` / `ASK_JOBS` (F-AQ)**, default `cpu_count() - 1`. The
94
+ fourth consecutive evaluation to ask for parallelism was the first to sample
95
+ it: `threads=1`, `CPU/wall = 0,92…0,95`, 54 minutes, 20 cores. Worker
96
+ processes now parse files into the *same* content-addressed cache the IR loop
97
+ is about to read, so the loop is untouched and **byte-identity is structural,
98
+ not inspected** — no worker output reaches the payload by any route. Measured
99
+ on 2 766 Java files, both sides parse-cold with their own cache root: 5,36 s →
100
+ 3,46 s (**1,55×**), CPU/wall 2,07, IR hash identical. Every failure mode — no
101
+ pool, a faulting worker, a frozen build, a disabled cache, a repository under
102
+ the 200-file floor — degrades to today's sequential parse and returns a receipt
103
+ naming the reason.
104
+ - **`ASK_PARSE_CACHE_MAX_MB` (F-AR)**, default **512 MB**, least-recently-used,
105
+ enforced on write and published in `cache status` beside the size it bounds.
106
+ C3-70 retires the generation that can never be read again — an exact rule, and
107
+ exact rules only cover what they can foresee. This bounds what they cannot:
108
+ many repositories, many branches, one machine, and an owner who never runs
109
+ `ask cache clear`. Reads refresh recency (hourly granularity, so a hit is not a
110
+ disk write), an entry and its body-fact blob are evicted together, and
111
+ `ASK_PARSE_CACHE_MAX_MB=0` turns the guard off deliberately and says so.
112
+ - **`--no-write` / `ASK_READONLY=1` (F-AS)** — nothing is created inside the
113
+ analysed repository, and a command that cannot answer without writing says so
114
+ instead of writing, in the same error envelope every other refusal uses.
115
+ Accepted by **every** command (consumed before the parser, the `--detach`
116
+ precedent), because a mode five commands accept and seventy-nine reject is a
117
+ mode whose one refusing run is the run that writes. Each refusal is announced
118
+ once on stderr with its path. Shared caches outside the repository are
119
+ untouched — an auditor is protecting a checkout, not a home directory.
120
+ - **`ask --help` names every command that writes into the repository**, with the
121
+ condition beside it ("only with `--snapshot`"), generated from one authority.
122
+
123
+ **Measured, not fixed, and said so:** the second half of F-AQ — *"one audit per
124
+ run, shared by the compositions"* — was measured before being touched.
125
+ Instrumenting `build_risk` on a real repository counts `run_tx_audit` **1×** and
126
+ `run_security_audit` **1×**: `risk` already hands its findings to every impact
127
+ chain it composes (C3-42-b), so the duplication the field named is between two
128
+ *processes*, not inside one. That needs a persisted audit keyed on the worktree
129
+ signature, and it is not in this release. What ships is the regression test that
130
+ keeps the in-run property true.
131
+
5
132
  ## [4.12.0] — 2026-08-08
6
133
 
7
134
  **Field evaluation #16 — the third round on the same repository and commit, and the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 4.12.0
3
+ Version: 4.14.0
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.12.0-blue)
45
+ ![Version](https://img.shields.io/badge/version-4.14.0-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`**
@@ -108,7 +108,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
108
108
  # pip / pipx
109
109
  pipx install sourcecode # or: pip install sourcecode
110
110
 
111
- ask version # ask 4.12.0 — and, on a build that has aged,
111
+ ask version # ask 4.14.0 — and, on a build that has aged,
112
112
  # how many releases have probably shipped since
113
113
  ```
114
114
 
@@ -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.12.0-blue)
7
+ ![Version](https://img.shields.io/badge/version-4.14.0-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`**
@@ -70,7 +70,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
70
70
  # pip / pipx
71
71
  pipx install sourcecode # or: pip install sourcecode
72
72
 
73
- ask version # ask 4.12.0 — and, on a build that has aged,
73
+ ask version # ask 4.14.0 — and, on a build that has aged,
74
74
  # how many releases have probably shipped since
75
75
  ```
76
76
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "4.12.0"
7
+ version = "4.14.0"
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"
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env python3
2
+ """Release performance gate (F-AP) — compare a run against a baseline, one verdict.
3
+
4
+ `scripts/perf_harness.py` measures; this decides. It reads two directories of
5
+ `perf-baseline-v1` cells, gates every cell it can match, and exits non-zero when
6
+ any of them regressed beyond the declared threshold, when a baselined cell was not
7
+ measured at all, or when a cell's sample is under the ADR-0006 floor.
8
+
9
+ The threshold, the repository, the mode and the excluded commands are constants in
10
+ `sourcecode.perf` — this script never carries a number of its own, and
11
+ `docs/perf/REGRESSION-GATE.md` is generated from the same constants.
12
+
13
+ Usage:
14
+ # gate a fresh run against the last release's baseline
15
+ python3 scripts/perf_gate.py \
16
+ --baseline docs/perf/baselines/4.12.0 \
17
+ --current docs/perf/baselines/4.13.0 \
18
+ --report gate.json --table RELEASE-TABLE.md
19
+
20
+ # prove the gate is armed: a synthetic 3x-slower cell must fail it
21
+ python3 scripts/perf_gate.py --self-test
22
+
23
+ # print the declared configuration (what the doc renders from)
24
+ python3 scripts/perf_gate.py --print-config
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import argparse
30
+ import copy
31
+ import json
32
+ import sys
33
+ from pathlib import Path
34
+
35
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
36
+ from sourcecode import perf # noqa: E402
37
+
38
+
39
+ def load_cells(directory: Path) -> dict[str, dict]:
40
+ """Every `perf-baseline-v1` cell in *directory*, keyed by `<repo>__<cmd>__<mode>`.
41
+
42
+ Gate artifacts (`*.gate.json`) and anything that is not a cell are skipped
43
+ rather than mis-read: a directory that also holds a report must not turn the
44
+ report into a measurement.
45
+ """
46
+ cells: dict[str, dict] = {}
47
+ if not directory.is_dir():
48
+ return cells
49
+ for path in sorted(directory.glob("*.json")):
50
+ if path.name.endswith(".gate.json"):
51
+ continue
52
+ try:
53
+ cell = json.loads(path.read_text(encoding="utf-8"))
54
+ except (OSError, ValueError):
55
+ continue
56
+ if not isinstance(cell, dict) or cell.get("schema_version") != perf.SCHEMA_VERSION:
57
+ continue
58
+ cells[perf.cell_id(cell)] = cell
59
+ return cells
60
+
61
+
62
+ def _self_test() -> int:
63
+ """A deliberately slowed command must fail the gate. Run in CI before the gate.
64
+
65
+ This is the exit criterion stated as an executable check: it is not enough for
66
+ the gate to exist and be green — a green gate is only evidence when a red one
67
+ is reachable on the same machine, in the same job, with the same constants.
68
+ """
69
+ base = perf.build_baseline_cell(
70
+ tool_version="self-test",
71
+ command="spring-audit",
72
+ repo={"name": perf.RELEASE_GATE_REPO, "commit": "self-test", "files": 3000},
73
+ mode=perf.RELEASE_GATE_MODE,
74
+ wall_ms_runs=[1000.0] * perf.RELEASE_GATE_MIN_RUNS,
75
+ rss_bytes_runs=[1.0] * perf.RELEASE_GATE_MIN_RUNS,
76
+ )
77
+ slowed = copy.deepcopy(base)
78
+ slowed["wall_ms"] = {k: v * 3.0 for k, v in base["wall_ms"].items()}
79
+
80
+ cid = perf.cell_id(base)
81
+ red = perf.gate_report({cid: base}, {cid: slowed})
82
+ green = perf.gate_report({cid: base}, {cid: base})
83
+ dropped = perf.gate_report({cid: base}, {})
84
+
85
+ ok = (not red["passed"]) and green["passed"] and (not dropped["passed"])
86
+ print(f"self-test: 3x slower -> {'FAIL (correct)' if not red['passed'] else 'PASS (BROKEN)'}")
87
+ print(f"self-test: unchanged -> {'PASS (correct)' if green['passed'] else 'FAIL (BROKEN)'}")
88
+ print(f"self-test: cell gone -> {'FAIL (correct)' if not dropped['passed'] else 'PASS (BROKEN)'}")
89
+ if not ok:
90
+ print("perf gate self-test FAILED: the gate would not catch a regression.",
91
+ file=sys.stderr)
92
+ return 0 if ok else 1
93
+
94
+
95
+ def _print_config() -> int:
96
+ print(json.dumps({
97
+ "schema_version": perf.GATE_SCHEMA_VERSION,
98
+ "threshold": perf.RELEASE_GATE_THRESHOLD,
99
+ "repository": perf.RELEASE_GATE_REPO,
100
+ "repository_url": perf.RELEASE_GATE_REPO_URL,
101
+ "repository_scale": perf.RELEASE_GATE_REPO_SCALE,
102
+ "mode": perf.RELEASE_GATE_MODE,
103
+ "min_runs": perf.RELEASE_GATE_MIN_RUNS,
104
+ "commands": list(perf.RELEASE_GATE_COMMANDS),
105
+ "exclusions": [
106
+ {"command": c, "reason": r} for c, r in perf.RELEASE_GATE_EXCLUSIONS
107
+ ],
108
+ }, indent=2))
109
+ return 0
110
+
111
+
112
+ def main() -> int:
113
+ ap = argparse.ArgumentParser(
114
+ description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
115
+ )
116
+ ap.add_argument("--baseline", type=Path, help="Directory of the release being compared against.")
117
+ ap.add_argument("--current", type=Path, help="Directory of this run's cells.")
118
+ ap.add_argument("--threshold", type=float, default=perf.RELEASE_GATE_THRESHOLD,
119
+ help=f"Max allowed regression ratio (declared default: "
120
+ f"{perf.RELEASE_GATE_THRESHOLD}).")
121
+ ap.add_argument("--min-runs", type=int, default=perf.RELEASE_GATE_MIN_RUNS)
122
+ ap.add_argument("--allow-incomparable", action="store_true",
123
+ help="Do not fail cells whose env/host differ. Off by default: "
124
+ "an incomparable cell is an unmeasured one.")
125
+ ap.add_argument("--report", type=Path, help="Write the perf-gate-v1 report here.")
126
+ ap.add_argument("--table", type=Path, help="Write the release-notes markdown table here.")
127
+ ap.add_argument("--self-test", action="store_true",
128
+ help="Prove the gate is armed; measures nothing.")
129
+ ap.add_argument("--print-config", action="store_true",
130
+ help="Print the declared gate configuration as JSON.")
131
+ args = ap.parse_args()
132
+
133
+ if args.self_test:
134
+ return _self_test()
135
+ if args.print_config:
136
+ return _print_config()
137
+ if not args.current:
138
+ ap.error("--current is required (or use --self-test / --print-config)")
139
+
140
+ current = load_cells(args.current)
141
+ if not current:
142
+ print(f"no perf cells in {args.current} — nothing was measured, "
143
+ f"which the gate reads as a failure, not a pass.", file=sys.stderr)
144
+ return 1
145
+ baseline = load_cells(args.baseline) if args.baseline else {}
146
+
147
+ report = perf.gate_report(
148
+ baseline, current,
149
+ max_regression=args.threshold,
150
+ allow_incomparable=args.allow_incomparable,
151
+ require_min_runs=args.min_runs,
152
+ )
153
+ table = perf.release_table(list(current.values()), gate=report)
154
+
155
+ if args.report:
156
+ args.report.parent.mkdir(parents=True, exist_ok=True)
157
+ args.report.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
158
+ if args.table:
159
+ args.table.parent.mkdir(parents=True, exist_ok=True)
160
+ args.table.write_text(table, encoding="utf-8")
161
+
162
+ print(table)
163
+ if not baseline:
164
+ print("no baseline directory — cells recorded, nothing compared.", file=sys.stderr)
165
+ for cid in report["regressions"]:
166
+ print(f"REGRESSION: {cid}", file=sys.stderr)
167
+ for cid in report["missing_cells"]:
168
+ print(f"NOT MEASURED (baselined): {cid}", file=sys.stderr)
169
+ for cid in report["under_sampled_cells"]:
170
+ print(f"UNDER-SAMPLED: {cid}", file=sys.stderr)
171
+
172
+ return 0 if report["passed"] else 1
173
+
174
+
175
+ if __name__ == "__main__":
176
+ raise SystemExit(main())
@@ -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.12.0"
7
+ __version__ = "4.14.0"
@@ -37,6 +37,7 @@ from pathlib import Path
37
37
  from typing import TYPE_CHECKING, Any
38
38
 
39
39
  from sourcecode import __version__ as TOOL_VERSION
40
+ from sourcecode import readonly as _readonly
40
41
  from sourcecode.architectural_delta import ArchMetrics, diff_metrics, extract_metrics
41
42
  from sourcecode.perf import collect_env
42
43
 
@@ -412,6 +413,9 @@ def protect_ask_dir(out_dir: Path, *, announce: bool = True) -> None:
412
413
  marker = ask_dir / ".gitignore"
413
414
  if not marker.exists():
414
415
  created = not ask_dir.exists()
416
+ # F-AS: the guard raises an OSError, which the `except OSError` below
417
+ # turns into "did not write" — the mode's promise, no new branch.
418
+ _readonly.guard(ask_dir, what="this tool's own directory")
415
419
  ask_dir.mkdir(parents=True, exist_ok=True)
416
420
  marker.write_text(_ASK_DIR_GITIGNORE, encoding="utf-8")
417
421
  if announce and created:
@@ -442,6 +446,7 @@ def _announce_ask_dir(ask_dir: Path) -> None:
442
446
 
443
447
  def write_baseline(baseline: dict, out_dir: Path) -> Path:
444
448
  """Write `baseline` as deterministic JSON under `out_dir`; return the file path."""
449
+ _readonly.guard(out_dir, what="baseline history directory")
445
450
  out_dir.mkdir(parents=True, exist_ok=True)
446
451
  protect_ask_dir(out_dir)
447
452
  path = out_dir / _baseline_filename(baseline)
@@ -99,6 +99,15 @@ REFERENCE_REPOSITORY = (
99
99
  #: figures are relative to.
100
100
  REFERENCE_JAVA_FILES = 2000
101
101
 
102
+ #: The build the reference figures were measured on. F-AP's second rider: C3-60
103
+ #: conditioned these figures on the repository in hand; this is the other half —
104
+ #: **keeping them current**, or, when they are not current, saying so. A table
105
+ #: measured nine releases ago and printed without its provenance is a prediction
106
+ #: presented as a measurement, which is the one thing this product does not do.
107
+ #: The nightly gate (`docs/perf/REGRESSION-GATE.md`) is what re-measures them; this
108
+ #: constant is what makes the gap visible until it does.
109
+ REFERENCE_MEASURED_VERSION = "3.2.2"
110
+
102
111
  #: The other end of the measured range, and the reason this module publishes a
103
112
  #: *class* rather than a projected duration (C4-19). Field evaluation #16, on
104
113
  #: Windows 11 / PowerShell 5.1 / pipx / Python 3.10: `spring-audit` on a
@@ -164,7 +173,9 @@ LAYERS: tuple[Layer, ...] = (
164
173
  location="~/.sourcecode/parse-cache-v1/",
165
174
  invalidated_by=(
166
175
  "the file's own bytes and the extractor's source — content-addressed, "
167
- "so it is never stale and never needs invalidating"
176
+ "so it is never stale and never needs invalidating. Bounded by "
177
+ "`ASK_PARSE_CACHE_MAX_MB` (default 512 MB, least-recently-used, "
178
+ "enforced on write) and reported in `cache status` (F-AR)"
168
179
  ),
169
180
  warmed="filled for every Java file the warm parses",
170
181
  ),
@@ -358,6 +369,34 @@ class Conditioning:
358
369
  )
359
370
 
360
371
 
372
+ def reference_currency() -> dict:
373
+ """How old the reference figures are, in releases the reader can name (F-AP).
374
+
375
+ Returns the build the figures were measured on, the build running now, and
376
+ whether they are the same. The gap is not converted into a count of releases:
377
+ versions are not a distance and this module does not invent one. Naming both
378
+ builds is enough for a reader to see that a figure was measured on a different
379
+ one — which is the whole defect: *"no predice nada a 3 342 ficheros"* applies
380
+ to the size axis, and this is the same failure on the time axis.
381
+ """
382
+ from sourcecode import __version__
383
+
384
+ current = __version__
385
+ return {
386
+ "measured_on_version": REFERENCE_MEASURED_VERSION,
387
+ "running_version": current,
388
+ "current": REFERENCE_MEASURED_VERSION == current,
389
+ "statement": (
390
+ f"Measured on {REFERENCE_MEASURED_VERSION}; you are running {current}. "
391
+ f"The figures are that build's on that repository — they are not a "
392
+ f"prediction for this one, and the nightly performance gate "
393
+ f"(docs/perf/REGRESSION-GATE.md) is what re-measures them."
394
+ if REFERENCE_MEASURED_VERSION != current
395
+ else f"Measured on {current}, the build you are running."
396
+ ),
397
+ }
398
+
399
+
361
400
  def as_dict(here: "Optional[Conditioning]" = None) -> dict:
362
401
  """The whole model as data — what `ask cache model --json` emits."""
363
402
  conditioned: dict = {}
@@ -386,6 +425,9 @@ def as_dict(here: "Optional[Conditioning]" = None) -> dict:
386
425
  ],
387
426
  "reference_repository": REFERENCE_REPOSITORY,
388
427
  "reference_java_files": REFERENCE_JAVA_FILES,
428
+ # F-AP rider: the figures carry the build they were measured on, so a
429
+ # table nine releases old cannot read as this build's measurement.
430
+ "reference_currency": reference_currency(),
389
431
  # Published beside the reference so no consumer reads the figures as a
390
432
  # law: the same command has been measured 46× slower on a repository 1.7×
391
433
  # the size, on a different platform (C4-19).
@@ -427,6 +469,15 @@ def render_markdown() -> str:
427
469
  out.append("")
428
470
  out.append(f"Measured on {REFERENCE_REPOSITORY}, each command in isolation.")
429
471
  out.append("")
472
+ # The build, not the gap: this block is a checked-in document, so naming the
473
+ # *running* version here would make the doc drift on every release. `cache
474
+ # model` names both, because there a reader has a running build to compare to.
475
+ out.append(
476
+ f"Measured on release {REFERENCE_MEASURED_VERSION}. Figures are that "
477
+ f"build's on that repository, not a prediction for yours; the nightly "
478
+ f"performance gate (`docs/perf/REGRESSION-GATE.md`) is what re-measures them."
479
+ )
480
+ out.append("")
430
481
  out.append("| Command | A warm gives it | Measured (nothing cached → after a warm) | Repeat run cached | Layers | Notes |")
431
482
  out.append("|---|---|---|---|---|---|")
432
483
  for cmd in COMMANDS:
@@ -504,6 +555,7 @@ def render_text(here: "Optional[Conditioning]" = None) -> str:
504
555
  lines.append("")
505
556
  lines.append("What a warm gives each command")
506
557
  lines.append(f" Timings: {REFERENCE_REPOSITORY}, each command measured in isolation.")
558
+ lines.append(f" {reference_currency()['statement']}")
507
559
  width = max(len(c.command) for c in COMMANDS)
508
560
  for cmd in COMMANDS:
509
561
  repeat = "repeat cached" if cmd.repeat else "recomputes"