sourcecode 5.4.0__tar.gz → 5.4.2__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 (356) hide show
  1. {sourcecode-5.4.0 → sourcecode-5.4.2}/CHANGELOG.md +44 -0
  2. {sourcecode-5.4.0 → sourcecode-5.4.2}/PKG-INFO +3 -3
  3. {sourcecode-5.4.0 → sourcecode-5.4.2}/README.md +2 -2
  4. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/DEFECT-LEDGER.md +8 -0
  5. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/DEVELOPMENT-ROADMAP.md +97 -0
  6. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/USER_GUIDE.md +1 -1
  7. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/semantic-ir-roadmap.md +40 -0
  8. {sourcecode-5.4.0 → sourcecode-5.4.2}/pyproject.toml +1 -1
  9. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/__init__.py +1 -1
  10. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/cli.py +6 -0
  11. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/consumer_join.py +22 -3
  12. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/repository_ir.py +42 -4
  13. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/risk.py +99 -9
  14. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/sql_taint.py +99 -18
  15. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/test_sources.py +6 -4
  16. {sourcecode-5.4.0 → sourcecode-5.4.2}/.github/workflows/build-windows.yml +0 -0
  17. {sourcecode-5.4.0 → sourcecode-5.4.2}/.github/workflows/perf-gate.yml +0 -0
  18. {sourcecode-5.4.0 → sourcecode-5.4.2}/.gitignore +0 -0
  19. {sourcecode-5.4.0 → sourcecode-5.4.2}/.ruff.toml +0 -0
  20. {sourcecode-5.4.0 → sourcecode-5.4.2}/CLAUDE.md +0 -0
  21. {sourcecode-5.4.0 → sourcecode-5.4.2}/CONTRIBUTING.md +0 -0
  22. {sourcecode-5.4.0 → sourcecode-5.4.2}/LICENSE +0 -0
  23. {sourcecode-5.4.0 → sourcecode-5.4.2}/SECURITY.md +0 -0
  24. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/CACHE.md +0 -0
  25. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/COMMERCIAL-OFFER.md +0 -0
  26. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/CUSTOMER-VALIDATION.md +0 -0
  27. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/DEMO-5MIN.md +0 -0
  28. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/EXECUTION-PLAN-12MO.md +0 -0
  29. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/EXECUTIVE-ROADMAP.md +0 -0
  30. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/MANUAL-USUARIO.md +0 -0
  31. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/MODERNIZATION.md +0 -0
  32. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/PRODUCT_AUDIT.md +0 -0
  33. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/PRODUCT_IDENTITY.md +0 -0
  34. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/PRODUCT_TIERS.md +0 -0
  35. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/RC1-CHECKLIST.md +0 -0
  36. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/RELEASE-CHECKLIST.md +0 -0
  37. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/CATALOG-retrieval-intents.md +0 -0
  38. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/DESIGN-knowledge-provider-integration.md +0 -0
  39. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/DESIGN-p5.3b-statement-surface.md +0 -0
  40. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/DESIGN-p5.5-span-index.md +0 -0
  41. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/DESIGN-p6-semantic-impact-engine.md +0 -0
  42. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/DESIGN-p6-semantic-integration-engine.md +0 -0
  43. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/DESIGN-p7-semantic-services.md +0 -0
  44. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/DESIGN-robustness-security-inference.md +0 -0
  45. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/DESIGN-semantic-retrieval.md +0 -0
  46. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/EPV-p6-class-typeref.md +0 -0
  47. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/REPORT-p6-flow-analyzer-comparison.md +0 -0
  48. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/REPORT-p6-integration-detector-comparison.md +0 -0
  49. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/REPORT-p8-flow-analyzer-migration.md +0 -0
  50. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/REPORT-p9-integration-detector-migration.md +0 -0
  51. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/adr/0001-semantic-ir.md +0 -0
  52. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/adr/0002-source-evidence-boundary.md +0 -0
  53. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/adr/0003-architectural-characterization.md +0 -0
  54. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/adr/0004-semantic-retrieval.md +0 -0
  55. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/adr/0005-evidence-reconciliation.md +0 -0
  56. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/adr/0006-performance-harness.md +0 -0
  57. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/adr/0007-composition-semantics.md +0 -0
  58. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/adr/0008-answer-coherence.md +0 -0
  59. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/consumer-disposition-inventory.md +0 -0
  60. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/overview.md +0 -0
  61. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/product-architecture-review.md +0 -0
  62. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/product-strategy-layering.md +0 -0
  63. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/architecture/semantic-ir-consumer-matrix.md +0 -0
  64. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/baseline-ci.yml +0 -0
  65. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/commercial-pipeline-audit.md +0 -0
  66. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/contracts-ci.yml +0 -0
  67. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/contracts.md +0 -0
  68. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/eval/2026-06-15-petclinic-rest-issue11-vets-pagination.md +0 -0
  69. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/eval/2026-06-15-petclinic-rest-issue147-validation.md +0 -0
  70. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/eval/2026-06-15-petclinic-rest-workflow.md +0 -0
  71. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/eval/2026-06-15-phase18-openapi-surface.md +0 -0
  72. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/eval/2026-06-15-phase20-validation-surface.md +0 -0
  73. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/eval/2026-06-16-broadleaf-checkout-impact-fieldtest.md +0 -0
  74. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/eval/2026-06-16-petclinic-rest-issue11-weakness2-closed.md +0 -0
  75. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/eval/2026-06-16-spring-petclinic-issue2333-dead-vets.md +0 -0
  76. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/licensing-schema.md +0 -0
  77. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/migrate-check.md +0 -0
  78. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/README.md +0 -0
  79. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/REGRESSION-GATE.md +0 -0
  80. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/SUMMARY.md +0 -0
  81. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/alfresco__ask__cold.json +0 -0
  82. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/alfresco__ask__warm.json +0 -0
  83. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/broadleaf__ask__cold.json +0 -0
  84. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/broadleaf__ask__warm.json +0 -0
  85. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/eureka__ask__cold.json +0 -0
  86. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/eureka__ask__warm.json +0 -0
  87. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/jenkins__ask__cold.json +0 -0
  88. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/jenkins__ask__warm.json +0 -0
  89. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/jobrunr__ask__cold.json +0 -0
  90. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/jobrunr__ask__warm.json +0 -0
  91. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/keycloak__ask__cold.json +0 -0
  92. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/keycloak__ask__warm.json +0 -0
  93. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/neo4j__ask__cold.json +0 -0
  94. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/neo4j__ask__warm.json +0 -0
  95. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/ofbiz__ask__cold.json +0 -0
  96. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/ofbiz__ask__warm.json +0 -0
  97. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/openmrs__ask__cold.json +0 -0
  98. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/openmrs__ask__warm.json +0 -0
  99. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/petclinic__ask__cold.json +0 -0
  100. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.15/petclinic__ask__warm.json +0 -0
  101. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/SUMMARY.md +0 -0
  102. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/alfresco__ask__cold.json +0 -0
  103. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/alfresco__ask__warm.json +0 -0
  104. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/broadleaf__ask__cold.json +0 -0
  105. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/broadleaf__ask__warm.json +0 -0
  106. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/eureka__ask__cold.json +0 -0
  107. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/eureka__ask__warm.json +0 -0
  108. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/jenkins__ask__cold.json +0 -0
  109. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/jenkins__ask__warm.json +0 -0
  110. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/jobrunr__ask__cold.json +0 -0
  111. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/jobrunr__ask__warm.json +0 -0
  112. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/keycloak__ask__cold.json +0 -0
  113. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/keycloak__ask__warm.json +0 -0
  114. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/neo4j__ask__cold.json +0 -0
  115. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/neo4j__ask__warm.json +0 -0
  116. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/ofbiz__ask__cold.json +0 -0
  117. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/ofbiz__ask__warm.json +0 -0
  118. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/openmrs__ask__cold.json +0 -0
  119. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/openmrs__ask__warm.json +0 -0
  120. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/petclinic__ask__cold.json +0 -0
  121. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/2.5.16/petclinic__ask__warm.json +0 -0
  122. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/gate-latest/README.md +0 -0
  123. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/gate-latest/broadleaf__ask__warm.json +0 -0
  124. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/gate-latest/broadleaf__endpoints__warm.json +0 -0
  125. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/gate-latest/broadleaf__migrate-check__warm.json +0 -0
  126. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/gate-latest/broadleaf__posture__warm.json +0 -0
  127. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/gate-latest/broadleaf__spring-audit__warm.json +0 -0
  128. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/gate-latest/broadleaf__validation__warm.json +0 -0
  129. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/perf/baselines/reference-best/README.md +0 -0
  130. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/posture.md +0 -0
  131. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/privacy.md +0 -0
  132. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/pro-experience-audit.md +0 -0
  133. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/schema.md +0 -0
  134. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/spec/envelope-v1.md +0 -0
  135. {sourcecode-5.4.0 → sourcecode-5.4.2}/docs/spring-audit-ci.yml +0 -0
  136. {sourcecode-5.4.0 → sourcecode-5.4.2}/raw +0 -0
  137. {sourcecode-5.4.0 → sourcecode-5.4.2}/scripts/compare_integration_engines.py +0 -0
  138. {sourcecode-5.4.0 → sourcecode-5.4.2}/scripts/customer_smoke_test.sh +0 -0
  139. {sourcecode-5.4.0 → sourcecode-5.4.2}/scripts/generate_jdk_exports.py +0 -0
  140. {sourcecode-5.4.0 → sourcecode-5.4.2}/scripts/perf_gate.py +0 -0
  141. {sourcecode-5.4.0 → sourcecode-5.4.2}/scripts/perf_harness.py +0 -0
  142. {sourcecode-5.4.0 → sourcecode-5.4.2}/scripts/sync_gate_anchors.py +0 -0
  143. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/adaptive_scanner.py +0 -0
  144. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/archetype.py +0 -0
  145. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/architectural_baseline.py +0 -0
  146. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/architectural_delta.py +0 -0
  147. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/architecture_analyzer.py +0 -0
  148. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/architecture_summary.py +0 -0
  149. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/ast_extractor.py +0 -0
  150. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/audit_report.py +0 -0
  151. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/baseline_autocapture.py +0 -0
  152. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/bundled_docs.py +0 -0
  153. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/cache.py +0 -0
  154. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/cache_model.py +0 -0
  155. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/call_surface.py +0 -0
  156. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/caller_metrics.py +0 -0
  157. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/caller_reach.py +0 -0
  158. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/canonical_ir.py +0 -0
  159. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/chain_rules.py +0 -0
  160. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/change_plan.py +0 -0
  161. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/cir_graphs.py +0 -0
  162. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/classifier.py +0 -0
  163. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/client_calls.py +0 -0
  164. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/code_notes_analyzer.py +0 -0
  165. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/compare.py +0 -0
  166. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/confidence_analyzer.py +0 -0
  167. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/constraint_diff.py +0 -0
  168. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/container_wiring.py +0 -0
  169. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/context_cache.py +0 -0
  170. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/context_graph.py +0 -0
  171. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/context_scorer.py +0 -0
  172. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/context_summarizer.py +0 -0
  173. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/contract_diff.py +0 -0
  174. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/contract_init.py +0 -0
  175. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/contract_model.py +0 -0
  176. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/contract_pipeline.py +0 -0
  177. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/coverage_parser.py +0 -0
  178. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/data_exposure.py +0 -0
  179. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/data_labels.py +0 -0
  180. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/declarations.py +0 -0
  181. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/defect_identity.py +0 -0
  182. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/degradation.py +0 -0
  183. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/dependency_analyzer.py +0 -0
  184. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/deployment_prefix.py +0 -0
  185. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detach.py +0 -0
  186. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/__init__.py +0 -0
  187. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/base.py +0 -0
  188. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/csproj_parser.py +0 -0
  189. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/dart.py +0 -0
  190. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/dotnet.py +0 -0
  191. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/elixir.py +0 -0
  192. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/go.py +0 -0
  193. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/heuristic.py +0 -0
  194. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/hybrid.py +0 -0
  195. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/java.py +0 -0
  196. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/jvm_ext.py +0 -0
  197. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/nodejs.py +0 -0
  198. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/parsers.py +0 -0
  199. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/php.py +0 -0
  200. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/project.py +0 -0
  201. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/python.py +0 -0
  202. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/ruby.py +0 -0
  203. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/rust.py +0 -0
  204. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/systems.py +0 -0
  205. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/terraform.py +0 -0
  206. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/detectors/tooling.py +0 -0
  207. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/doc_analyzer.py +0 -0
  208. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/dynamic_argument_surface.py +0 -0
  209. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/endpoint_literals.py +0 -0
  210. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/endpoint_metrics.py +0 -0
  211. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/entrypoint_classifier.py +0 -0
  212. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/env_analyzer.py +0 -0
  213. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/envelope.py +0 -0
  214. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/environment_resolution.py +0 -0
  215. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/error_schema.py +0 -0
  216. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/evidence_provider.py +0 -0
  217. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/execution_plan.py +0 -0
  218. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/explain.py +0 -0
  219. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/facts/__init__.py +0 -0
  220. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/facts/registry.json +0 -0
  221. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/file_chunker.py +0 -0
  222. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/file_classifier.py +0 -0
  223. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/filter_surface.py +0 -0
  224. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/format_contract.py +0 -0
  225. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/fqn_utils.py +0 -0
  226. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/gate_anchors.py +0 -0
  227. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/git_analyzer.py +0 -0
  228. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/git_checkout.py +0 -0
  229. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/graph_analyzer.py +0 -0
  230. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/graph_evidence.py +0 -0
  231. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/hibernate_strat.py +0 -0
  232. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/identity_fallback.py +0 -0
  233. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/integration_coordinates.py +0 -0
  234. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/jdk_exports.py +0 -0
  235. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/license.py +0 -0
  236. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/__init__.py +0 -0
  237. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/dependency_import.py +0 -0
  238. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  239. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  240. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  241. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  242. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  243. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/orchestrator.py +0 -0
  244. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/registry.py +0 -0
  245. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/runner.py +0 -0
  246. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp/server.py +0 -0
  247. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/mcp_nudge.py +0 -0
  248. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/metrics_analyzer.py +0 -0
  249. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/migrate_check.py +0 -0
  250. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/migration_blast.py +0 -0
  251. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/non_coverage.py +0 -0
  252. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/openapi_surface.py +0 -0
  253. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/openrewrite_recipe.py +0 -0
  254. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/output_budget.py +0 -0
  255. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/output_encoding.py +0 -0
  256. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/parallel.py +0 -0
  257. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/parse_cache.py +0 -0
  258. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/partial_contract.py +0 -0
  259. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/path_admission.py +0 -0
  260. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/path_filters.py +0 -0
  261. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/perf.py +0 -0
  262. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/phased_run.py +0 -0
  263. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/pipe_contract.py +0 -0
  264. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/posture.py +0 -0
  265. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/pr_comment_renderer.py +0 -0
  266. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/pr_impact.py +0 -0
  267. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/prepare_context.py +0 -0
  268. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/progress.py +0 -0
  269. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/provenance.py +0 -0
  270. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/ranking_engine.py +0 -0
  271. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/readiness_timeline.py +0 -0
  272. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/readonly.py +0 -0
  273. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/reconciliation.py +0 -0
  274. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/redactor.py +0 -0
  275. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/reference_facts.py +0 -0
  276. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/release_info.py +0 -0
  277. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/relevance_scorer.py +0 -0
  278. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/remedies.py +0 -0
  279. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/rename_refactor.py +0 -0
  280. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/repo_classifier.py +0 -0
  281. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/__init__.py +0 -0
  282. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/context.py +0 -0
  283. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/errors.py +0 -0
  284. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/executor.py +0 -0
  285. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/planner.py +0 -0
  286. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/query.py +0 -0
  287. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/request.py +0 -0
  288. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/resolution.py +0 -0
  289. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/result.py +0 -0
  290. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/retriever.py +0 -0
  291. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/runtime.py +0 -0
  292. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/steps.py +0 -0
  293. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  294. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/steps_graph.py +0 -0
  295. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/steps_impact.py +0 -0
  296. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/steps_intf.py +0 -0
  297. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/steps_struct.py +0 -0
  298. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  299. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/ris.py +0 -0
  300. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/rule_catalog.py +0 -0
  301. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/rule_pass.py +0 -0
  302. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/runs.py +0 -0
  303. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/runtime_classifier.py +0 -0
  304. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/sarif.py +0 -0
  305. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/scanner.py +0 -0
  306. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/schema.py +0 -0
  307. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  308. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/security_chain.py +0 -0
  309. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/security_config.py +0 -0
  310. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/security_config_scan.py +0 -0
  311. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/security_posture.py +0 -0
  312. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/semantic_analyzer.py +0 -0
  313. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/semantic_impact_engine.py +0 -0
  314. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/semantic_integration_engine.py +0 -0
  315. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/semantic_services.py +0 -0
  316. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/serializer.py +0 -0
  317. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/servlet_surface.py +0 -0
  318. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/source_text.py +0 -0
  319. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/spring_event_topology.py +0 -0
  320. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/spring_findings.py +0 -0
  321. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/spring_impact.py +0 -0
  322. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/spring_model.py +0 -0
  323. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/spring_profiles.py +0 -0
  324. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/spring_properties.py +0 -0
  325. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/spring_security_audit.py +0 -0
  326. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/spring_semantic.py +0 -0
  327. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/spring_tx_analyzer.py +0 -0
  328. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/summarizer.py +0 -0
  329. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/target_admission.py +0 -0
  330. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/telemetry/__init__.py +0 -0
  331. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/telemetry/config.py +0 -0
  332. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/telemetry/consent.py +0 -0
  333. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/telemetry/events.py +0 -0
  334. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/telemetry/filters.py +0 -0
  335. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/telemetry/transport.py +0 -0
  336. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/test_gap_ranking.py +0 -0
  337. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/text_input.py +0 -0
  338. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/timeline.py +0 -0
  339. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/token_estimate.py +0 -0
  340. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/tree_utils.py +0 -0
  341. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/type_usage_surface.py +0 -0
  342. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/validation_inference.py +0 -0
  343. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/validation_surface.py +0 -0
  344. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/verify_edit.py +0 -0
  345. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/verify_repo.py +0 -0
  346. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/verify_rules.py +0 -0
  347. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/version_check.py +0 -0
  348. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/waivers.py +0 -0
  349. {sourcecode-5.4.0 → sourcecode-5.4.2}/src/sourcecode/workspace.py +0 -0
  350. {sourcecode-5.4.0 → sourcecode-5.4.2}/supabase/functions/README.md +0 -0
  351. {sourcecode-5.4.0 → sourcecode-5.4.2}/supabase/functions/get-license/index.ts +0 -0
  352. {sourcecode-5.4.0 → sourcecode-5.4.2}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  353. {sourcecode-5.4.0 → sourcecode-5.4.2}/supabase/functions/telemetry/index.ts +0 -0
  354. {sourcecode-5.4.0 → sourcecode-5.4.2}/supabase/sql/license_event_ordering.sql +0 -0
  355. {sourcecode-5.4.0 → sourcecode-5.4.2}/supabase/sql/licensing_schema.sql +0 -0
  356. {sourcecode-5.4.0 → sourcecode-5.4.2}/supabase/sql/telemetry_events.sql +0 -0
@@ -2,6 +2,50 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.4.2] — 2026-08-12
6
+
7
+ **External audit fixes and roadmap alignment.**
8
+ This release preserves bounded SQL interpolation evidence in composed risk
9
+ payloads, declares the population of existing test counts, and removes routes
10
+ declared only by abstract controller classes. The external audit queue and
11
+ defect ledger are synchronized with the shipped behavior.
12
+
13
+ ### Fixed
14
+ - **Bounded SQL interpolations remain auditable in `risk` and `enrich`.** The
15
+ `sql_taint.bounded_interpolations` rows now survive composition serialization
16
+ beside `sinks_total`.
17
+ - **`existing_test_count` declares its population.** The payload now identifies
18
+ the all-path, multi-language `prepare-context` population and distinguishes it
19
+ from the Java IR test-gap population.
20
+ - **Abstract controller declarations no longer emit phantom routes.** Their
21
+ endpoints remain available for projection onto concrete subtypes, but the
22
+ abstract class itself is not treated as a registered handler bean.
23
+
24
+ ### Documentation
25
+ - Added the external 5.4.2 audit queue and recommended capability work to the
26
+ semantic IR roadmap.
27
+ - Synchronized the distributed defect ledger with the 5.4.1/5.4.2 build state.
28
+
29
+ ## [5.4.1] — 2026-08-12
30
+
31
+ **Two correctness fixes and a release bump.**
32
+ ASK now stops treating ambiguous `it` directory segments as test roots, so
33
+ business paths like `src/app/shared/akita/it/` no longer inflate test counts or
34
+ test-source facts. It also stops publishing static asset requests such as
35
+ `/assets/i18n/en.json` as `broken_integrations`; those are served files, not
36
+ server routes.
37
+
38
+ ### Fixed
39
+ - **Ambiguous `it` segments no longer promote arbitrary business paths to test roots.**
40
+ The `test_sources` authority now leaves the bare `it` segment out of the
41
+ repository-level test-directory set, while `src/it` remains a declared test
42
+ source root. That closes the false-positive path in Angular and similar apps
43
+ where `it` is a domain token, not an integration-test marker.
44
+ - **Static asset calls are no longer reported as broken integrations.**
45
+ `consumer_join` now filters unmatched client calls under `/assets/`,
46
+ `/static/` and `/public/`, plus calls ending in static file extensions, and
47
+ documents that exclusion in the payload meaning.
48
+
5
49
  ## [5.4.0] — 2026-08-12
6
50
 
7
51
  **Fourteen rows from one audit, and the worst defect this ledger has recorded.**
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 5.4.0
3
+ Version: 5.4.2
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-5.4.0-blue)
45
+ ![Version](https://img.shields.io/badge/version-5.4.2-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`**
@@ -126,7 +126,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
126
126
  # pip / pipx
127
127
  pipx install sourcecode # or: pip install sourcecode
128
128
 
129
- ask version # ask 5.4.0 — and, on a build that has aged,
129
+ ask version # ask 5.4.2 — and, on a build that has aged,
130
130
  # how many releases have probably shipped since
131
131
  ```
132
132
 
@@ -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-5.4.0-blue)
7
+ ![Version](https://img.shields.io/badge/version-5.4.2-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`**
@@ -88,7 +88,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
88
88
  # pip / pipx
89
89
  pipx install sourcecode # or: pip install sourcecode
90
90
 
91
- ask version # ask 5.4.0 — and, on a build that has aged,
91
+ ask version # ask 5.4.2 — and, on a build that has aged,
92
92
  # how many releases have probably shipped since
93
93
  ```
94
94
 
@@ -59,6 +59,7 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
59
59
  | C1-40 | **`cache model` promises `onboard` an answer-level cache hit and the second run recomputes.** `ask cache model .` publishes `onboard` → *"the answer (repeat cached)"* `[task, ris]`. Measured: first run 56 s (cold, expected), **second run on a warm cache 23,5 s** — not an answer hit, a partial recomputation. Corroborated by `cache context-stats .`: `Contexts: 1 · Hits: 1 · Misses: 1 · Hit ratio: 0.5 · Avg lookup 1029,1 ms (p95 1955,2 ms) · Avg build 18332,5 ms · Bytes stored 9 969 057`. A **full second to decide whether there is a hit**, over a single 10 MB entry, is the signature of deserialising the whole payload to read its key | 4.10.4 (eval #14) | **Medium** — `cache model` exists so a user can plan instead of guess; a row that overstates what a warm buys is the one defect that command cannot afford. The lookup half is the second field measurement of C3-47 | **closed (4.10.6), and the claim was the false half.** Reproduced at home on keycloak (5 486 sources): **with** a `cache warm`, `onboard` costs 0,7 s — the row's promise holds. **Without** one, a second identical run costs 9,4 s against 10,2 s cold: the warm is what stores the answer, and the command does not store its own. The row said `repeat cached`, the field ran it twice with no warm, and got 56 s → 23,5 s. `repeat` is now `false` for `onboard` and the measured figures are published at two scales, with and without a warm, so the reader can tell which run they are about to make. Half (b) shipped too: the validity keys (`ev`/`kl`/`sig`) live in an uncompressed sidecar beside the blob, so deciding *is this entry still valid?* is a small file read instead of decompressing and parsing a 10 MB envelope — the case that matters most, an entry invalidated by a changed signature, used to pay all of it and then discard the result. A genuine hit still pays for the payload it returns; that is the answer, not overhead. Entries written before the sidecar existed fall back to the old full read. Original remedy note: two halves. (a) Reconcile the claim: either `onboard` really caches its answer, or its row says `shared work`, not `the answer`. (b) Split key metadata (hash + validity) from the payload into a sidecar index so a hit is decided without deserialising the blob; target < 5 ms against the ~1 029 ms measured. Related F-AA |
60
60
  | C1-43 | **"Does this repository configure a request filter chain?" has two authorities, and the one that decides whether a `high` finding family runs is the older, weaker one.** `posture._security_config_types` reads the question off the IR's own edges — a type that `extends`/`implements` a chain SPI type, **or a member that `returns` one**, i.e. the `@Bean SecurityFilterChain` shape — and the docstring beside it records why: *"a repository that wires its chain with a `@Bean SecurityFilterChain` and no `@EnableWebSecurity` reported `security.active: 0` — a false zero on the axis this command exists for."* `repository_ir._filter_based`, which decides `security_model` and therefore whether **SEC-001 runs at all**, still tests only `@EnableWebSecurity` (`_FILTER_SECURITY_ANNOTATIONS`, deliberately one entry) and `extends WebSecurityConfigurerAdapter`. A Spring Security 6 repository that declares its chain as a bean is therefore classified `annotation_based`, and SEC-001 then asserts, per unannotated handler, *"In an annotation_based security model there is no centralized filter — any caller can reach this endpoint without authentication"* | 4.18.0 (eval #24) | **High** — a confident falsehood on the strongest axis, and the only one of the series: measured on a greenfield Boot 3 module, the single SEC-001 finding is `POST /api/v1/auth/login`, which the repository's `SecurityConfig` lists in `PUBLIC_ENDPOINTS` with `permitAll()` under a closing `anyRequest().authenticated()` — the chain the model says does not exist both admits that route *and* protects the other 24. The evaluator adjudicated it by reading the source: *"la herramienta lo marca `high` porque solo lee la anotación del método, no la cadena de filtros"* | **in-progress** — the premise half shipped on `fix/sec-001-chain-authority`. One authority, `security_chain`, holds the request-chain SPI vocabulary, the member rule (*a member returning the SPI type makes its declaring class the configuration*) and the name normalisation; `posture` and both `repository_ir` sites read it, so the mechanism can no longer differ. **Two questions are named apart there rather than merged, because only one of them may silence a finding:** `declares_request_chain` (is there a chain at all — the annotation, the pre-5.7 adapter, or a member producing a `SecurityFilterChain`) is SEC-001's premise and is deliberately narrow, while `chain_participant_types` (what takes part in configuring it, filters included) is `posture`'s question. Widening the first to the second would silence SEC-001 on repositories with no centralized authorization — the P1-A failure mode, and the one with no symptom. Measured end-to-end on a fixture of the reported shape: `security_model` `annotation_based` → `mixed`, and the SEC-001 finding on `POST /api/v1/auth/login` disappears; recall asserted in the same battery (no chain → still reported; `@EnableMethodSecurity` alone → still reported). Fleet A/B over 5 repositories × 3 commands: 15/15 payloads identical in content, which is the expected result and the point of running it — no fleet repository declares a chain in either shape, so nothing could move. **Open: the verdict half.** Where the chain resolves, the per-endpoint answer should come from the resolved chain rather than from the model classification (C1-8's `matched_path` machinery already exists), and where it does not resolve, SEC-001 abstains — never *"any caller can reach this endpoint"* over a chain the run did not read |
61
61
  | C1-44 | **`has_uncommitted_changes: false` sits in the same document as `git_context.uncommitted_files: 1`, and it is the field cache freshness is decided on.** `ask <repo> --compact --git-context` publishes both, one run, one payload: `git_context.uncommitted_files` counts the file, `_cache.has_uncommitted_changes` says there is none, and `git status --porcelain` on the same tree prints `?? .claude/settings.json`. The third authority in the build agrees with the first — `--changed-only` reports `changed_files_count: 1` for that file and its `--help` defines the set it counts as *"staged, unstaged, untracked"* — so two of three surfaces admit the file and the one a consumer trusts for staleness does not. Under that published definition the boolean is simply false | 5.3.1 (audit #28) | Medium | **closed 5.4.0** — one predicate (`baseline_autocapture.counts_as_dirty`), extracted from `worktree_dirty` and read by the count in both the fresh path and the cache-hit patch, so the boolean cannot drift from the function that defines it. What it excludes is published (`uncommitted_files_not_read`, `uncommitted_files_basis`) rather than subtracted in silence, and only when something was actually excluded. Original remedy note: one predicate over one definition of *the working set*, read by both fields; the boolean is derived from the count rather than measured a second time, and where the two cannot be derived from one another the document may not publish both |
62
+ | C1-46 | **`existing_test_count: 6` sits beside `test_files: 0` and `has_test_sources: false` in one `prepare-context generate-tests` payload, and the 6 are five production files in a business package.** C3-115 closed the half that was reported — `test_gaps` now declares its population — and left a second authority for *"how many tests are there"* answering a different number in the same document. Re-measured in the field: 0 Java tests, 1 `.spec.ts`, 3 karma/jest configs; no population is worth 6. Root cause is in `test_sources.declared_test_root`: `_TEST_DIR_NAMES` carries the bare segment `it`, the final loop accepts it **at any depth**, and the only disarm — `_under_main_root` — knows `src/main` alone, which is a Maven layout. An Angular app lives under `src/app`, so `saint-client/src/app/shared/akita/it/` — *Incapacidad Temporal*, a business entity whose `it.model.ts` declares `ENFERMEDAD COMÚN` and `ACCIDENTE DE TRABAJO` — is read as a test source root and contributes 5 files; the 6th is the one real `.spec.ts`, and the reconciliation is exact. **It generalizes off this repository:** `it` is also the ISO 639-1 code for Italian, so `web/src/assets/i18n/it/messages.json` and `app/locales/it/common.ts` both answer `is_test_path: true` on the shipped build. Sibling of C1-28, in the direction C1-28 was written to prevent | 5.4.0 (audit #29) | **Medium** — the path-classification half is closed in 5.4.1; `existing_test_count_unit` now declares its all-path, multi-language task population in 5.4.2, distinct from the Java IR population | **closed 5.4.2** |
62
63
  | C1-45 | **The SQL taint join keys a sink on a bare statement id, so a mapper method in one namespace is joined to a call site that resolves to another — and the result is published as the repository's #1 critical risk with a call trace that does not exist.** `risk`'s top row on a 3 342-file monolith is `severity_effective: 29.57` at `ProcesosGerenciaMapper.xml:74`, `factors.query_construction = http_input_reaches_sql_interpolation`, with evidence naming `AutocoberturasRestController#actualizar(id, obj)` *calls* `actualizar(…)` in `ProcesosGerenciaMapper`. The controller calls its own service's `actualizar`; the join matched on the method name alone. **The correct key already exists in the code and is not used** — `sql_taint` line 76 returns `f"{self.namespace}#{self.statement_id}" if self.namespace else self.statement_id`, and `namespace` is captured (141, 151) and then spent only on an explanatory sentence (313-314), while the published basis claims the sinks are *"keyed by namespace and statement id"*. **Accompanying defect, same row:** the interpolated expression is stored (72, 144) and emitted (86) and never inspected, so `bloqueada = ${dto.bloqueada ? "'S'" : "'N'"}` — an OGNL ternary whose two branches are string literals, a closed allowlist by construction and precisely the mitigation SEC-008's own `fix_hint` recommends — is ranked as a text-controllable sink | 5.3.1 (audit #28) | **High** — F-BF's whole argument is that this axis fires on evidence rather than on multiplication by 1,0; a name-collision join inverts the first row of the ranking, which is the product | **closed 5.4.0** — both sides keyed on `namespace#statement_id`: a call carries the type its receiver resolves to and is that statement's only when the receiver resolves to the mapper it lives in; an unresolved receiver is not the same answer as any type name, so the sink stays `undecidable` rather than `reaches`. `does_not_reach` carries the same qualification, because it is a confident no. `splices_free_text` reads the interpolation at last — anything it cannot prove closed is free text, and what it closes leaves the population into a published `bounded_interpolations` list. The regression fixture this row specified is in the suite. Original remedy note: key both sides on `namespace#statement_id` (the property is already written, the call sites read the wrong half), and give the interpolation a three-state read: an expression whose reachable values are all literals is `does_not_reach`, an unresolvable one stays `undecidable`, never `reaches` by default. Regression fixture: two mappers in different namespaces sharing a `statement_id`, one reached by HTTP input and one not — expected one `reaches`, one `undecidable`; today, two `reaches` |
63
64
 
64
65
 
@@ -227,6 +228,11 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
227
228
  | C3-117 | **`--env-map` is inert: the section it advertises as an opt-in is emitted whether the flag is passed or not.** `ask <repo> --compact -o a.json` and `ask <repo> --compact --env-map -o b.json` are **byte-identical** (sha256 equal), and `env_map` is populated with the same 6 variables in both. The `--help` describes an opt-in (*"Map environment variables referenced across the codebase"*) that does not exist | 5.3.1 (audit #28) | Low — the payload is not wrong, the flag's contract is | **closed 5.4.0** — the behaviour stays (a compact payload with a null section is a hole where a reader expects one) and the claim is corrected: `_IMPLIED_NOTE`, one sentence in one place, appended by every flag it is true of, asserted against the parameter list including the negative. Original remedy note: either bind the flag (and pay the C3-103 attention: two views of one state must not become two analyses) or retire it and say the section is unconditional. A flag that changes nothing is a claim the build does not keep |
228
229
  | C3-118 | **`prepare-context generate-tests` spends 56,45 s enforcing a 30 000 ms budget, and returns nothing.** No `--timeout`: wall clock 56,45 s, payload `analysis_budget.limit_ms: 30000`, `exhausted: true`, `relevant_files: 0` — an 88 % overshoot of the budget it was enforcing, to publish an empty answer. C3-1 closed this command's budget contract (published flag, `analysis_budget` block, budget in the cache key, an exhausted result never stored) on a measurement of **3,35 s over 3 300 files**; at 3 342 files with 6 947 sources the same default is 19× that and does not bind. This is C3-83's provenance question asked of the value rather than the source: the default is a constant, not a function of the scope | 5.3.1 (audit #28) | Medium — the default is unusable at the scale the product is sold for, and it costs double what it promises to return nothing | **closed 5.4.0** — the overshoot was the walk being *waited for*: `concurrent.futures` joins abandoned workers at interpreter shutdown, so the process paid the walk's full duration whatever the budget said. It runs on a daemon thread, which is not joined, and two subprocess assertions pin that property in both directions. The default is derived from the scope (`_tests_budget_ms`: 60 ms per Java file between the old 30 s figure as a floor and a 300 s ceiling) and publishes the count it derived it from. Original remedy note: the budget must bind (check it where the walk can be cut, not only between phases — C3-82's shape), and the default must be derived from the scope the command already measures rather than fixed at 30 s. When it is exhausted the payload already says so honestly; that half stays |
229
230
  | C3-119 | **A client call whose URL is an expression is promoted to `broken_integration` — the one verdict this command presents as unconditional.** `ask endpoints <repo> --consumer <repo>/saint-client` reports **20 calls resolved, 115 unresolved** with a single reason (*"the URL is an expression, not a literal at the call site"*), `routes consumed: 0 / 3 574`, `delete_candidates: 2 637` — and 16 of the unresolved arrive as `broken_integrations`. The consumer centralises its base URL in a service (``this.httpGet(`${this.END_POINT}/porAgrupacion/${id}/${fecha}`)``, `planificacion-especial.service.ts:109`) and only the literal tail is captured, so the join key is a fragment and the route is then declared unserved. F-BH's own framing is that this direction *"needs no assumption about coverage to be true"*; it is the one verdict presented as incondicional and the one that is wrong. **Second facet, same extractor:** the TypeScript scan does not strip comments — `"GET /to/your/validation/service"` at `seguro-grid.component.ts:204` is a commented-out DevExpress documentation placeholder admitted as a live call. That is the E-1/E-2 predicate, applied in Java and not in TypeScript | 5.3.1 (audit #28) | Medium-High — a team acting on `broken_integrations` chases 16 integrations that are not broken, and the `delete_candidates` list inherits the same error at 2 637 rows | **closed 5.4.0** — a call whose head was an interpolation travels as `path_resolution: prefix_stripped`; it still matches where it matches, and where it does not it leaves `broken_integrations` for `partial_client_calls` with the reason and a count, on both join surfaces. `broken_integrations`' own `meaning` now states the condition it holds under. The comment model is `blank_comments`, offsets and line count preserved so every call still points at a line a reader can open, and string/template literals are state. Original remedy note: an unresolvable URL degrades to `unresolved_client_call` and never to `broken_integration` (absence of evidence is not evidence — INV-F1-1); the comment model the Java side has runs on the TypeScript side too. The resolution half is **F-BO**, not this row |
231
+ | C3-120 | **The composition engine became unusable at 3 342 files, and one line of C1-45's fix is why.** Measured against 5.3.1 artefacts on the same subject: `risk` 38,37 s → **601,74 s** under a 300 s budget, returning `partial: true` with **0 of 98** defects composed; `risk` under a 3 000 s budget → 444 s complete (×11,6); `enrich` 36,30 s → **>600 s**, stopped without completing; `audit-report` not run at all after two hangs. Median ratio over 48 comparable pairs is **1,00x** — the regression is confined to this family. Root cause is `sql_taint._receiver_type`: `_RECEIVER_RE` is anchored `…\s*$` and is applied as `search(method.body[:body_index])`, which copies an O(body_index) prefix **and** makes the regex engine try every start position in it before concluding. `_called_names` pays that for **every call of every method in the repository**, unfiltered and unbounded, where 5.3.1 did `names.add(call.group(1))` at O(1); a service body of 1 650+ lines makes the pass quadratic. Auditor's microbenchmark on the shipped regex, results byte-identical to a windowed `search(body, start, endpos)`: 8,6 KB/200 calls **36,9×**, 43 KB/1 000 calls **186,9×**, 215 KB/5 000 calls **916,5×** — 5× the calls, 25× the time | 5.4.0 (audit #29) | **Critical** — three of the seven experimental commands, and the ones that carry the price premium, do not return on the repository this product is sold for | **closed 5.4.1** — the read is windowed (`search(body, start, body_index)`: the `$` still ends at the call, no prefix is copied and the engine is offered a bounded number of start positions), and the window start retreats over any identifier it lands inside so a receiver wider than 128 characters is resolved whole rather than by its suffix — `""` is *unresolved* here and must never be reached by truncation. Second half: `_called_names` takes the statement ids being decided as its scope, because the only question asked of that set is `(mapper, statement id) in called`, so a name no statement carries can change no verdict. Re-measured on the shipped regex: 5 000 calls over a 159 KB body, **18 079 ms → 28 ms**, results identical at every call site. Four regression assertions, including a wall-clock gate on the pass |
232
+ | C3-121 | **The budget does not bind in the composition family, and one invocation froze the process.** `ASK_MAX_ANALYSIS_SECONDS=300 ask risk .` ran **601,74 s** of wall clock and wrote `elapsed_seconds: 600.92` into its own `_partial`; `ASK_MAX_ANALYSIS_SECONDS=150 ask enrich . --sarif` passed 600 s and was stopped by hand; a third `risk` invocation stopped responding until the working session was killed. This is the failure mode C3-82/C3-118 closed for `generate-tests` — *the budget must be checked where the walk can be cut, not only between phases* — reappearing in another family, and the lesson did not travel. Second half, independent of the overrun: a **respected** budget yields no answer — at 300 s the payload is `phases_completed: ["audit"]`, `phases_pending: ["compose"]`, `composition_stopped_after.defects_composed: 0`, which in CI is an empty envelope after ten minutes. The degradation shape itself is exemplary and is not what this row is against: `_partial` names the completed and pending phases, `why_stopped`, the repo-wide walks it skipped and the axes left `unknown`. ⚠ Re-measure after C3-120 before investigating separately — same subsystem, same triage C3-114 got against E-3 | 5.4.0 (audit #29) | **High** — a budget that does not cut is worse than no budget: it sells CI a guarantee it does not keep, and a hard freeze consumes the runner | **closed 5.4.1** — the deadline is asked *inside* the two repository-wide walks that hold the wall time, not only at their doors: the method parse samples the clock every 16 files and the two propagation walks sample it on their own units. A walk cut while running raises rather than returning what it had — every verdict these walks publish below `reaches` is a statement about the **whole** repository, and a negative over a partial universe is a confident no built on an unfinished read — so the measurement is dropped, the axis reads `unknown`, and the walk is named in `_partial.composition_walks_not_run` exactly as a walk that never started is. Nothing partial is cached on the CIR, so a later caller cannot be served a repository with files missing. Two assertions, both non-vacuous: the walk cut inside, and the composer naming it. ⚠ The residue this row does not close: the walks that are still gated only at the door (`inferring the security posture`, `indexing the validation surface`, `resolving the conditional bean graph`) — none of them was measured holding the overrun, and giving each a sampling seam is its own increment |
233
+ | C3-122 | **`ask risk <repo> -o <path>` spent 444 s, exited 0, printed nothing and wrote no file.** One occurrence, from bash with a Windows path in forward slashes; a later recursive search found the file nowhere in the user tree. The same path shapes worked in the same session for `ask endpoints -o`, `ask config` and `ask baseline capture --dir`. Not reproduced — the auditor stopped invoking `risk` after the freeze in C3-121 — and recorded with that limitation. An analysis command that consumes 444 s and reports success without producing its output owes the reader a line on stderr at minimum | 5.4.0 (audit #29) | **Low** (unreproduced) — but it is a C5 shape: the exit code is the published fact and it is wrong | **open** |
234
+ | C3-123 | **A static asset reaches `broken_integrations`, the one verdict this command presents as unconditional.** C3-119 did the main work — 19 → 1, with 14 relocated to `partial_client_calls` and the `meaning` stating its condition. The survivor is `GET /assets/i18n/{}.json` from `transloco-root.module.ts:18`: a translation bundle served by the container, not a Spring route. True under the published definition and not a broken integration in any sense a reader means. Remedy: exclude paths under known asset roots (`/assets/`, `/static/`, `/public/`) or ending in a static file extension, and say so in the `meaning` | 5.4.0 (audit #29) | **Low** | **closed 5.4.1** — static asset roots and file extensions are excluded and the meaning names the exclusion |
235
+ | C3-124 | **`risk` renamed three published keys inside a minor with no alias.** 5.3.1 emitted `total_defects`, `total_findings` and `risks_cap`; 5.4.0 emits `total_defects_floor`, `total_findings_floor` (plus `counts_basis`, `counts_are_floor`, `partial`, `_partial`) and dropped `risks_cap`. Permitted by the command's own experimental tier — *shape may change in a minor* — and the new names are better; the payload still carries no notice, so a consumer bound to the old keys reads `None` with nothing telling it why. `auth status` already publishes a `deprecated_fields` block: the channel exists and this command did not use it | 5.4.0 (audit #29) | **Informative** | **closed 5.4.1 as not a defect** — a complete 5.4.1 run retains the legacy keys; the renamed floor fields are emitted only for budget-cut payloads and are explicitly marked as floors |
230
236
 
231
237
  ## Class C4 — Discoverability (the ceiling as of 3.2.0)
232
238
 
@@ -278,6 +284,7 @@ robustness **4/10** — the lowest number any evaluation has given any axis.*
278
284
  | `impact.stats.endpoints_affected_count` | C1-16 | ~~`0` where `impact-chain` returns a populated list~~ → **closed 3.5.0**: one reach authority (`caller_reach`), 0 → 20 of 21 endpoints on `Money`, exact parity on two other repositories |
279
285
  | `ask <cmd> \| head` | C3-28 | ~~the answer is produced and the exit code says failure~~ → **closed 3.7.0**: a broken output stream changes nothing but where the bytes go; the command still exits on its own terms, so a failure keeps its code and its envelope |
280
286
  | `spring-audit` cut by its own budget | C2-31 | ~~`summary` reads `total_defects: 15`, `confidence_level: high` where the complete run measures 98 — the truncation is declared only in `_partial`, at the root~~ → **closed 4.14.0**: a partial answer is capped at `low` confidence and `summary` carries `partial` / `counts_are_floor` / `counts_basis`, so the mark is where the numbers are |
287
+ | `risk -o <path>` | C3-122 | 444 s, exit **0**, no stdout and no file written — one occurrence, unreproduced |
281
288
  | `plan <unknown target>` | C3-29 | ~~exit **0** over a payload of zeros while `impact` answers the same fact with exit 1~~ → **closed 3.7.0**: one authority, four commands, and ambiguity recognised as the same fact |
282
289
 
283
290
  **Two witnesses arrived after the invariant.** Both are exit codes, and I-8 governs payloads —
@@ -327,6 +334,7 @@ provenance: E-3 was found from outside, on the surface where silence is loudest.
327
334
  | E-1 | **Prose opened a declaration, and the file left the graph.** The declaration joiner decided whether a line *starts* a type declaration by scanning raw text. A comment carrying `class`/`interface`/`enum`/`record` followed by an identifier — `/** Implements the interface only for the legacy path. */`, or a trailing `// starter class is not an utility class` on an annotation — matched, so the join swallowed the annotation and the real declaration into one entry beginning with `@`, which the main loop reads as a pending annotation and never examines. The file then yields **zero symbols**: no type, no endpoint, no transaction boundary, nothing | 3.7.0 (battery, while building the C3-13 fixture) | Medium | **closed 3.7.0**. Comments are stripped before the keyword scan, block state carried across lines and string-literal aware; the join's terminating `{` must also be code, so a commented brace no longer cuts a real multi-line declaration in half. Measured A/B over six repositories (petclinic, eureka, open-banking-gateway, openmrs-core, Broadleaf, keycloak): **open-banking-gateway recovers 6 types — three of them Spring Boot application classes** — and **no repository loses a single symbol** |
328
335
  | E-2 | **The security configuration scan cannot tell code from a comment.** `security_config_scan._scan_java` and `_scan_descriptor` match on raw text with no comment model. Reproduced at unit scope: `SEC-004` is returned identically for a live statement, a `//` comment and a `/* */` block; `SEC-007` identically for a live and a commented-out XML block. **Measured cost in the field: 4 of 13 `high` findings were false — 31 %** — and the damage is worse than the count, because the correlation engine wrote a persuasive paragraph over a commented-out block (*"these deployments share one credential, so the least protected decides the strength of all"*). **Confident prose about dead code is worse than a terse warning: it invites trust.** The same blindness runs in reverse and costs a *finding*: a transport-guarantee constraint that is commented out is invisible, so a mitigation somebody switched off is never reported, and it aggravates a cookie finding reported beside it. E-1 fixed exactly this class for the declaration joiner in 3.7.0; the configuration scan was written afterwards without the lesson | 4.5.1-class (eval #10) | **High** — it is the precision axis (scored 5,5/10) and the reason the evaluator concludes the output *"no es publicable sin verificación humana"* | **closed 4.5.3** — one authority, `source_text`, wired at the **three points a file is read** rather than inside each rule, so a rule added later is covered by construction instead of by whoever remembers. Comments are **blanked, not deleted**: offsets and line count are preserved, so every observation still points at the line a reader can open (asserted). Two properties are asserted as hard as the fix, because getting either wrong trades one wrong answer for another — **string literals survive** (`hibernate_strat._strip_comments_strings` blanks literal *content* on purpose, and these rules carry their evidence *inside* the literal: `MessageDigestPasswordEncoder("SHA-1")`, `<param-value>admin:{SHA-256}…`), and a `//` or `/*` **inside** a string is not a comment (`"http://example.com"`). A/B over the battery: BroadleafCommerce, openmrs-core, keycloak and spring-petclinic report byte-identical security findings — the fix removes false positives and loses no true one. `source_text.commented_spans` is published unused, so the *reverse* half — a control that is present but switched off — has a seam to be reported from rather than a second parser (DEAD-001, queued as F-A) |
329
336
  | E-3 | **A handler that inherits its `@RequestMapping` loses the authorization annotation it declares, and 54,7 % of a monolith's HTTP surface is published as unguarded.** The inheritance projection in `_build_route_surface` indexes a projected route under the **declaring** member — the abstract method on the superclass that owns the mapping — and every security lookup downstream reads that symbol. The gate lives on the `@Override` in the concrete controller, which carries no mapping annotation and is therefore never consulted. Census on the subject (3 574 endpoints, parser validated 15/15 against hand-checked cases): **1 955 handlers inherit their mapping, carry a live gate in source, and are published `none_detected`**; 909 that declare their own mapping and carry the same gate are published `custom_gate_inferred`; 363 inherit and genuinely carry nothing; 115 declare and carry nothing; 111 carry a gate that is commented out (correctly `none_detected`, DEAD-001). The shape is the generic CRUD base class — one abstract class with the mappings and no security, 235 controllers extending it. Runtime semantics confirm the direction: the aspect's pointcut is `@Before("@annotation(…)")`, which matches the executing method — the override — so the annotation **is** applied and only the reading stops at the declaration. **Propagates to eight commands**: `endpoints`, `spring-audit`, `posture`, `data-exposure`, `risk`, `impact`, `enrich`, `audit-report`, plus `retrieve endpoint-security|security-surface`. It also retires this repository's own headline: *"2 635 endpoints with no security policy (73,7 %)"* is really **≈659** | 5.3.1 (audit #28) — **the first E row found in the field** | **Critical** — the largest single mis-classification this ledger has recorded, running in the unsafe direction, on the axis the product is bought for, in the answer the field quotes most | **closed 5.4.0** — the projection now names the member that runs. A projected route carries `effective_symbol` (the override where one exists, the declaration otherwise) beside `symbol`; the phase-3 security projection reads a gate the override declares for itself **method-level only**, so the fix can add a policy and never widen one away; and both `custom_gate_inferred` carry sites — the assembled route surface every CIR consumer reads, and the `endpoints` extractor — read the annotation set of **both** members of the pair. Second half, same root and found while fixing it: `_infer_gate_annotations` classified an inherited-mapping override as a non-handler, which puts the only members carrying the gate on the wrong side of the specificity ratio — diluting it at this subject's scale and **suppressing the gate outright** on a repository where most controllers extend a generic base. A member the route surface publishes as a route is a handler. 7 regression assertions over both surfaces and both vocabularies (a repository-declared gate and `@PreAuthorize`), **5 of 7 red on the previous build**; the two that are green are the negative controls — an ungated override stays ungated, because a fix in this direction must not invent a policy either. Suite 8 149 |
337
+ | E-4 | **E-3's residue: the annotation-to-method pairing is decided on raw text, so a blank line switches a security verdict off.** 24 endpoints in 14 controllers still publish `none_detected` with a live gate, out of the 1 955 E-3 corrected, and they partition perfectly into two lexical triggers. **(a) 18 endpoints** — a blank line between the annotation and the declaration (`@Override`, `@M3FiltroSeguridad(...)`, *blank*, `public List<MutuaDto> obtenerTodos()`): **0/18** detected, against 1 931/1 937 = 99,7 % where no blank line intervenes. `repository_ir` clears `pending_anns` in the fall-through at the end of the per-line loop, and the only blank-line guard sits **inside** the block-comment branch, so a genuinely empty line reaches the reset and empties the buffer. **(b) 6 endpoints** — a space between the type and its generic argument (`public List <CategoriaProfesionalDto> obtenerTodos()`): **0/6**. The `return_type` group is `[\w.<>\[\]?,]+`, which admits no space, so `List` matches, `\s+` eats the space, and `(?P<name>[a-z_]\w*)` is offered `<Categoria…>` and fails — **the whole declaration fails to match and the method is dropped**, which is the exact loss the regex's own comment three lines above warns about for modifier-position annotations. Java applies the annotation identically in both forms. In every one of the 14 controllers only `obtenerTodos` fails while its siblings (`crear`, `borrar`, `actualizar`, `obtenerPorId`) resolve. **E-3's 7 regression assertions cover neither form** | 5.4.0 (audit #29) | **Medium** — small population, unsafe direction, and it shows the pairing is still lexical rather than structural | **closed 5.4.1** — two edits, one per trigger. A blank line is skipped before the fall-through that clears the pending-annotation buffer: Java binds an annotation to the next declaration whatever whitespace separates them, and nothing about an empty line is a statement the reader must forget. The `return_type` group admits a space before the generic argument, so `List <Dto>` matches and the method exists again with its endpoint, its gate and its return-type edge. The spelling is normalised where it enters `stable_id` — `List <Dto>` and `List<Dto>` are one type and may not become two identities — and only the space *before* a bracket is removed, so `Map<String, List<X>>` keeps the id it already had. Four assertions added to E-3's battery, all four red on the previous build's source: both forms on the assembled route surface, both on the `endpoints` extractor, and the identity. ⚠ The class of defect is not closed by this row: the pairing is still lexical, which is what F-BP is queued for |
330
338
 
331
339
 
332
340
  ## Class P — Product and procurement (not analysis defects)
@@ -3,6 +3,103 @@
3
3
  *Chief-engineer development record. This is the internal, dev-facing counterpart to the
4
4
  customer-facing `docs/EXECUTIVE-ROADMAP.md`.*
5
5
 
6
+ > **PHASE UPDATE 2026-08-12 (audit #29 — the re-audit of 5.4.0, published the
7
+ > same day, `age_days: 0`).** Same subject (`saint-server @ 3dde037`, branch
8
+ > `banyan-v2`, 3 342 Java / 3 574 endpoints), same protocol (`ASK_READONLY=1` +
9
+ > `--no-write`), same instrumented harness and the same self-validated parser
10
+ > (15/15), compared directly against the 89 artefacts kept from the 5.3.1 round.
11
+ > ~45 commands re-run. **8,05/10** (5.3.1: 8,41) — and the composite hides the
12
+ > story, which has to be told whole: **what the product answers got much better
13
+ > and what delivers it got worse.** Correctness +1,3, performance −2,5,
14
+ > reliability −2,5.
15
+ >
16
+ > **Ten of the twelve rows verified closed independently, one partial, one closed
17
+ > by correcting the claim.** The security axis moved from unusable to reliable:
18
+ > detection on inherited-mapping handlers **0,0 % → 98,8 %** (0/1 955 →
19
+ > 1 931/1 955), affected controllers 232 → 14, and the 909/909 that already
20
+ > declared their own mapping stayed at 100 % — no collateral damage. Measured
21
+ > downstream: `impact.stats.security_surface_count` 894 → 1 888, and `endpoints
22
+ > --consumer` `delete_candidates` 2 637 → 701. The two that did not close clean
23
+ > are **DEF-04** (closed against a narrower restatement than the one reported)
24
+ > and **DEF-07** (`--env-map`, closed by correcting the `--help` text rather than
25
+ > the behaviour — accepted, recorded, not re-opened).
26
+ >
27
+ > ⚠ **The finding of this round is a regression, not a false answer: the
28
+ > composition engine no longer returns on this repository.** `risk` 38,37 s →
29
+ > 601,74 s under a 300 s budget with **0 of 98** defects composed; `enrich`
30
+ > 36,30 s → >600 s stopped by hand; `audit-report` not run after two hangs; one
31
+ > `risk` invocation froze the process until the working session was killed.
32
+ > Median ratio over 48 comparable pairs: **1,00x** — nothing else moved. The
33
+ > auditor localised it to one line of C1-45's own fix by microbenchmark, without
34
+ > invoking `risk` again: `sql_taint._receiver_type` searches an end-anchored
35
+ > pattern over a **copied prefix** of the method body, and `_called_names` pays
36
+ > it for every call of every method in the repository. **916× measured at
37
+ > 215 KB / 5 000 calls, results byte-identical to a windowed search.**
38
+ >
39
+ > **Seven rows opened.** C3-120 (critical, the quadratic receiver resolution) ·
40
+ > C3-121 (the budget does not bind in this family, plus one hard freeze) · E-4
41
+ > (E-3's residue: 24 endpoints whose gate is switched off by a blank line or by
42
+ > `List <T>`) · C1-46 (`existing_test_count: 6` against `test_files: 0`, and the
43
+ > six are a business package called `it`) · C3-122 (`risk -o` exit 0 with no
44
+ > file) · C3-123 (a static asset in `broken_integrations`) · C3-124 (`risk`
45
+ > renamed three published keys inside a minor with no alias).
46
+ >
47
+ > **Order of work: fixes only, one commit each, no features in this batch.**
48
+ > C3-120 → C3-121 (re-measure after C3-120 before investigating separately —
49
+ > same subsystem, the triage C3-114 got against E-3) → E-4 → C1-46 → C3-122 →
50
+ > C3-123 → C3-124. C3-120 first because it is one line with a verified
51
+ > equivalence and it probably closes C3-121 on its way.
52
+ >
53
+ > ⏸ **BATTERY PAUSED AFTER THREE ROWS (user directive, 2026-08-12).** Closed and
54
+ > committed, one commit each, suite green at every one (8 244 → 8 246 → 8 250):
55
+ > **C3-120** `5f28829` (windowed receiver read + the scope on `_called_names`:
56
+ > 18 079 ms → 28 ms over 5 000 calls, identical results) · **C3-121** `b2abec6`
57
+ > (the deadline sampled *inside* the two walks that hold the wall time; a walk
58
+ > cut inside answers `unknown` and is named, never a partial universe) · **E-4**
59
+ > (blank line and `List <T>`, four assertions red on the previous build).
60
+ > **STILL OPEN, in order, when the battery resumes: C1-46 · C3-122 · C3-123 ·
61
+ > C3-124.** No release commit and no version bump has been made — the tree
62
+ > carries 5.4.0's version with three fixes on top, and C3-120/C3-121 are the two
63
+ > that would need re-measuring on the field's subject before any release claims
64
+ > the composition family is back.
65
+ >
66
+ > **What the round confirms, and it is worth keeping:** remediation speed (12
67
+ > reported, 10 closed and verifiable in a release published the same day); the
68
+ > published ledger read as *"an asset, not marketing"*, including how many
69
+ > regression assertions were red before each fix; fixes that move the authority
70
+ > rather than patching the surface (`CONFIG_KEYS` as a registry readers consult,
71
+ > two derived fields off one predicate); first-class degradation under budget
72
+ > (`_partial` names phases completed and pending, `why_stopped`, the skipped
73
+ > walks and the axes left `unknown` — *"an absent measurement, never a zero"*);
74
+ > instrumented progress with stage, counter and ETA; both documents distributed
75
+ > **and the build verifying it against the wheel manifest**; and non-persistence
76
+ > intact over ~45 commands with the repository byte-identical.
77
+ >
78
+ > **Price, re-derived on the same method** (EUR per developer seat per year, 8
79
+ > seats): floor €180 → **€200**, mid €420 → **€450**, ceiling €780 → **€650**.
80
+ > The ceiling fell because `risk` / `enrich` / `data-exposure` are what carries
81
+ > the premium over a SAST and two of the three do not finish here — *capacity
82
+ > that does not terminate cannot be charged for*. **The ceiling is conditional
83
+ > and recoverable: closing C3-120 returns it to €780 and above, because the input
84
+ > to that composition is now correct — in 5.3.1 it finished over bad data.** The
85
+ > Boot 2→3 migration engagement is unchanged at €3 000–6 000 (`migrate-check` /
86
+ > `migrate-recipe` byte-identical: 61,0 days, 532 blocking findings).
87
+ >
88
+ > **Amendment V — three capabilities, all of them recommended by this round and
89
+ > all deferred behind the fix battery.** They are features, not fixes: none of
90
+ > them corrects a false claim.
91
+ >
92
+ > | | what it closes | shape |
93
+ > |---|---|---|
94
+ > | **F-BP** structural annotation↔member binding | E-4 is the second time a whitespace form has switched a security verdict off, and the fix for it is two more lexical tolerances. The pairing wants to be decided on the declaration the IR already builds, not on adjacency in raw text | a member's annotation set comes from the member, not from a line-buffer that anything unrecognised can clear. Bounded to the Java path that already produces `effective_symbol` |
95
+ > | **F-BQ** a deprecation channel for experimental payload keys | C3-124: an experimental tier is permission to change shape, not permission to change it in silence. `auth status` already publishes `deprecated_fields` and nothing else reuses it | one alias table per command, emitted beside the new key for one minor, with the release that removes it named in the payload |
96
+ > | **F-BR** a cost gate in the release battery for the composition family | C3-120 is a ×15,7 regression that a release shipped, and the performance gate that exists (C3-110/C3-113) has never had a cell for `risk` or `enrich` on a body of this size | a synthetic fixture — one ~200 KB method, ~5 000 calls — with a wall-clock assertion on `build_sql_taint`, and warm cells for the three composition commands in the release gate's baseline |
97
+ >
98
+ > ⚠ **Still queued from Amendment U, untouched by this round:** F-BK, F-BL,
99
+ > F-BM, F-BN, F-BO. F-BN (`ask explain-endpoint`) gains an argument from E-4:
100
+ > the derivation it would print is exactly what makes a residue like this
101
+ > visible in one command instead of in a census.
102
+
6
103
  > **PHASE UPDATE 2026-08-12 (audit #28 of 5.3.1 — the first round run under a
7
104
  > written non-persistence protocol, and the round that finds the worst defect
8
105
  > this ledger has recorded).** Same subject (`saint-server @ 3dde037`, branch
@@ -136,7 +136,7 @@ pipx install sourcecode # isolated install, no venv needed
136
136
 
137
137
  # Verify
138
138
  ask version
139
- # ask 5.4.0
139
+ # ask 5.4.2
140
140
  ```
141
141
 
142
142
  Requires Python 3.9+.
@@ -297,3 +297,43 @@ milestone; hash-neutral; suite green.
297
297
  > completion), then `integration_detector` migration (Move 3). Both are named here
298
298
  > only to show the arc — neither is begun until P6.0's evidence is in hand and the
299
299
  > user selects it.
300
+
301
+ ---
302
+
303
+ ## Part V — External re-audit queue (5.4.1 → 5.4.2)
304
+
305
+ The latest external re-audit was run against the installed `5.4.1` build using
306
+ the same read-only harness and repository as the previous rounds. This section
307
+ records only findings that are not already represented elsewhere in this
308
+ roadmap; previously closed findings remain historical evidence, not duplicate
309
+ work items.
310
+
311
+ ### Bugs to fix
312
+
313
+ | ID | Finding | Severity | Resolution / acceptance criterion |
314
+ |----|---------|----------|------------------------------------|
315
+ | D-01 | `risk` drops `bounded_interpolations` while serializing the `sql_taint` block, so `sinks_total` is not auditable. | Medium | Preserve the bounded rows in the published payload. A fixture with one bounded and one free interpolation must report `sinks_total == 1` and one `bounded_interpolations` entry in both `risk` and `enrich`. |
316
+ | D-02 | `existing_test_count` has no population/unit declaration and is confused with the IR's `test_gap_ranking.population.test_files`. | Medium | Publish an explicit unit naming the all-paths, multi-language task population, or remove the redundant field. The chosen authority must be covered end to end. |
317
+ | D-03 | Route projection emits endpoints declared by abstract controller classes, creating phantom routes and inflating the unprotected census. | Low | Carry class modifiers into route-surface metadata and exclude abstract declarers from emitted routes; concrete subtype projection remains unchanged. |
318
+ | D-04 | The distributed defect ledger can claim `C3-123` is open although the `5.4.1` build already excludes static assets. | Informational | Synchronize ledger status with the shipped build and close the stale row. Documentation artifacts must describe the wheel they accompany. |
319
+ | D-05 | `risk -o` once exited successfully without writing a file; the four current invocations did write correctly. | Unreproduced | Keep as an observation only. Do not implement or mark it closed without a reproducible fixture. |
320
+
321
+ The audit explicitly retracted the apparent `N-01` global performance regression
322
+ after cache-settled, consecutive measurements, and retracted `REG-07` schema
323
+ breakage after a complete run preserved the legacy keys. Neither is a bug queue
324
+ item. The already closed `REG-01` through `REG-06` findings are likewise not
325
+ reopened here.
326
+
327
+ ### Recommended capabilities and engineering work
328
+
329
+ These are recommendations from the audit, not fixes to mix into the bug commits:
330
+
331
+ | Item | Recommendation | Product rationale |
332
+ |------|----------------|-------------------|
333
+ | Performance budget gate | Add a release-time assertion for IR construction and keep comparable cold/warm measurements. | Two consecutive releases had performance concerns before the cache-settled protocol separated real regressions from first-build cost. |
334
+ | Cache measurement protocol | Require an established cache and two concordant runs before publishing a performance claim; retain cold/warm values separately. | A single measurement inside a warming batch is not a stable comparison. |
335
+ | CLI grammar closure | Finish the unresolved CLI grammar/command-surface work tracked by `C4-25`. | The audit still identifies inconsistent or incomplete command syntax as an open usability risk. |
336
+
337
+ The existing P6.0 semantic-IR milestone is intentionally not repeated here;
338
+ its class-level type usage and semantic impact work remains the roadmap's
339
+ architectural feature track.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "5.4.0"
7
+ version = "5.4.2"
8
8
  description = "Persistent structural context and ultra-fast repeated analysis for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "5.4.0"
7
+ __version__ = "5.4.2"
@@ -5461,6 +5461,12 @@ def prepare_context_cmd(
5461
5461
  _et_count = getattr(output, "existing_test_count", None)
5462
5462
  if _et_count is not None:
5463
5463
  out["existing_test_count"] = _et_count
5464
+ out["existing_test_count_unit"] = (
5465
+ "test source files present in the complete prepare-context path "
5466
+ "universe, across all detected languages; this is a task-scope "
5467
+ "count and is not the Java IR population in "
5468
+ "`test_gap_ranking.population.test_files`"
5469
+ )
5464
5470
  # C3-13: the basis of the ranked list travels with it — emitted even when the
5465
5471
  # list is empty, because "no gaps" and "no measurement" are different answers.
5466
5472
  if task == "generate-tests":
@@ -112,6 +112,13 @@ _VERB_FROM_OP = {
112
112
  "delete": "DELETE",
113
113
  }
114
114
 
115
+ _STATIC_ASSET_ROOTS = ("/assets/", "/static/", "/public/")
116
+ _STATIC_ASSET_EXTS = frozenset({
117
+ ".css", ".js", ".mjs", ".cjs", ".ts", ".tsx", ".json", ".map",
118
+ ".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".ico",
119
+ ".woff", ".woff2", ".ttf", ".eot",
120
+ })
121
+
115
122
 
116
123
  def _java_files(base: Path) -> list[Path]:
117
124
  out: list[Path] = []
@@ -227,6 +234,14 @@ def _slug(path: str) -> str:
227
234
  return re.sub(r"[^a-z0-9/{}]", "", normalize_path(path).lower())
228
235
 
229
236
 
237
+ def _is_static_asset_path(path: str) -> bool:
238
+ norm = normalize_path(path).lower()
239
+ if any(root in norm for root in _STATIC_ASSET_ROOTS):
240
+ return True
241
+ suffix = Path(norm).suffix
242
+ return suffix in _STATIC_ASSET_EXTS
243
+
244
+
230
245
  def build_consumer_join(
231
246
  root: Path,
232
247
  endpoints: "Iterable[dict]",
@@ -316,7 +331,8 @@ def build_consumer_join(
316
331
  unmatched = [
317
332
  {"join_key": key, "call_sites": group[:5],
318
333
  "consumers": sorted({g["consumer"] for g in group})}
319
- for key, group in _unmatched_groups if not _is_partial(group)
334
+ for key, group in _unmatched_groups
335
+ if not _is_partial(group) and not _is_static_asset_path(group[0]["path"])
320
336
  ]
321
337
  partial_unmatched = [
322
338
  {"join_key": key, "call_sites": group[:5],
@@ -394,8 +410,11 @@ def build_consumer_join(
394
410
  "meaning": (
395
411
  "a consumer makes this request and this server serves no route "
396
412
  "for it — the one direction that needs no assumption about "
397
- "coverage to be true. It holds only for calls whose whole path "
398
- "is written at the call site; a call whose head is an "
413
+ "coverage to be true. static asset paths under /assets/, "
414
+ "/static/ or /public/, and paths ending in static file "
415
+ "extensions, are excluded because they are served files, not "
416
+ "server routes. It holds only for calls whose whole path is "
417
+ "written at the call site; a call whose head is an "
399
418
  "interpolation is in `partial_client_calls`, because the path "
400
419
  "matched against is the written tail (C3-119)"
401
420
  ),
@@ -199,7 +199,16 @@ _METHOD_DECL_RE = re.compile(
199
199
  # the method (its endpoint + return-type edge) is silently dropped (CH-003/Fase 22).
200
200
  r'(?P<inline_anns>(?:@[\w.]+(?:\s*\([^)]*\))?\s+)*)'
201
201
  r'(?:<[\w,\s?]+>\s+)?'
202
- r'(?P<return_type>(?:void|boolean|byte|char|short|int|long|float|double|String|[\w.<>\[\]?,]+)\s+)'
202
+ # E-4: the generic argument may be written apart from its type — `List <Dto>`
203
+ # is legal Java and the annotation applies identically. The character class
204
+ # below admits no space, so `List` matched, the `\s+` ate the space and the
205
+ # name group was offered `<Dto>`: the whole declaration failed and the method
206
+ # was dropped with its endpoint, its gate and its return-type edge — the exact
207
+ # loss the comment above warns about, from a different direction. Measured:
208
+ # 6 endpoints of the field's subject, all in the unsafe direction.
209
+ r'(?P<return_type>(?:void|boolean|byte|char|short|int|long|float|double|String'
210
+ r'|[\w.\[\]?]+\s*<[\w.<>\[\]?,\s]*>(?:\s*\[\s*\])*'
211
+ r'|[\w.<>\[\]?,]+)\s+)'
203
212
  r'(?P<name>[a-z_]\w*)\s*\(',
204
213
  )
205
214
 
@@ -874,8 +883,16 @@ def _parse_param_types(params_str: str) -> list[str]:
874
883
 
875
884
 
876
885
  def _normalize_return_type(raw: str) -> str:
877
- """Normalize return type string: strip whitespace, keep generics."""
878
- return raw.strip()
886
+ """Normalize return type string: strip whitespace, keep generics.
887
+
888
+ E-4: `List <Dto>` and `List<Dto>` are one type written two ways, and once the
889
+ first spelling is extracted at all it must not become a second identity —
890
+ this string is part of `stable_id`, which is what a baseline, a delta and a
891
+ contract diff are keyed on. Only the space *before* a bracket is removed:
892
+ `Map<String, List<X>>` keeps the spelling it already had, so nothing that
893
+ parsed before changes id.
894
+ """
895
+ return re.sub(r"\s+(?=[<>])", "", raw.strip())
879
896
 
880
897
 
881
898
  def _compute_stable_id(
@@ -1144,6 +1161,18 @@ def _extract_symbols(
1144
1161
  stripped = stripped.strip()
1145
1162
  if not stripped:
1146
1163
  continue
1164
+ if not stripped:
1165
+ # E-4. A blank line is not a statement about anything, and clearing
1166
+ # the annotation buffer on it made one switch a security verdict off:
1167
+ # 18 endpoints of the field's subject published `none_detected` with
1168
+ # a live gate written two lines above the method, because
1169
+ # `@Override`, `@M3FiltroSeguridad(...)`, blank, `public List<Dto>
1170
+ # obtenerTodos()` reached the fall-through reset at the end of this
1171
+ # loop. Java binds an annotation to the next declaration whatever
1172
+ # whitespace separates them. The only blank-line guard used to sit
1173
+ # inside the block-comment branch above, which a genuinely empty line
1174
+ # never enters.
1175
+ continue
1147
1176
  if stripped.startswith("//") or stripped.startswith("*"):
1148
1177
  continue
1149
1178
 
@@ -1261,7 +1290,9 @@ def _extract_symbols(
1261
1290
  conf = "high" if ("public" in modifiers or pending_anns) else "medium"
1262
1291
 
1263
1292
  # Extract return type and params from matched line
1264
- _ret_raw = (mth_m.group("return_type") or "").strip()
1293
+ _ret_raw = _normalize_return_type(
1294
+ mth_m.group("return_type") or ""
1295
+ )
1265
1296
  _after_paren = stripped[mth_m.end():]
1266
1297
  # Paren-balanced close (DR-1): a naive first-')' truncated the
1267
1298
  # parameter list at the first annotation ')' — e.g.
@@ -4928,6 +4959,7 @@ def _build_route_surface(
4928
4959
  "fqn": sym.symbol,
4929
4960
  "prefixes": prefixes,
4930
4961
  "own_endpoints": [],
4962
+ "abstract": "abstract" in (sym.modifiers or []),
4931
4963
  "has_path_ann": "@Path" in sym.annotations or "@RequestMapping" in sym.annotations,
4932
4964
  }
4933
4965
 
@@ -4985,6 +5017,12 @@ def _build_route_surface(
4985
5017
  (method, suffix, sym.symbol, sym.stable_id)
4986
5018
  )
4987
5019
 
5020
+ # Abstract controllers contribute declarations for concrete subtype
5021
+ # projection, but Spring does not register the abstract class itself as
5022
+ # a handler bean.
5023
+ if class_info.get(cls_simple, {}).get("abstract"):
5024
+ continue
5025
+
4988
5026
  # P1 FIX: extract security annotations (method-level first, class fallback)
4989
5027
  _cls_sym_for_sec = class_sym_by_simple.get(cls_simple)
4990
5028
  _sec = _route_security_from_sym(sym, _cls_sym_for_sec, custom_security)