sourcecode 5.7.2__tar.gz → 5.8.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 (361) hide show
  1. {sourcecode-5.7.2 → sourcecode-5.8.0}/CHANGELOG.md +124 -0
  2. {sourcecode-5.7.2 → sourcecode-5.8.0}/PKG-INFO +4 -3
  3. {sourcecode-5.7.2 → sourcecode-5.8.0}/README.md +3 -2
  4. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/DEFECT-LEDGER.md +30 -5
  5. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/DEVELOPMENT-ROADMAP.md +267 -0
  6. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/USER_GUIDE.md +1 -1
  7. {sourcecode-5.7.2 → sourcecode-5.8.0}/pyproject.toml +1 -1
  8. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/__init__.py +1 -1
  9. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/archetype.py +8 -1
  10. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/canonical_ir.py +6 -1
  11. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/change_plan.py +7 -0
  12. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/cir_graphs.py +67 -0
  13. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/cli.py +17 -10
  14. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/container_wiring.py +226 -17
  15. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/contract_diff.py +6 -1
  16. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/java.py +7 -1
  17. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/explain.py +46 -0
  18. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/integration_coordinates.py +109 -1
  19. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/non_coverage.py +31 -0
  20. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/posture.py +138 -28
  21. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/pr_impact.py +52 -1
  22. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/reconciliation.py +33 -0
  23. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/repository_ir.py +791 -57
  24. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/security_posture.py +27 -7
  25. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/semantic_integration_engine.py +33 -0
  26. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/serializer.py +9 -2
  27. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/spring_impact.py +24 -6
  28. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/spring_model.py +19 -0
  29. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/verify_edit.py +9 -0
  30. {sourcecode-5.7.2 → sourcecode-5.8.0}/.github/workflows/build-windows.yml +0 -0
  31. {sourcecode-5.7.2 → sourcecode-5.8.0}/.github/workflows/perf-gate.yml +0 -0
  32. {sourcecode-5.7.2 → sourcecode-5.8.0}/.gitignore +0 -0
  33. {sourcecode-5.7.2 → sourcecode-5.8.0}/.ruff.toml +0 -0
  34. {sourcecode-5.7.2 → sourcecode-5.8.0}/CLAUDE.md +0 -0
  35. {sourcecode-5.7.2 → sourcecode-5.8.0}/CONTRIBUTING.md +0 -0
  36. {sourcecode-5.7.2 → sourcecode-5.8.0}/LICENSE +0 -0
  37. {sourcecode-5.7.2 → sourcecode-5.8.0}/NOTICE +0 -0
  38. {sourcecode-5.7.2 → sourcecode-5.8.0}/SECURITY.md +0 -0
  39. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/CACHE.md +0 -0
  40. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/COMMERCIAL-OFFER.md +0 -0
  41. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/CUSTOMER-VALIDATION.md +0 -0
  42. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/DEMO-5MIN.md +0 -0
  43. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/EXECUTION-PLAN-12MO.md +0 -0
  44. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/EXECUTIVE-ROADMAP.md +0 -0
  45. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/MANUAL-USUARIO.md +0 -0
  46. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/MODERNIZATION.md +0 -0
  47. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/PRODUCT_AUDIT.md +0 -0
  48. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/PRODUCT_IDENTITY.md +0 -0
  49. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/PRODUCT_TIERS.md +0 -0
  50. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/RC1-CHECKLIST.md +0 -0
  51. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/RELEASE-CHECKLIST.md +0 -0
  52. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/CATALOG-retrieval-intents.md +0 -0
  53. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/DESIGN-knowledge-provider-integration.md +0 -0
  54. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/DESIGN-p5.3b-statement-surface.md +0 -0
  55. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/DESIGN-p5.5-span-index.md +0 -0
  56. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/DESIGN-p6-semantic-impact-engine.md +0 -0
  57. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/DESIGN-p6-semantic-integration-engine.md +0 -0
  58. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/DESIGN-p7-semantic-services.md +0 -0
  59. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/DESIGN-robustness-security-inference.md +0 -0
  60. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/DESIGN-semantic-retrieval.md +0 -0
  61. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/EPV-p6-class-typeref.md +0 -0
  62. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/REPORT-p6-flow-analyzer-comparison.md +0 -0
  63. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/REPORT-p6-integration-detector-comparison.md +0 -0
  64. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/REPORT-p8-flow-analyzer-migration.md +0 -0
  65. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/REPORT-p9-integration-detector-migration.md +0 -0
  66. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/adr/0001-semantic-ir.md +0 -0
  67. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/adr/0002-source-evidence-boundary.md +0 -0
  68. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/adr/0003-architectural-characterization.md +0 -0
  69. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/adr/0004-semantic-retrieval.md +0 -0
  70. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/adr/0005-evidence-reconciliation.md +0 -0
  71. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/adr/0006-performance-harness.md +0 -0
  72. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/adr/0007-composition-semantics.md +0 -0
  73. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/adr/0008-answer-coherence.md +0 -0
  74. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/consumer-disposition-inventory.md +0 -0
  75. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/overview.md +0 -0
  76. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/product-architecture-review.md +0 -0
  77. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/product-strategy-layering.md +0 -0
  78. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/semantic-ir-consumer-matrix.md +0 -0
  79. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/architecture/semantic-ir-roadmap.md +0 -0
  80. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/baseline-ci.yml +0 -0
  81. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/commercial-pipeline-audit.md +0 -0
  82. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/contracts-ci.yml +0 -0
  83. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/contracts.md +0 -0
  84. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/eval/2026-06-15-petclinic-rest-issue11-vets-pagination.md +0 -0
  85. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/eval/2026-06-15-petclinic-rest-issue147-validation.md +0 -0
  86. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/eval/2026-06-15-petclinic-rest-workflow.md +0 -0
  87. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/eval/2026-06-15-phase18-openapi-surface.md +0 -0
  88. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/eval/2026-06-15-phase20-validation-surface.md +0 -0
  89. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/eval/2026-06-16-broadleaf-checkout-impact-fieldtest.md +0 -0
  90. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/eval/2026-06-16-petclinic-rest-issue11-weakness2-closed.md +0 -0
  91. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/eval/2026-06-16-spring-petclinic-issue2333-dead-vets.md +0 -0
  92. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/licensing-schema.md +0 -0
  93. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/migrate-check.md +0 -0
  94. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/README.md +0 -0
  95. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/REGRESSION-GATE.md +0 -0
  96. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/SUMMARY.md +0 -0
  97. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/alfresco__ask__cold.json +0 -0
  98. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/alfresco__ask__warm.json +0 -0
  99. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/broadleaf__ask__cold.json +0 -0
  100. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/broadleaf__ask__warm.json +0 -0
  101. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/eureka__ask__cold.json +0 -0
  102. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/eureka__ask__warm.json +0 -0
  103. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/jenkins__ask__cold.json +0 -0
  104. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/jenkins__ask__warm.json +0 -0
  105. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/jobrunr__ask__cold.json +0 -0
  106. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/jobrunr__ask__warm.json +0 -0
  107. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/keycloak__ask__cold.json +0 -0
  108. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/keycloak__ask__warm.json +0 -0
  109. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/neo4j__ask__cold.json +0 -0
  110. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/neo4j__ask__warm.json +0 -0
  111. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/ofbiz__ask__cold.json +0 -0
  112. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/ofbiz__ask__warm.json +0 -0
  113. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/openmrs__ask__cold.json +0 -0
  114. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/openmrs__ask__warm.json +0 -0
  115. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/petclinic__ask__cold.json +0 -0
  116. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.15/petclinic__ask__warm.json +0 -0
  117. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/SUMMARY.md +0 -0
  118. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/alfresco__ask__cold.json +0 -0
  119. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/alfresco__ask__warm.json +0 -0
  120. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/broadleaf__ask__cold.json +0 -0
  121. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/broadleaf__ask__warm.json +0 -0
  122. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/eureka__ask__cold.json +0 -0
  123. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/eureka__ask__warm.json +0 -0
  124. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/jenkins__ask__cold.json +0 -0
  125. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/jenkins__ask__warm.json +0 -0
  126. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/jobrunr__ask__cold.json +0 -0
  127. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/jobrunr__ask__warm.json +0 -0
  128. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/keycloak__ask__cold.json +0 -0
  129. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/keycloak__ask__warm.json +0 -0
  130. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/neo4j__ask__cold.json +0 -0
  131. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/neo4j__ask__warm.json +0 -0
  132. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/ofbiz__ask__cold.json +0 -0
  133. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/ofbiz__ask__warm.json +0 -0
  134. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/openmrs__ask__cold.json +0 -0
  135. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/openmrs__ask__warm.json +0 -0
  136. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/petclinic__ask__cold.json +0 -0
  137. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/2.5.16/petclinic__ask__warm.json +0 -0
  138. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/gate-latest/README.md +0 -0
  139. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/gate-latest/broadleaf__ask__warm.json +0 -0
  140. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/gate-latest/broadleaf__endpoints__warm.json +0 -0
  141. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/gate-latest/broadleaf__migrate-check__warm.json +0 -0
  142. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/gate-latest/broadleaf__posture__warm.json +0 -0
  143. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/gate-latest/broadleaf__spring-audit__warm.json +0 -0
  144. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/gate-latest/broadleaf__validation__warm.json +0 -0
  145. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/perf/baselines/reference-best/README.md +0 -0
  146. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/posture.md +0 -0
  147. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/privacy.md +0 -0
  148. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/pro-experience-audit.md +0 -0
  149. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/schema.md +0 -0
  150. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/spec/envelope-v1.md +0 -0
  151. {sourcecode-5.7.2 → sourcecode-5.8.0}/docs/spring-audit-ci.yml +0 -0
  152. {sourcecode-5.7.2 → sourcecode-5.8.0}/raw +0 -0
  153. {sourcecode-5.7.2 → sourcecode-5.8.0}/scripts/compare_integration_engines.py +0 -0
  154. {sourcecode-5.7.2 → sourcecode-5.8.0}/scripts/customer_smoke_test.sh +0 -0
  155. {sourcecode-5.7.2 → sourcecode-5.8.0}/scripts/generate_jdk_exports.py +0 -0
  156. {sourcecode-5.7.2 → sourcecode-5.8.0}/scripts/perf_gate.py +0 -0
  157. {sourcecode-5.7.2 → sourcecode-5.8.0}/scripts/perf_harness.py +0 -0
  158. {sourcecode-5.7.2 → sourcecode-5.8.0}/scripts/sync_gate_anchors.py +0 -0
  159. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/adaptive_scanner.py +0 -0
  160. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/architectural_baseline.py +0 -0
  161. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/architectural_delta.py +0 -0
  162. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/architecture_analyzer.py +0 -0
  163. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/architecture_summary.py +0 -0
  164. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/ast_extractor.py +0 -0
  165. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/audit_report.py +0 -0
  166. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/baseline_autocapture.py +0 -0
  167. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/bundled_docs.py +0 -0
  168. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/cache.py +0 -0
  169. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/cache_model.py +0 -0
  170. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/call_surface.py +0 -0
  171. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/caller_metrics.py +0 -0
  172. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/caller_reach.py +0 -0
  173. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/chain_rules.py +0 -0
  174. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/classifier.py +0 -0
  175. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/client_calls.py +0 -0
  176. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  177. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/compare.py +0 -0
  178. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/confidence_analyzer.py +0 -0
  179. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/constraint_diff.py +0 -0
  180. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/consumer_join.py +0 -0
  181. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/context_cache.py +0 -0
  182. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/context_graph.py +0 -0
  183. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/context_scorer.py +0 -0
  184. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/context_summarizer.py +0 -0
  185. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/contract_init.py +0 -0
  186. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/contract_model.py +0 -0
  187. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/contract_pipeline.py +0 -0
  188. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/coverage_parser.py +0 -0
  189. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/data_exposure.py +0 -0
  190. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/data_labels.py +0 -0
  191. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/declarations.py +0 -0
  192. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/defect_identity.py +0 -0
  193. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/degradation.py +0 -0
  194. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/dependency_analyzer.py +0 -0
  195. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/deployment_prefix.py +0 -0
  196. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detach.py +0 -0
  197. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/__init__.py +0 -0
  198. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/base.py +0 -0
  199. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  200. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/dart.py +0 -0
  201. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/dotnet.py +0 -0
  202. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/elixir.py +0 -0
  203. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/go.py +0 -0
  204. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/heuristic.py +0 -0
  205. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/hybrid.py +0 -0
  206. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  207. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/nodejs.py +0 -0
  208. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/parsers.py +0 -0
  209. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/php.py +0 -0
  210. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/project.py +0 -0
  211. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/python.py +0 -0
  212. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/ruby.py +0 -0
  213. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/rust.py +0 -0
  214. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/systems.py +0 -0
  215. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/terraform.py +0 -0
  216. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/detectors/tooling.py +0 -0
  217. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/doc_analyzer.py +0 -0
  218. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/dynamic_argument_surface.py +0 -0
  219. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/endpoint_literals.py +0 -0
  220. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/endpoint_metrics.py +0 -0
  221. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  222. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/env_analyzer.py +0 -0
  223. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/envelope.py +0 -0
  224. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/environment_resolution.py +0 -0
  225. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/error_schema.py +0 -0
  226. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/evidence_provider.py +0 -0
  227. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/execution_plan.py +0 -0
  228. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/facts/__init__.py +0 -0
  229. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/facts/registry.json +0 -0
  230. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/file_chunker.py +0 -0
  231. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/file_classifier.py +0 -0
  232. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/filter_surface.py +0 -0
  233. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/format_contract.py +0 -0
  234. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/fqn_utils.py +0 -0
  235. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/gate_anchors.py +0 -0
  236. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/git_analyzer.py +0 -0
  237. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/git_checkout.py +0 -0
  238. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/graph_analyzer.py +0 -0
  239. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/graph_evidence.py +0 -0
  240. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/hibernate_strat.py +0 -0
  241. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/identity_fallback.py +0 -0
  242. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/jdk_exports.py +0 -0
  243. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/license.py +0 -0
  244. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/manifest_cache.py +0 -0
  245. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/__init__.py +0 -0
  246. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/dependency_import.py +0 -0
  247. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  248. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  249. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  250. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  251. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  252. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/orchestrator.py +0 -0
  253. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/registry.py +0 -0
  254. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/runner.py +0 -0
  255. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp/server.py +0 -0
  256. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/mcp_nudge.py +0 -0
  257. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/metrics_analyzer.py +0 -0
  258. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/migrate_check.py +0 -0
  259. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/migration_blast.py +0 -0
  260. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/openapi_surface.py +0 -0
  261. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/openrewrite_recipe.py +0 -0
  262. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/output_budget.py +0 -0
  263. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/output_ceiling.py +0 -0
  264. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/output_encoding.py +0 -0
  265. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/parallel.py +0 -0
  266. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/parse_cache.py +0 -0
  267. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/partial_contract.py +0 -0
  268. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/path_admission.py +0 -0
  269. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/path_filters.py +0 -0
  270. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/perf.py +0 -0
  271. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/phased_run.py +0 -0
  272. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/pipe_contract.py +0 -0
  273. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/pr_comment_renderer.py +0 -0
  274. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/prepare_context.py +0 -0
  275. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/product_info.py +0 -0
  276. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/progress.py +0 -0
  277. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/provenance.py +0 -0
  278. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/ranking_engine.py +0 -0
  279. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/readiness_timeline.py +0 -0
  280. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/readonly.py +0 -0
  281. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/redactor.py +0 -0
  282. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/reference_facts.py +0 -0
  283. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/release_info.py +0 -0
  284. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/relevance_scorer.py +0 -0
  285. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/remedies.py +0 -0
  286. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/rename_refactor.py +0 -0
  287. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/repo_classifier.py +0 -0
  288. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/__init__.py +0 -0
  289. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/context.py +0 -0
  290. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/errors.py +0 -0
  291. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/executor.py +0 -0
  292. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/planner.py +0 -0
  293. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/query.py +0 -0
  294. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/request.py +0 -0
  295. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/resolution.py +0 -0
  296. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/result.py +0 -0
  297. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/retriever.py +0 -0
  298. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/runtime.py +0 -0
  299. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/steps.py +0 -0
  300. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  301. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/steps_graph.py +0 -0
  302. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/steps_impact.py +0 -0
  303. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/steps_intf.py +0 -0
  304. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/steps_struct.py +0 -0
  305. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  306. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/ris.py +0 -0
  307. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/risk.py +0 -0
  308. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/risk_model.py +0 -0
  309. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/rule_catalog.py +0 -0
  310. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/rule_pass.py +0 -0
  311. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/runs.py +0 -0
  312. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/runtime_classifier.py +0 -0
  313. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/sarif.py +0 -0
  314. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/scanner.py +0 -0
  315. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/schema.py +0 -0
  316. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  317. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/security_chain.py +0 -0
  318. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/security_config.py +0 -0
  319. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/security_config_scan.py +0 -0
  320. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/semantic_analyzer.py +0 -0
  321. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/semantic_impact_engine.py +0 -0
  322. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/semantic_services.py +0 -0
  323. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/servlet_surface.py +0 -0
  324. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/source_text.py +0 -0
  325. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/spring_event_topology.py +0 -0
  326. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/spring_findings.py +0 -0
  327. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/spring_profiles.py +0 -0
  328. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/spring_properties.py +0 -0
  329. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/spring_security_audit.py +0 -0
  330. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/spring_semantic.py +0 -0
  331. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/spring_tx_analyzer.py +0 -0
  332. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/sql_taint.py +0 -0
  333. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/summarizer.py +0 -0
  334. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/target_admission.py +0 -0
  335. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/telemetry/__init__.py +0 -0
  336. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/telemetry/config.py +0 -0
  337. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/telemetry/consent.py +0 -0
  338. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/telemetry/events.py +0 -0
  339. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/telemetry/filters.py +0 -0
  340. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/telemetry/transport.py +0 -0
  341. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/test_gap_ranking.py +0 -0
  342. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/test_sources.py +0 -0
  343. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/text_input.py +0 -0
  344. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/timeline.py +0 -0
  345. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/token_estimate.py +0 -0
  346. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/tree_utils.py +0 -0
  347. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/type_usage_surface.py +0 -0
  348. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/validation_inference.py +0 -0
  349. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/validation_surface.py +0 -0
  350. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/verify_repo.py +0 -0
  351. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/verify_rules.py +0 -0
  352. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/version_check.py +0 -0
  353. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/waivers.py +0 -0
  354. {sourcecode-5.7.2 → sourcecode-5.8.0}/src/sourcecode/workspace.py +0 -0
  355. {sourcecode-5.7.2 → sourcecode-5.8.0}/supabase/functions/README.md +0 -0
  356. {sourcecode-5.7.2 → sourcecode-5.8.0}/supabase/functions/get-license/index.ts +0 -0
  357. {sourcecode-5.7.2 → sourcecode-5.8.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  358. {sourcecode-5.7.2 → sourcecode-5.8.0}/supabase/functions/telemetry/index.ts +0 -0
  359. {sourcecode-5.7.2 → sourcecode-5.8.0}/supabase/sql/license_event_ordering.sql +0 -0
  360. {sourcecode-5.7.2 → sourcecode-5.8.0}/supabase/sql/licensing_schema.sql +0 -0
  361. {sourcecode-5.7.2 → sourcecode-5.8.0}/supabase/sql/telemetry_events.sql +0 -0
@@ -2,6 +2,130 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.8.0] — 2026-08-16
6
+
7
+ *The field queue from the two 5.7.3 validations, closed end to end: the blast
8
+ radius stopped losing the relation everything else is projected over, and the
9
+ answers that were floors stopped being published as totals. One commit per row,
10
+ A/B measured against the parent commit on each.*
11
+
12
+ ### Fixed
13
+ - A type that declares parameters of its own still declares a supertype (E-34).
14
+ The parameter block was matched as "everything up to the next `>`", which
15
+ stops inside `<T, V>`; to reach the body brace the group then swallowed the
16
+ whole `extends …` clause. `class Generic<T, V> extends Base {` produced **no
17
+ edge of either kind**, and 254 of 8 182 types across the fleet (3,1 %) had no
18
+ supertype anywhere in the model. The block is now a balanced `<…>` span, which
19
+ also settles the bounded-parameter trap `class E<T extends Map<String, X>>`.
20
+ Measured over 25 repositories: 0 type declarations lost, 0 gained; clauses
21
+ recovered wherever the shape occurs (dubbo +207, alfresco +130, jenkins +104).
22
+ - A call made through an inherited member belongs to the class that makes it
23
+ (E-27). Neither pass climbed the supertype chain: the field map is keyed on
24
+ the class that declares the field, and the intra-class scan only ever saw the
25
+ methods a class declares itself. Both now resolve against `supertype_closure`,
26
+ one authority for what is above a type, nearest declaration first — Java's own
27
+ rule. On BroadleafCommerce, `ask impact ExploitProtectionService .` goes from
28
+ **38 to 276 endpoints affected** and from **34 to 247 security declarations**;
29
+ `ask impact FormBuilderService .` from 3 to 7 direct callers. Fleet: +1 021
30
+ call-graph edges on Broadleaf, +7 210 on killbill, **+0 on mall** — the
31
+ correct answer for a repository whose calls are not inheritance-mediated.
32
+ - A filter is container-wired through the chain, not the clause (E-28). The
33
+ classification compared a class's *immediate* supertypes against the
34
+ framework's published extension points, so one repository-local hop hid a
35
+ filter: `AdminSecurityFilter` published `low` / `1.0` / `confidence: high` /
36
+ "Low-risk isolated change" over the admin chain, while its base class one
37
+ level up published `high`. Now `high` / `risk_score: null` / `container_wired`,
38
+ and the evidence names the hop (`inherits X via Y`) instead of citing a clause
39
+ the class never wrote. `ask spring-audit .` carries 20 → 42 authentication
40
+ pipeline relations.
41
+ - Two routes that share one inherited handler are two endpoints (E-30). The
42
+ de-duplication was already keyed on `endpoint_id`; the loss was one step
43
+ earlier, in walking from a caller to its own class when the route's controller
44
+ is a subclass. `impact-chain ExploitProtectionService` 189 → 239 affected
45
+ endpoints, `FormBuilderService` 186 → 236.
46
+ - A walk that stopped short says so (E-32). `bfs_truncated` reported whether the
47
+ hub guard cut the depth, not whether the closure was exhausted, so a run whose
48
+ frontier was still 76 nodes wide published `false` beside its depth. It now
49
+ means "the closure was not exhausted", with `closure_complete`,
50
+ `depth_capped_by_guard` and `frontier_open_at_depth_limit` beside it — the two
51
+ ways a walk stops early have different remedies — and the explanation says it
52
+ in prose.
53
+ - The owner of an anonymous member, and the `throws` clause it declares (E-33).
54
+ `verify-edit` answered correctly and cited the wrong evidence twice: a method
55
+ of an anonymous `Runnable` reported as removed from the file's named type, and
56
+ a removed `throws PrivilegedActionException` not reported at all. Anonymous
57
+ class bodies now open a scope named the way Java names it (`Outer$1`), the
58
+ public-surface readers skip such owners, and the `throws` clause is part of the
59
+ compared signature (the DR-1 precedent).
60
+ - A bean reached from a template is declared, not silently missed (E-31).
61
+ **NC-012** states that a SpEL `@beanName` reference in a template is outside
62
+ what a call-graph projection looks at, and what answers it today. The block
63
+ itself was published only by `pr-impact`; `impact`, `impact-chain` and `plan`
64
+ publish it now.
65
+ - A JAX-RS client is an outbound integration (E-26). `export --integrations`
66
+ returned 0 on a codebase that exists to speak HTTP. The JAX-RS client API and
67
+ RESTEasy are admitted the way every other row is — an FQN-guarded import edge
68
+ to a type named by a published specification, never the build coordinate.
69
+ keycloak-config-cli 0 → 3, seven golden repositories byte-identical.
70
+ - A statement is not a declaration (E-35). `return unknownHelper("x");` matched
71
+ the method-declaration shape and minted a method that does not exist, with a
72
+ call edge pointing at it: 1 047 such symbols on BroadleafCommerce (4,4 % of
73
+ its methods), 846 on killbill. The return-type slot is the discriminator;
74
+ `void` is deliberately not in the rejected set. Phantoms → 0, and the method
75
+ population falls by exactly that count, so no real declaration is lost.
76
+
77
+ ### Changed
78
+ - `impact`, `impact-chain` and `plan` publish the `blast_radius` non-coverage
79
+ block (E-31), and `impact` publishes `closure_complete`,
80
+ `depth_capped_by_guard` and `frontier_open_at_depth_limit` (E-32).
81
+ - The symbol population grows by the anonymous types now modelled and shrinks by
82
+ the phantom methods no longer minted (E-33, E-35). Endpoint populations are
83
+ byte-identical on every repository measured.
84
+
85
+ ### Positioning
86
+ - **CL-30 reversed.** The exclusion claim it froze is measurable again — the
87
+ reversal condition the row set was met, not argued. The field revalidation on
88
+ the evaluator's repository is still owed before the differential promise goes
89
+ into commercial material, and the row says so.
90
+
91
+ ## [5.7.3] — 2026-08-16
92
+
93
+ *Two more rows of the same family — a population published under the wrong name,
94
+ and source read past the point it could be read — plus one row closed by
95
+ measurement rather than by code.*
96
+
97
+ ### Fixed
98
+ - A caller that never names the target is not a direct caller (E-11). On
99
+ `examples`, `ask impact OrdersService` reported critical/35.5 with four direct
100
+ callers; three were `MicroserviceUtils` and its nested classes, which contain
101
+ zero occurrences of `OrdersService` — what they touch is the `Service`
102
+ interface it implements. The DI recovery stays (a walk that stops at the
103
+ interface loses every real production dependent), but the population is
104
+ published under its own name — `interface_mediated_callers` — and scored on
105
+ the indirect axis, because a class that reaches the target only if the
106
+ container binds this implementation is at the same conditional distance as a
107
+ transitive caller. Reach is untouched: endpoints, transactions, mappers and
108
+ cross-module figures are unchanged. Measured across 8 symbols on 7
109
+ repositories: 7 byte-identical, `examples/OrdersService` 4 → 1 direct callers,
110
+ 35.5 → 31.0, endpoints still 3.
111
+ - Source that cannot compile is named as such (E-10). spaghetti-api's
112
+ `BrokenSyntax.java` has two opening braces and no closing one, and the payload
113
+ reported a clean class and method from it with nothing anywhere saying the file
114
+ was broken. A `parse_structure` gap now names files whose braces do not balance
115
+ once comments and strings are masked, and states what is missing from the
116
+ model. The symbols stay — a file that fails to compile still tells us its type
117
+ exists — and the cost is a `str.count` per file, with the mask running only for
118
+ the rare candidate, so braces inside strings and comments are not false
119
+ positives.
120
+
121
+ ### Verified
122
+ - C3-112 was open on a stale state: B4 (5.7.0) already replaced the clean-tree
123
+ predicate with `_can_move_a_verdict`, and `analysis.disregarded_changes`
124
+ already publishes the paths git printed that no axis reads. Re-measured on
125
+ jobrunr with an untracked `.md` present: **0.44 s**, `model_built: false`,
126
+ against the 3.74 s the row recorded at 5.2.0. Closed by measurement; no code
127
+ was changed for it.
128
+
5
129
  ## [5.7.2] — 2026-08-16
6
130
 
7
131
  *Four rows from the open ledger, chosen for one property: each was a number or a
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 5.7.2
3
+ Version: 5.8.0
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  Project-URL: Homepage, https://github.com/HarounDominique/sourcecode
6
6
  Project-URL: Repository, https://github.com/HarounDominique/sourcecode
@@ -47,7 +47,7 @@ Description-Content-Type: text/markdown
47
47
 
48
48
  **Context · Impact · Migration · Architecture · Review — everything from one structural model.**
49
49
 
50
- ![Version](https://img.shields.io/badge/version-5.7.2-blue)
50
+ ![Version](https://img.shields.io/badge/version-5.8.0-blue)
51
51
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
52
52
 
53
53
  Copyright (c) 2026 Dominique Haroun. All rights reserved. ASK Engine 5.7.0 and
@@ -135,7 +135,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
135
135
  # pip / pipx
136
136
  pipx install sourcecode # or: pip install sourcecode
137
137
 
138
- ask version # ask 5.7.2 — and, on a build that has aged,
138
+ ask version # ask 5.8.0 — and, on a build that has aged,
139
139
  # how many releases have probably shipped since
140
140
  ```
141
141
 
@@ -487,6 +487,7 @@ And one about blast radius:
487
487
  | Not covered | Why | What answers it |
488
488
  |---|---|---|
489
489
  | **The reach of a component the container invokes rather than the code — a security filter chain, an around-advice aspect, a `@ControllerAdvice`, a bean resolved through the interface it implements.** | Blast radius here is projected over the call graph: who calls the changed symbol, and which routes reach those callers. Nothing in the repository calls a container-wired component, so its measured call-graph fan-in is ~0 and every figure derived from it — affected endpoints, direct callers, rollback surface — reads as no impact. The fan-in figure is correct; using it as a risk proxy on this population is not. Publishing that as a low verdict would be a confident zero, which is the one thing this product does not do. | Such a change is flagged in `analysis_warnings` and its verdict is floored, never rated on fan-in. The reach itself is measured elsewhere and was there all along: `ask endpoints` resolves a gate annotation to the routes carrying it, `ask explain <class>` prints the request chain a configuration declares with its source line, and `ask posture --profile <set>` states what each profile leaves open. Projecting those into the blast radius (pointcut → routes, matcher → routes) is a measurement this command does not yet make. |
490
+ | **A bean reached from a template rather than from code — a Thymeleaf or JSP expression that resolves a bean by name (`${@blExploitProtectionService.getCsrfTokenParameter()}`) and invokes a method on it.** | Every reacher this blast radius models is a typed edge or a structural fact recovered from Java: a call, an injection, a container extension point, a string literal inside a reflective lookup. A template is none of those — it is not compiled against the bean, and the reference lives in a file this analysis does not parse. So a component invoked only from a template has a measured fan-in of ~0 and, unlike every other blind spot here, that silence was published without a label: the field found `blind_spots: []` on a service two templates call by name. The miss is ordinary; presenting it as an empty result is the defect (E-31). | A SpEL bean reference in a template is fixed, published syntax (`@beanName` inside `${...}`), so a text search for the bean's name across `.html`/`.jsp` answers it today: `grep -rn '@<beanName>' src/main/resources`. Admitting it as a fourth reacher would be evidence-shaped for the same reason — the reference is a literal naming a bean this repository declares — and it is not modelled yet, which is what this row states rather than implies. |
490
491
 
491
492
  And one about request-body validation:
492
493
 
@@ -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.7.2-blue)
7
+ ![Version](https://img.shields.io/badge/version-5.8.0-blue)
8
8
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
9
9
 
10
10
  Copyright (c) 2026 Dominique Haroun. All rights reserved. ASK Engine 5.7.0 and
@@ -92,7 +92,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
92
92
  # pip / pipx
93
93
  pipx install sourcecode # or: pip install sourcecode
94
94
 
95
- ask version # ask 5.7.2 — and, on a build that has aged,
95
+ ask version # ask 5.8.0 — and, on a build that has aged,
96
96
  # how many releases have probably shipped since
97
97
  ```
98
98
 
@@ -444,6 +444,7 @@ And one about blast radius:
444
444
  | Not covered | Why | What answers it |
445
445
  |---|---|---|
446
446
  | **The reach of a component the container invokes rather than the code — a security filter chain, an around-advice aspect, a `@ControllerAdvice`, a bean resolved through the interface it implements.** | Blast radius here is projected over the call graph: who calls the changed symbol, and which routes reach those callers. Nothing in the repository calls a container-wired component, so its measured call-graph fan-in is ~0 and every figure derived from it — affected endpoints, direct callers, rollback surface — reads as no impact. The fan-in figure is correct; using it as a risk proxy on this population is not. Publishing that as a low verdict would be a confident zero, which is the one thing this product does not do. | Such a change is flagged in `analysis_warnings` and its verdict is floored, never rated on fan-in. The reach itself is measured elsewhere and was there all along: `ask endpoints` resolves a gate annotation to the routes carrying it, `ask explain <class>` prints the request chain a configuration declares with its source line, and `ask posture --profile <set>` states what each profile leaves open. Projecting those into the blast radius (pointcut → routes, matcher → routes) is a measurement this command does not yet make. |
447
+ | **A bean reached from a template rather than from code — a Thymeleaf or JSP expression that resolves a bean by name (`${@blExploitProtectionService.getCsrfTokenParameter()}`) and invokes a method on it.** | Every reacher this blast radius models is a typed edge or a structural fact recovered from Java: a call, an injection, a container extension point, a string literal inside a reflective lookup. A template is none of those — it is not compiled against the bean, and the reference lives in a file this analysis does not parse. So a component invoked only from a template has a measured fan-in of ~0 and, unlike every other blind spot here, that silence was published without a label: the field found `blind_spots: []` on a service two templates call by name. The miss is ordinary; presenting it as an empty result is the defect (E-31). | A SpEL bean reference in a template is fixed, published syntax (`@beanName` inside `${...}`), so a text search for the bean's name across `.html`/`.jsp` answers it today: `grep -rn '@<beanName>' src/main/resources`. Admitting it as a fourth reacher would be evidence-shaped for the same reason — the reference is a literal naming a bean this repository declares — and it is not modelled yet, which is what this row states rather than implies. |
447
448
 
448
449
  And one about request-body validation:
449
450
 
@@ -57,7 +57,7 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
57
57
  | C1-13 | **Hub-guard publishes `endpoints_total: 0` instead of `unknown`.** A service with 780 callers is capped at `depth=1` for cost — defensible — but reports 0 endpoints reached, contradicting the epistemic standard the tool imposes on itself everywhere else. Hubs are precisely where blast radius is asked for | 3.2.1 (eval #4) | Medium | **closed 3.6.0** — no battery repository crosses the guard (openmrs-core's largest hub, `Context`, has 215 caller classes against a threshold of 500), so it was reproduced on a generated fixture: `bfs_truncated: true`, `depth_reached: 1`, `endpoints_affected_count: 0` beside four more zeros nothing looked for. A reach-derived count of 0 under the cap is now `null`, named in `stats.unknown_under_cap`. The distinction is the point and is asserted both ways: a **non-zero** count under the cap stays a number (it is a floor the effect block already describes), and an **uncapped** run keeps its measured zero, because converting that would be the mirror-image defect. The hub threshold moved to module level so the fixture crosses the real guard instead of a copy of its value |
58
58
  | C1-14 | **File-count drift across commands**: 3 334 / 3 337 / 3 342 / 3 374 files for one repository in one session. No command is wrong in isolation; together they cost the one thing a deterministic tool cannot lose — that its numbers agree with themselves | 3.2.1 (eval #4) | Medium | **within-document half closed 3.6.0 · cross-command half does not reproduce.** Of six commands measured on openmrs-core (`--compact`, `endpoints`, `spring-audit`, `migrate-check`, `modernize`, `validation`) only `migrate-check` publishes a scanned-file count at all, so there are no two figures left to disagree. What does reproduce is the same defect *inside one document*: `affected_files` named two populations — 265 under `summary` (every finding, any scope) against 264 under `effort_breakdown` (blocking findings only). Both correct; the shared name was the contradiction. Same remedy as C1-5: the narrower becomes `blocking_affected_files`, both carry a `_basis`, the old key stays an alias, and the battery asserts the two diverge exactly when a finding is non-blocking |
59
59
  | C1-15 | **Analysis assumes a single destination.** Asked about a JDK move, `migrate-check` answers `headline_blocker: boot3_migration` and emits fix hints that send a developer to migrate namespaces they never planned to touch (481 `javax.persistence` under MIG-001). There is no way to say "JDK 11, keep Boot 2.7" | 3.2.1 (eval #4) | **High (product)** | **closed 3.6.0** — `--target-jdk N` and `--keep-boot` say which migration is being planned, filtered over the `migration_target` vocabulary every rule already carried rather than a parallel one. Measured on openmrs-core with `--target-jdk 11 --keep-boot`, the row's own acceptance: **MIG-001 85 → 0**, blocking 114 → 0, `headline_blocker` `hibernate_rewrite` → `none_applicable`, and the Hibernate 5→6 rewrite drops with it because Hibernate 6 arrives with Boot 3. Narrowing is not silencing: `metadata.route` publishes the target, the flag and every excluded axis, a `limitations` entry states how many findings were left out and that they are unresolved rather than fixed, and the route's own blockers stay (a JDK 11 route still reports MIG-021, asserted). The default is untouched, verified identical on three repositories |
60
- | C1-16 | **Four fan-in figures for one class, and the note that promises the relationship is itself false.** Field: `modernize.in_degree` 1 026 / `impact-chain.direct_callers` 611 / `explain.incoming_callers` 519 / `impact.stats.direct_caller_count` 484 for one service, plus 3 222 vs 3 458 endpoints affected. **Re-measured on the battery, 3.4.0, `Money` @ BroadleafCommerce: 673 / 1 868 / 151 / 418** — `impact-chain` is absent from the published relationship and is **2,8× larger than the figure the note declares "always the largest of the three"**, while `explain` vs `impact` differ by **177 %**, not the *"small margin"* the same note promises. In the same runs `impact.stats.endpoints_affected_count: 0` against a populated `endpoints_affected` from `impact-chain` — a **false zero** on the blast-radius axis | 3.2.1 (eval #5), reopened 5.5.4 | **High** — this is the number a risk decision is taken with: *"¿el blast radius son 484 o 611 clases?"* | **reopened 5.5.4.** The DI and subtype fixtures remain closed, but the claimed one-authority implementation was false for service locators: `impact` kept a local BFS that elevated `Locator#accessor` to every caller of `Locator`, while `impact-chain` used `caller_reach` and did not. 5.5.4 recovery removes that elevation and adds a two-accessor regression fixture; authority unification and cross-command parity remain open as Q-03. |
60
+ | C1-16 | **Four fan-in figures for one class, and the note that promises the relationship is itself false.** Field: `modernize.in_degree` 1 026 / `impact-chain.direct_callers` 611 / `explain.incoming_callers` 519 / `impact.stats.direct_caller_count` 484 for one service, plus 3 222 vs 3 458 endpoints affected. **Re-measured on the battery, 3.4.0, `Money` @ BroadleafCommerce: 673 / 1 868 / 151 / 418** — `impact-chain` is absent from the published relationship and is **2,8× larger than the figure the note declares "always the largest of the three"**, while `explain` vs `impact` differ by **177 %**, not the *"small margin"* the same note promises. In the same runs `impact.stats.endpoints_affected_count: 0` against a populated `endpoints_affected` from `impact-chain` — a **false zero** on the blast-radius axis | 3.2.1 (eval #5), reopened 5.5.4 | **High** — this is the number a risk decision is taken with: *"¿el blast radius son 484 o 611 clases?"* | **reopened 5.5.4.** The DI and subtype fixtures remain closed, but the claimed one-authority implementation was false for service locators: `impact` kept a local BFS that elevated `Locator#accessor` to every caller of `Locator`, while `impact-chain` used `caller_reach` and did not. 5.5.4 recovery removes that elevation and adds a two-accessor regression fixture; authority unification and cross-command parity remain open as Q-03. **Three further field witnesses, 2026-08-16, all on Q-03 and none needing a row of their own.** (a) `keycloak-config-cli` F-5: `KeycloakProvider` fan-in published as **10** (`impact.stats.direct_caller_count`), **12** (`explain.incoming_callers_count`) and **22** (`impact-chain.metadata.direct_caller_symbol_count`) in one session — every figure carries its declared unit and `caller_metric_note` reconciles them, but `impact-chain`'s *prose* prints the symbol count under the words "22 direct callers", contradicting its own metadata two lines below. The unit belongs in the sentence, not only in the field. (b) `keycloak-config-cli` F-4 → **closed `ea4d791`**: `impact` and `impact-chain` published 77.26 vs 74.19 and 79.81 vs 76.79 for the same symbols while `impact` shipped no `risk_model`, so the divergence could not be read off the payload; `impact` now publishes the basis it already shares. (c) BroadleafCommerce D7: `impact-chain` 24 direct callers / 227 indirect / 37 endpoints against `impact` 12 classes / 34 symbols / 249 / 38 for `ExploitProtectionService`, plus `project_summary: "41 transactional boundaries"` beside `transactional_boundaries.class_count: 29`. The evaluator's verdict is the one that matters commercially: *"un cliente que compare dos comandos verá una contradicción antes de leer la nota."* |
61
61
  | C1-17 | **A fact with an authority still contradicts itself across two commands**: `--compact.analysis_gaps` says *"1 test file for 3 336 Java files"* while `review-pr.test_coverage_risk.repository_has_test_sources` says `false`, in the same repository. C1-2 was closed in 3.2.1 with five consumers rebound, and this evaluation ran **on 3.2.1** | 3.2.1 (eval #5) | Medium | **does not reproduce (measured 3.6.0)** — re-measured as the row instructed. On openmrs-core, keycloak and spring-petclinic neither statement is emitted, and both consumers reach one authority: `--compact` through `analyze_test_sources`, `review-pr` through `is_test_path`. What was missing is now asserted: the authority has **two entry points** and nothing held them to each other, which is exactly the shape a sixth consumer would take. Zero mismatches across a case table including the C1-2 discriminator (a `test` package inside a *main* source root is production code), plus the literal contradiction — "N test files" and "no test sources" — asserted unreachable in both directions |
62
62
  | C1-18 | **The profile population differs per command, and the narrower one is what an agent reads.** Field: `--agent` reports a two-name profile set while `prepare-context fix-bug` reports four for the same repository in the same session — and the two names missing from the agent payload are the ones the *build* names for its deployable artefacts. Every finding about conditional security is keyed on that set, so the default agent surface answers the profile question with the population that lost the environments | 3.5.0 (eval #6) | **High** — it is the input to the one capability nothing else in the market has | **closed (unreleased)** — **fixture first, as C1-10 required**, and it reproduced worse than reported: on `tests/fixtures/spring_profile_population` the same repository in one session answered **`["qa","dev"]`** (default view — filenames + overlay directories), **`["dev","prod","staging"]`** (`posture` — config documents + `@Profile`) and **`["dev","prod","qa","staging"]`** (`--agent` / `fix-bug`, the union). Three populations under one name; every conditional-security finding is keyed on it. Authority `spring_profiles.profile_population`: four named signals (`file_naming`, `overlay_directory`, `config_documents`, `annotations`, plus `caller_supplied`), the union, and `by_signal` published so a name's provenance is auditable. The stack detector, `posture` and the serializer section all read it — asserted per surface, not claimed. Closing it exposed two false positives in the signals it inherited, both fixed structurally (never by name — VAI): an `application-{x}` file counts only **beside its base config** (alfresco's `alfresco/messages/application-model_de.properties` + 19 siblings invented twenty environments; now 0) and an overlay directory counts only when it **holds a config file** (spring-petclinic's `db/ messages/ static/ templates/` reported four; now `mysql, postgres`, which are its real profiles) |
63
63
  | C1-19 | **File-count drift across commands, with witnesses this time.** 3 337 (`compact.project_summary`) / 3 336 (`analysis_gaps.testing`) / 3 342 (`migrate-check.metadata.java_files_scanned`) for one repository in one session | 3.8.0 (eval #7) | Medium | **closed 3.9.0** — measured, not equalised: the three figures count code-extension files in any language, the non-test denominator of a test ratio, and the .java files handed to the migration rules. Each names its population now, and `migrate-check.files_scanned_basis` states the two it is not. Original note: open — **C1-14's cross-command half, reopened.** 3.6.0 recorded that half as *not reproducing* because on openmrs-core only `migrate-check` published a scanned-file count at all; eval #7 shows three commands publishing one on a repository we do not have. Three populations are plausible and each may be right in isolation (all Java files / files admitted to the graph / files the migration rules scanned), which is exactly the C1-5 remedy: name the unit per figure, or bind them to one authority. Do not fix by making the numbers equal — measure which population each counts first |
@@ -244,7 +244,7 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
244
244
  | C3-109 | **A field anchor is refreshed once and expires again on the next release, because nothing in this repository can re-measure it.** C3-97 built the mechanism and C3-100 completed it: every field figure carries the build and the cache state it was taken on, and `field_currency()` names the rows that are behind. The tenth round confirms the mechanism works and reports the hole it leaves: the `spring-audit` anchor reads *"8,4 s (field, warm cache, on 4.18.0)"* and the same command measured **40,8 s on 5.0.1** on the same machine, so the anchor was *"born expired"* — correct as history, useless as advice, and the reader has no in-house figure to fall back on. The evaluator's conclusion is the right one: *"arreglaron la epistemología del ancla pero no el proceso que la mantiene cierta"*. The anchors are the **field's** measurements on a private repository and a Windows box; this repository can never refresh them | 5.0.1 (eval #25) | **High** — the anchors drive `here:`, the budget warning and the `cache model` header, i.e. every place the product advises how to run something, and their half-life is one release | **closed (unreleased)** — the gate's cells are carried into the package by `scripts/sync_gate_anchors.py` and published beside the field figure: `gate:` in `cache model`, `gate_measurement` per command in the JSON, and `gate_currency()` naming the build that produced them and — when it is behind — the command that fixes it, which is the whole difference from a field anchor. Beside, never instead: a measurement at field scale outranks one at gate scale, and both print their size, cache state, host class and build. One authority (the baseline JSON); `tests/test_gate_anchor_currency.py` fails when the generated module and the committed cells disagree, so a refreshed capture that never reached the package is a red suite rather than a silent lie. The release step is written down in `docs/perf/REGRESSION-GATE.md` §7. Original plan: publish, beside every field anchor, the **release gate's own measurement of the build in hand**: the gate already measures a 2 985-file repository per release under a declared host class, and its cells are committed (`docs/perf/baselines/gate-latest`). One authority, no hand-written number, and the property that closes the row — an anchor nobody can re-measure is no longer the only figure a reader has **✅ the labelling half closed 5.1.0; the second half closed 5.2.0 with C3-110** — a figure that *advises* now says the build it was taken on **and** that this build has not been re-measured, and the anchors themselves were refreshed from evaluation #26. |
245
245
  | C3-110 | **The release performance gate has run nightly for six releases and has never blocked one, because nothing in the release path asks it anything.** F-AP shipped the detector, C3-98 gave it a baseline, C3-99 corrected its exclusion list and C3-108 taught it to measure cold as well as warm — and the eleventh round measures, on the same commit and the same machine, `validation` at **35,4 → 14,0 → 36,8 → 35,8 → 41,8 → 17,8 → 39,8 → 13,8 → 34,1 → 39,5 s** across eleven releases: four complete oscillations of ~2,9× on the cheapest command in the affected set. `risk` covers two orders of magnitude over the same series. Three separate holes, all of them ours: the gate's population is **six commands** (`ask`, `endpoints`, `spring-audit`, `migrate-check`, `validation`, `posture`) against the **eighteen** the field measures every round — `risk`, `verify-edit`, `delta`, `contract-diff`, `modernize`, `explain`, `repo-ir`, `data-exposure`, `pr-impact`, `impact-chain`, the root `--agent` view and the cold root build are all outside it; the workflow runs on `schedule` and `workflow_dispatch`, so no tag and no release commit is gated by it; and the baseline it compares against is `gate-latest`, which the previous release overwrote, so a release compares against *a* release rather than against **the best measured build** the product has ever had (4.18.0). The evaluator's framing is the one to keep, because it is the reason this row is critical rather than a performance chore: *"B10 no es deuda de rendimiento. Es el mecanismo que impide que las demás correcciones se mantengan ciertas"* — C3-109's anchor expired the release after it was closed for exactly this reason, and the two are one task | 5.1.0 (eval #26) | **Critical** — every other row in this class is a fix that can silently come undone, and eleven rounds of evidence say some of them do | open — the population becomes every command the field measures that takes only a path (`risk`, `modernize`, `explain`, `repo-ir`, `data-exposure`, the root `--agent` view, and `delta`/`contract-diff` against one ref pair), the gate runs on the release commit and not only at 03:00, and the comparison is against **two** baselines: the previous release (regression) and the best measured build (drift). The same run writes `gate_anchors.py`, which is C3-109's maintenance and not a second task **✅ closed 5.2.0** — `push: tags: v*` gates the release (the nightly stays, to catch a regression the day it lands); the population is every path-only command the field times, with `risk`, `modernize`, `repo-ir` and `data-exposure` added and every exclusion carrying its reason; `drift_report()` publishes the distance from `reference-best` without deciding, because a staircase of sub-threshold steps is invisible to a release-to-release comparison and unmissable here. Two defects found while measuring the gate: cold cells were not cold (only the snapshot layer was isolated, so every cold run used the previous cell's parse store) and a failed run was recorded as a fast one (`--no-cache` is rejected by most subcommands — `ask risk . --no-cache` exits 2 — and the harness timed the error envelope at ~0,29 s). Both fixed; a cell that did not run is not written, so the gate reads it as `missing` and fails. **The gate went red on its own first run over a release (v5.2.0), and not on a timing** — which is the row proving itself: the default JSON run of `migrate-check` on the gate repository **exits 1** with `OUTPUT_TOO_LARGE` (~269 K estimated tokens on 2 985 files), and the cell had sat in `gate-latest` since 4.13.0 at 8,72 s because the analysis does run and only the emit refuses; and `data-exposure` measured **0,28 s warm and cold**, the cost of its abstention (`answered: false`, no `dataLabels` in the gate repository) rather than of the command. `migrate-check` is measured with `--compact` — its own published remedy — and `data-exposure` is excluded with the reason. Both were invisible for as long as the harness ignored exit codes. First honest cold cells at gate scale, from the same run: `ask` 24,2 s cold / 0,36 s warm · `spring-audit` 16,9 / 3,2 · `validation` 15,9 / 8,0 · `posture` 16,2 / 2,6 · `risk` 31,1 / 17,7 · `repo-ir --summary-only` 8,4 / 4,3 · `modernize` 8,6 / 4,8. |
246
246
  | C3-111 | **`repo-ir`'s size ceiling guards stdout and nothing guards the file, and the advisory that would have warned is terminal-gated — so the run that actually produces 101 MB is the one that says nothing.** CL-26 answered eval #23's *"`repo-ir` has no cap and no warning"* with the stdout path, and that half is accurate: `OUTPUT_TOO_LARGE` refuses above ~50 K estimated tokens with four ways out in the hint. The half nobody read is `-o`: the write path has **no ceiling at all** (*"--output FILE skips the payload budget entirely"*), and the pre-build band (C3-18/C3-105) is emitted through `_notice`, which is silent off-TTY by contract (C3-26). A scripted or CI run — which is every run that passes `-o` — therefore gets **101 254 852 B, byte-identical across three releases, with no line anywhere**, into the panel titled *Context for AI agents*, whose consumer is the one that tolerates 101 MB worst. The product's own discipline is the contrast the evaluator draws: `endpoints --servlets` bounds its population and declares it, `--compact` publishes its budget and `direction: under_reports` | 5.1.0 (eval #26) | **Medium** | open — the ceiling applies to the answer, not to the pipe it goes through: above a declared byte estimate the `-o` run refuses **before it builds** with the same envelope and the same four ways out, and the advisory that survives the refusal travels in the payload rather than through a TTY-gated notice **✅ closed 5.2.0** — an estimate above 50 MB refuses **before the analysis**, through the error envelope (which a script reads and a TTY-gated notice does not), naming the band, its basis and the four ways out that already existed. The band has one derivation now, shared by the advisory and the guard, and the ceiling compares against its midpoint: the low end estimates the field's repository at 34 MB and would have let 101 MB through. Broadleaf refuses in 0,3 s; spring-petclinic writes 319 KB unchanged. |
247
- | C3-112 | **The clean-tree short-circuit is decided by every path git prints, so one untracked note beside the code buys a full repository-wide model — and the payload prints `changed_files: []`, so the reason is invisible.** C3-102 short-circuits on `working_cir_reused`, which `head_vs_working` sets from `git_answered and not all_changed`; `all_changed` is the tracked diff ∪ untracked adds over **all** paths, while the `changed_files` the verdict publishes is only its `.java` subset and `changed_build_files` only the admitted build/config subset. So an untracked `NOTES.md`, an audit output written into the repository, a `.sourcecode-cache/` directory — none of which any axis can read — flip `reused` to False, both sides are modelled, and the basis reads *"0 changed .java file(s) and 0 changed build file(s): HEAD and the working tree were both modelled and diffed"* without ever naming the path that forced the build. Measured at HEAD (5.2.0) on jobrunr (964 Java files), which carries exactly one untracked `.md` — with it present **3,74 s**, `model_built: true`; with it moved aside **0,31 s**, `model_built: false`: **12×**, one file, no code difference. Eval #27 is the same shape at 3 342 files (**68,3 s** to answer *"you have not edited anything"*), and its payload proves which path ran: it carries per-axis `detail` strings (*"no custom rules"*, *"no build or configuration file changed"*) that the short-circuit never emits, because it returns `AxisResult(False)` with an empty detail. Collateral, and the reason this outranks its cost: the release gate excludes `verify-edit` on the written premise that *"its clean-tree path builds no model at all since C3-102, so the figure a gate could take here would not be the figure a user pays"* — in the field's regime that premise is false, and the figure a user does pay is ungated | 5.2.0 (eval #27) | **High** — the one command whose value is bounded by its latency, defeated in the state every working copy is actually in; and a fix measured as closed in-house (9,94 → 0,62 s) that the field cannot reach is indistinguishable, from outside, from a fix that came undone | open — the condition must be the change set the axes can read (`.java` ∪ `_is_build_config_path`), not every path git prints: the two are already computed side by side in `head_vs_working`, so this is which tuple `reused` reads. The verdict then publishes the paths it saw and did not admit (`ignored_paths`: count, sample, and the rule that excluded them), because an answer that costs nothing must still say what it looked at — and once the clean-tree path is the one the field takes, `verify-edit` loses its gate exclusion and joins the gated population |
247
+ | C3-112 | **The clean-tree short-circuit is decided by every path git prints, so one untracked note beside the code buys a full repository-wide model — and the payload prints `changed_files: []`, so the reason is invisible.** C3-102 short-circuits on `working_cir_reused`, which `head_vs_working` sets from `git_answered and not all_changed`; `all_changed` is the tracked diff ∪ untracked adds over **all** paths, while the `changed_files` the verdict publishes is only its `.java` subset and `changed_build_files` only the admitted build/config subset. So an untracked `NOTES.md`, an audit output written into the repository, a `.sourcecode-cache/` directory — none of which any axis can read — flip `reused` to False, both sides are modelled, and the basis reads *"0 changed .java file(s) and 0 changed build file(s): HEAD and the working tree were both modelled and diffed"* without ever naming the path that forced the build. Measured at HEAD (5.2.0) on jobrunr (964 Java files), which carries exactly one untracked `.md` — with it present **3,74 s**, `model_built: true`; with it moved aside **0,31 s**, `model_built: false`: **12×**, one file, no code difference. Eval #27 is the same shape at 3 342 files (**68,3 s** to answer *"you have not edited anything"*), and its payload proves which path ran: it carries per-axis `detail` strings (*"no custom rules"*, *"no build or configuration file changed"*) that the short-circuit never emits, because it returns `AxisResult(False)` with an empty detail. Collateral, and the reason this outranks its cost: the release gate excludes `verify-edit` on the written premise that *"its clean-tree path builds no model at all since C3-102, so the figure a gate could take here would not be the figure a user pays"* — in the field's regime that premise is false, and the figure a user does pay is ungated | 5.2.0 (eval #27) | **High** — the one command whose value is bounded by its latency, defeated in the state every working copy is actually in; and a fix measured as closed in-house (9,94 → 0,62 s) that the field cannot reach is indistinguishable, from outside, from a fix that came undone | open — the condition must be the change set the axes can read (`.java` ∪ `_is_build_config_path`), not every path git prints: the two are already computed side by side in `head_vs_working`, so this is which tuple `reused` reads. The verdict then publishes the paths it saw and did not admit (`ignored_paths`: count, sample, and the rule that excluded them), because an answer that costs nothing must still say what it looked at — and once the clean-tree path is the one the field takes, `verify-edit` loses its gate exclusion and joins the gated population **✅ closed by B4 (5.7.0), verified 5.7.3 — this row was left open on a stale state.** B4 replaced the predicate with `_can_move_a_verdict`, which is exactly the change this remedy asked for, arrived at from the other side: rather than admitting only `.java` ∪ build/config, it disregards inert directories and a short inert suffix list and analyses everything else — conservative in the direction that costs time rather than truth. Re-measured on jobrunr, the row's own repository, with an untracked `.md` present: **0,44 s**, `model_built: false`, against the 3,74 s the row recorded at 5.2.0. The disclosure half is also there: `analysis.disregarded_changes` lists the paths, with a count, a basis sentence and the reason the model was not built. Nothing was changed for this closure — it is a measurement |
248
248
  | C3-113 | **The release gate ships armed and its two baselines are empty or stale, so the release it gates is compared against 5.0.0's six warm cells and its distance from the best build is never computed.** C3-110 closed the mechanism — `push: tags: v*`, a widened population (`risk`, `modernize`, `repo-ir --summary-only`, cold cells, every exclusion with its reason), `drift_report()` against `reference-best` — and the artefacts it decides from were not promoted with it. At HEAD: `docs/perf/baselines/gate-latest/` holds **six** `warm` cells, all stamped `tool_version: 5.0.0` (`ask`, `endpoints`, `migrate-check`, `posture`, `spring-audit`, `validation`) — the pre-C3-110 population, no `risk`, no `modernize`, no `repo-ir`, no cold cell; `docs/perf/baselines/reference-best/` holds **no cells at all**, only its README, so the drift half prints its honest absence every run and 4.18.0 — the build the field still calls the best measured — is a number nothing compares against; and `gate_anchors.py`, generated from `gate-latest`, carries `GATE_MEASURED_VERSION = '5.0.0'` into a 5.2.0 build, which makes `gate_currency()` non-current and suppresses the one sentence in `cache model` that would have told a reader what the gate measured on the build in hand. A cell present in the current run and absent from the baseline is *recorded, not compared* (only the reverse fails), so the widened population is measured and ungated until a capture is promoted. This is the exact residue of the field's B10 — *"cero regresiones, pero sigo sin evidencia de que exista el gate"* — and the residue is real rather than a reporting gap | 5.2.0 (eval #27) | **High** — C3-110's argument applies to C3-110: a gate whose baseline is two releases old and whose drift reference is empty cannot hold the line it was built to hold, and the mechanism being correct is invisible to anyone outside this repository | open — promote the v5.2.0 tag run's cells (warm **and** cold, full population) to `gate-latest` and re-run `scripts/sync_gate_anchors.py`, so the anchors a build publishes are the build's own; capture `reference-best` once from the reference build in the gate's host class (`gh workflow run perf-gate.yml --ref v4.18.0`, cells committed) so drift has a floor to measure from — the field's *"congelad esta versión como línea base"* is the same request, and 5.2.0 is now the better candidate on their own table; and publish the verdict where a consumer reads it (the gate stanza in `cache model`, and the release notes), because *"the gate ran and was green"* is a fact about the release, not about this repository |
249
249
  | C3-114 | **Two runs of `endpoints` over one unchanged tree returned two different security censuses, and the disagreement is stable on each side.** Run #12 and one run taken directly after `cache clear -y --all` + warm published `custom_gate_inferred: 939 / none_detected: 2635`; eight later runs — including `--no-cache`, `--jobs 1` and `--jobs 4` — published `935 / 2635 / programmatic: 4`. The four that moved are `NominaRestController#{obtenerNominasEjercicio, NominaActiva, cerrarNomina, estado, desbloquearProcesos}`, verified in source at `NominaRestController.java:125-158` to carry a literal `@M3FiltroSeguridad`, with zero programmatic security calls anywhere in the file — so the **939 answer is the correct one** and the stable, reproducible 935+4 is wrong. The trigger was irreproducible state: the shared parse cache held 13 368 entries the build reports as *"written by another build … can never be read again"*, which the first run retired. Two facts make it a row rather than an anecdote: a security verdict differed between two runs of one command on one tree, and the `programmatic` fallback — a whole-file scan whose result is memoised per `declaring_file` — is the only classifier in the path that can produce exactly this pair | 5.3.1 (audit #28) | **High** — determinism is an eleven-round property of this product and the axis that moved is the security verdict | **closed 5.4.0** — three causes, all in the fallback. The read was unrooted (`declaring_file` is repository-relative and was resolved against the process working directory, so the verdict depended on `cd`); the scan could not tell code from prose (`hasRole(` inside `@PreAuthorize("hasRole('ADMIN')")` — a declarative gate on another method read as runtime enforcement of this one, now blanked through the E-1/E-2 authority); and a whole-file regex preempted a per-method gate on both surfaces. An unreadable file now abstains and is cached as `None`, which is not the same entry as "read it, found none". Original triage: **re-measure after E-3 and only then investigate separately**: E-3 is the same subsystem and the same four handlers, and its fix changes which member the policy is read from. If it survives, the suspect is named: `_prog_sec_cache` is keyed on the file and populated from a `Path.read_text` inside a bare `except`, so a read that fails once poisons the answer for every handler in that file for the rest of the run |
250
250
  | C3-115 | **`prepare-context generate-tests` publishes a `test_gaps` key whose population nothing declares, on a repository with 3 342 ranked gaps.** The command's `--all`/`--include-config` help documents the key; the payload emits it empty on a repository where `test_gap_ranking.population.test_files` is `0` and 3 342 sources rank as gaps. The degradation itself is honest — the budget block states in words that empty is not the same as *nothing found* — which is exactly why the second key is the defect: it is the one field in the payload with no declared population, beside a ranking that has one | 5.3.1 (audit #28) | Low-Medium | **closed 5.4.0** — present whenever the ranking ran, with `untested_sources_unit` naming what it counted over and pointing at `test_gap_ranking.population` as the authority. Original remedy note: declare the population `test_gaps` counts over, or delete it in favour of `test_gap_ranking.population.test_files`, which is already the authority. One fact, one authority (C1) |
@@ -355,6 +355,11 @@ contributes nothing to the graph looks exactly like a file with nothing in it.*
355
355
  it, which is what audit #28 built. The class keeps its name and loses its claim about
356
356
  provenance: E-3 was found from outside, on the surface where silence is loudest.
357
357
 
358
+ As of 5.7.3 the parenthetical is not merely falsified but inverted: **E-18 through E-33
359
+ were all found in the field**, by two adversarial evaluations run four hours apart
360
+ (`adorsys/keycloak-config-cli`, 227 files; BroadleafCommerce, 2 985). Silence is now the
361
+ class's subject, not its provenance.
362
+
358
363
  | ID | Defect | Found | Severity | Status |
359
364
  |---|---|---|---|---|
360
365
  | 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** |
@@ -366,13 +371,32 @@ provenance: E-3 was found from outside, on the surface where silence is loudest.
366
371
  | E-7 | **A security-policy classifier regex matches a method's own declaration text, not a call site, and the resulting false flag is cached per-file and leaks onto a handler explicitly marked bypass-all.** On sa-token (a non-Spring-Security Java auth framework with its own `@SaCheckLogin`/`@SaCheckPermission`/`@SaIgnore` annotations), `ask endpoints` labels `AtCheckController`'s handlers `policy: "programmatic"` although no `sourcecode.config.json` custom-security declaration exists (`ask config` confirms "Declaration: none"). The only evidence found is a regex matching the literal text `checkPermission(` in the file — which matches the demo's own method **declaration**, `public SaResult checkPermission()`, not a call to a security check. Reported at `repository_ir.py:394-408`/`:5064-5076` (field-agent-located; not independently re-read this session, one level less verified than E-5/E-6/C1-47/C1-48). The flag, once set, is applied to every handler in the file lacking its own recognized policy — reaching `ignore()`, a handler annotated `@SaIgnore` (sa-token's explicit "skip all checks" marker), which is reported as `programmatic`-protected, the opposite of what the annotation on it says. Contamination reproduces in `impact-chain`'s `endpoints_affected` for the same controller | 5.7.0 (golden-repo battery #2 2026-08-15) | **High** — this is an auth-verdict false positive on the exact axis `ask risk`'s own documentation says its composition depends on (`severity_effective = ... × auth_verdict × ...`); a wrongly-`programmatic` handler would suppress severity on a genuinely open endpoint | **closed 5.7.1** — **re-verified at source first, as this row required**, and the field report was exact. `_PROGRAMMATIC_SECURITY_RE` matched `\b(?:…|checkPermission|…)\s*\(`, which is the shape of a *call* and equally the shape of the *method that declares one*. On `AtCheckController` the regex finds **one** match in the entire file — line 46, `public SaResult checkPermission() {` — and on that evidence all **seven** handlers were published `policy: programmatic`, `ignore()` among them, which carries `@SaIgnore`, sa-token's explicit skip-all-checks marker: the verdict stated the opposite of the annotation on the method. Repo-wide the false population was **23 endpoints (programmatic 41 → 18)**, and every one of the 15 surviving files was re-read and carries a real call site (`StpUtil.checkPermission("")`, `stpLogic.hasRole(role)`, `SaRouter.match(…)`). The discriminator is what precedes the name on its own line: a call is led by a receiver, an operator, a bracket, a statement boundary or one of `return`/`new`/`throw`/…; a declaration is led by its return type — an identifier, a closing generic, or a closing array bracket. ⚠ **The first attempt introduced a false negative and the cross-repository A/B is what caught it**, not the suite: applied to the whole pattern the filter also ran on the receiver alternatives, and `ReactiveSecurityContextHolder.getContext()` reads as identifier-then-match — exactly what a declaration looks like — so spring-security-samples' reactive `MeController` silently lost a correct verdict (that repository ships **two** `example.MeController` classes, which is how it surfaced). The pattern is now split: the ambiguous `name(` half is filtered, the unambiguous receiver/member/`throw` half never is, and that negative control is the sixth assertion in the battery. **Measured across all 23 golden repositories: 22 of 23 policy censuses byte-identical**, sa-token the only mover — BroadleafCommerce (12), keycloak (147) and spring-security-samples (3) unchanged. 6 assertions, **3 red on the previous build**, the 3 green ones being the negative controls. Suite 8 761. ⚠ Residue this row does not close: sa-token's handlers now read `none_detected`, which is honest but incomplete — `@SaCheckPermission` is a vendor annotation this analyzer models only when declared in `sourcecode.config.json`, and that is the documented custom-security path, not a new gap |
367
372
  | E-8 | **`impact-chain` silently merges unrelated same-named classes from different modules into one answer; `impact` on the identical symbol correctly refuses.** tutorials (Baeldung's mega-repo, hundreds of independent modules) has 4 distinct classes named `PersonService` in unrelated modules. `ask impact PersonService <repo>` returns `resolution: "ambiguous"` and lists all 4 `matched_fqns`. `ask impact-chain PersonService <repo>` returns `resolution: "partial"`, but its `direct_callers` mix classes from two different, unrelated modules (`com.baeldung.activej.*` and `com.baeldung.hibernatejfr.*`) into one merged answer and a single risk score (25.0, `critical`), with no field disclosing which of the 4 classes contributed which caller. The command that should have said "ambiguous" said "partial" and guessed | 5.7.0 (golden-repo battery #2 2026-08-15) | Medium-High — only demonstrated on a repository large and diverse enough to carry real name collisions across modules (tutorials, 24 074 java files — the largest repository this battery has run against); a confident composite risk score built from an admittedly-ambiguous symbol resolution is the opposite of `impact`'s own behavior on the same input | **closed 5.7.2** — on C1-41's precedent rather than a second one of its own. The facts stay (every caller listed is a real caller of *some* candidate, and dropping them would hide reach the reader asked about); the figure that reads as *this symbol's risk* is void — `risk_score` and `risk_score_raw` null, `risk_level` `unknown` — with the reason published beside the null in `risk_score_basis`. The candidates are named in `metadata.matched_classes` / `matched_classes_count`, and both a warning and the explanation say so, because an agent quotes the explanation and a script reads the metadata. `impact-chain` is `core` tier, so no published enum value is invented: `resolution` stays `partial` and `risk_level` uses `unknown`, which its schema already documents, and every new key appears only on an ambiguous match — an unambiguous answer is byte-identical. Measured on tutorials: 4 candidates named where the payload named none, `high`/25.0 replaced by `unknown`/null, the six direct callers still listed. Regression: `tests/test_ambiguous_chain_e8.py`, 8 assertions |
368
373
  | E-9 | **An unresolved JAX-RS path's caveat is dropped between `endpoints` and `impact-chain`, and the path's embedded regex syntax collides with the `endpoint_id` field separator.** killbill's `AccountResource` declares `@Path("/{accountId:" + UUID_PATTERN + "}")` (string-concatenated). `ask endpoints` correctly marks it `path_resolution: "unresolved"`. `ask impact-chain AccountResource <repo>` shows the same route without that caveat and truncates the path to `"/{accountId:"`, and because the embedded `:` collides with the `endpoint_id` separator the emitted id is malformed: `DELETE:/{accountId::org.killbill.billing.jaxrs.resources.AccountResource:...#closeAccount` | 5.7.0 (golden-repo battery #2 2026-08-15) | Low | open |
369
- | E-10 | **Malformed Java is silently absorbed into the symbol export with full confidence, and the parse failure is not named anywhere in the payload.** spaghetti-api's `BrokenSyntax.java` has 2 open braces and 0 close braces — invalid Java, confirmed by brace count and by reading the file. The root command's `contracts` export nonetheless reports it with a clean `class BrokenSyntax` export plus a `method` export (signature `()->void`), no `parse_error`, absent from `analysis_gaps` — and it never surfaces in `spring-audit`, `migrate-check` or `risk` output either, no "N files failed to parse" anywhere in any of them | 5.7.0 (golden-repo battery #1 2026-08-14) | Medium — a file that cannot be Java is exported as if it parsed cleanly, on the exact axis this repository's own CLAUDE.md is most explicit about ("never a confident falsehood... unknown, never 0") | open |
370
- | E-11 | **`impact`'s DI-interface caller resolution fabricates direct callers that never reference the target, inflating the risk band; `impact-chain` on the identical symbol does not.** On examples, `ask impact OrdersService` reports `critical` (score 35.5, 4 direct + 35 indirect callers); `ask impact-chain OrdersService` reports `medium` (score 11.0, 1 direct caller) for the same repository state. The extra "direct callers" `impact` adds — `MicroserviceUtils` and its two nested classes — never reference `OrdersService` anywhere (grepped, 0 hits); they only import the shared `Service` interface `OrdersService` implements. `impact`'s own explanation names the mechanism: `"callers resolved via interface (Service) — Spring/CDI/Guice DI pattern"` — the resolver treats any class touching a common interface as a caller of every implementer of it. This compounds C1-48 (the two commands already score from different formulas) with a caller **set** that disagrees on its own, independent of which formula is applied to it | 5.7.0 (golden-repo battery #1 2026-08-14) | Medium-High — same family as C1-21 (a fabricated identity reported as protection): here a fabricated call edge is reported as reach, on the exact factor `ask risk`'s reachability axis depends on | open |
374
+ | E-10 | **Malformed Java is silently absorbed into the symbol export with full confidence, and the parse failure is not named anywhere in the payload.** spaghetti-api's `BrokenSyntax.java` has 2 open braces and 0 close braces — invalid Java, confirmed by brace count and by reading the file. The root command's `contracts` export nonetheless reports it with a clean `class BrokenSyntax` export plus a `method` export (signature `()->void`), no `parse_error`, absent from `analysis_gaps` — and it never surfaces in `spring-audit`, `migrate-check` or `risk` output either, no "N files failed to parse" anywhere in any of them | 5.7.0 (golden-repo battery #1 2026-08-14) | Medium — a file that cannot be Java is exported as if it parsed cleanly, on the exact axis this repository's own CLAUDE.md is most explicit about ("never a confident falsehood... unknown, never 0") | **closed 5.7.3** — the recovery stays (a file that fails to compile still tells us its type exists); what changes is that it is qualified. A `parse_structure` gap names every file whose braces do not balance **once comments and strings are masked**, and states what is missing from the model: the members after the unclosed block and the type's own end. Distinct from `parse_coverage`, which fires on *total* extraction absence — this one fires when extraction succeeded over source that cannot be complete, and both readings of "this file was not fully read" now live in `reconciliation`. Cost is a `str.count` per file; the mask, which is not cheap, runs only for the rare file that already looks unbalanced, so a brace inside a string or a comment is not a false positive (asserted). Measured on spaghetti-api: `BrokenSyntax.java` named, its symbols retained. ⚠ Residue: the gap reaches every CIR consumer through `analysis_gaps`, but the root command builds its own list from `ConfidenceAnalyzer`, so `ask <repo> --compact` still does not carry it. Regression: `tests/test_parse_structure_gap_e10.py`, 7 assertions |
375
+ | E-11 | **`impact`'s DI-interface caller resolution fabricates direct callers that never reference the target, inflating the risk band; `impact-chain` on the identical symbol does not.** On examples, `ask impact OrdersService` reports `critical` (score 35.5, 4 direct + 35 indirect callers); `ask impact-chain OrdersService` reports `medium` (score 11.0, 1 direct caller) for the same repository state. The extra "direct callers" `impact` adds — `MicroserviceUtils` and its two nested classes — never reference `OrdersService` anywhere (grepped, 0 hits); they only import the shared `Service` interface `OrdersService` implements. `impact`'s own explanation names the mechanism: `"callers resolved via interface (Service) — Spring/CDI/Guice DI pattern"` — the resolver treats any class touching a common interface as a caller of every implementer of it. This compounds C1-48 (the two commands already score from different formulas) with a caller **set** that disagrees on its own, independent of which formula is applied to it | 5.7.0 (golden-repo battery #1 2026-08-14) | Medium-High — same family as C1-21 (a fabricated identity reported as protection): here a fabricated call edge is reported as reach, on the exact factor `ask risk`'s reachability axis depends on | **closed 5.7.3** — the DI recovery is right and stays: a container binds implementations to interfaces, and a walk that stops at the interface loses every real production dependent (C1-16). What was wrong is the name it was filed under. A class that reaches the target only if the container binds *this* implementation sits at the same conditional distance as a transitive caller, not at the distance of a class that names the target. `interface_mediated_callers` / `interface_mediated_caller_count` publish the population, `via_interface_note` says what it is and is not, the explanation carries both figures, and they are scored on the **indirect** axis (0,5) rather than the direct one (2,0). Reach is untouched — they remain BFS seeds and members of the blast cone, so endpoints, transactions, mappers and cross-module reach are unchanged. Measured across 8 symbols on 7 repositories: **7 byte-identical**, including BroadleafCommerce `Money` (134 direct, 1 584 indirect) and keycloak `UserResource`; `examples/OrdersService` moves 4 → 1 direct, 35 → 38 indirect, 35,5 → 31,0, `endpoints_affected` unchanged at 3. Regression: `tests/test_interface_mediated_callers_e11.py`, 7 assertions |
371
376
  | E-12 | **Spring bean detection matches an annotation's *simple name* with no check that it is Spring's, so a repository that declares its own `@Service` is published `spring_detected: true`.** `spring_model.py:41-46` (`_BEAN_ANNOTATIONS`) holds bare spellings — `@Component`, `@Service`, `@Repository`, `@Controller`, `@RestController`, `@Configuration`, `@Bean` — and `BeanGraph.build` (`:184-202`) admits a node whose `annotations` list intersects that set. The list carries simple names only, so the match is on the token, not on what it resolves to. `spring_detected` (`spring_model.py:455`, the F-AY single authority) is then `has_spring_beans() or tx_total > 0`, and the bean half decides alone. **Measured on neo4j**: `grep -r org.springframework` over the whole tree returns **0** occurrences and the graph holds **0** edges mentioning it, while `import org.neo4j.annotations.service.Service` appears **199** times — neo4j's own SPI marker, declared at `annotations/src/main/java/org/neo4j/annotations/service/Service.java`. `BeanGraph.build` over the CLI's own CIR (5 598 files) returns **34 beans, every one stereotype `service`**, all of them that annotation; `tx_index.stats()["total"]` is **0**. So `ask spring-audit neo4j` publishes `spring_detected: true` in the same payload as `tx_stats: {total: 0}`, under both `--scope security` and `--scope tx`. The discriminating evidence is **already in the graph and already walked**: pass 1 of the same function iterates annotation-type nodes to build the meta-annotation map, and one of those nodes is `org.neo4j.annotations.service.Service` — a repository that declares the annotation itself is the witness that the token is not Spring's. Negative control: killbill (0 `org.springframework`, 0 bare bean annotations) correctly publishes `spring_detected: false`. **Not observed**: no false finding fires on neo4j today (`total_findings: 0`), so this row is against the published fact and what reads it, not against a finding — but `has_spring_beans()` is also the AOP-premise witness (`spring_security_audit.py:638-642`), the one C2-era fix installed so a self-invocation finding cannot fire where nothing proxies, and on this repository that premise is satisfied by a non-Spring annotation. **Propagates to every surface that reads the authority**: `spring-audit` and `spring-audit --scope tx` (`spring_tx_analyzer.py:1039`), `posture` (`posture.py:1207`), `risk` (`risk.py:1593`), `ris.py:355`, and the MCP orchestrator's `repo_type: "java_spring"` (`mcp/orchestrator.py:360`). ⚠ Unquantified exposure: sa-token (134) and dubbo (164) also carry bare `@Service`/`@Component` spellings — dubbo does use Spring, so its verdict may be right for the wrong reason; neither was measured this round | 5.7.0 (split out of E-5 while closing it, 2026-08-15) | **High** — this is the vendor-agnostic rule this repository enforces, inverted: the logic branches on a proprietary name and treats it as the rule rather than as evidence, so the blast radius is every repository that spells an annotation the way Spring does, and the field it decides is the one that says whether the Spring axes are measuring this repository at all | **closed 5.7.1** — the annotation is **resolved**, not merely spelled. `BeanGraph.build` walks the `imports` edges the graph already carries (109 475 of them on neo4j, indexed by the same class FQN the bean nodes use) and asks where the spelling binds, in the order the compiler would: an explicit single-type import **decides it either way**; a wildcard import of a Spring package leaves Spring reachable, so the spelling stays admissible; failing that, an annotation this repository declares in the owner's own package is the owner's own; and **nothing resolved stays `None`** — an absent import edge is not evidence, so the node keeps exactly the behaviour it had rather than acquiring a verdict from silence. The node is not deleted: `BeanNode.spring` records the verdict and `annotation_fqn` records what it bound to, so `get_stereotype` still answers for `explain` and only the **Spring** claim is withdrawn — `has_spring_beans()` is what filters. A meta-annotation is decided by what it *carries*, not by its own name: `@DomainService` is repository-declared by definition, so resolving its own spelling would have rejected every meta-bean the codebase deliberately supports — the question asked is where the `@Service` **on it** binds. The two annotation passes were split so the same-package map is complete before any resolution reads it, and the import walk shares the edge traversal that was already being made for the injection edges. **Measured**: neo4j 34 beans → **0 Spring** (all 34 bind to `org.neo4j.annotations.service.Service`), `spring_detected` **true → false**, and the `stack_fit` block — the honesty affordance that tells the reader to read the Spring axes as `unknown` rather than `none` — is **now published on that repository, where it was suppressed before** (verified by stashing the fix and re-running). Two more repos were silently wrong and are now right: sa-token had **35** beans bound to Solon (`org.noear.solon.annotation.*`) and loveqq, jobrunr **1** bound to Micronaut (`io.micronaut.http.annotation.Controller`) — both repositories still read `spring_detected: true` on their genuine Spring beans, so the correction shows up in the bean census rather than the headline. **Negative controls, unchanged**: spring-petclinic (12 Spring beans, 9 correctly withheld as `jakarta.persistence.*` entities the stereotype rule already excluded), dubbo 133/133, mall 158/158, spring-boot-admin 199/199, sagan 76 with 12 JPA withheld, killbill and eureka still `false`. 9 assertions, **7 red on the previous build**. Suite 8 360, the same 21 previous reds. **Re-measured on the full golden set before the bump**: in the 23-repository A/B battery neo4j's `spring_detected` is the only headline that moved, and the two independent authorities now **agree on all five repositories checked** — `migrate-check`'s `spring_present` and `spring-audit`'s `spring_detected` read false/false on neo4j, killbill and eureka, true/true on examples and spring-petclinic; before this fix neo4j was the one disagreement. Cost: `BeanGraph.build` goes from 7,2 → 17,7 ms on skywalking, 28,2 → 58,8 ms on keycloak and 41,7 → 82,4 ms on neo4j (284 338 edges), best-of-3 — the import walk roughly doubles a step that is tens of milliseconds inside a multi-second command. The three apparent `spring-audit` slowdowns in the battery log (skywalking +259 %, open-banking-gateway +200 %, mall +162 %) were **contention noise, not this fix**: re-timed in isolation they are 2,5 s / 1,2 s / 1,1 s against baselines of 2,7 s / 1,3 s / 1,3 s. ⚠ Residue this row does not close: `@Bean`/`@Configuration` on a class with no import edge for the name still falls to the unresolved branch (137 of spring-boot-admin's 199, 87 of dubbo's 133) — admitted, which is the direction that preserves behaviour but is not a resolution; and `next(iter(match))` still picks arbitrarily when a node carries two bean annotations |
372
377
  | E-13 | **No field in any repository carries a `contained_in` edge, so `ask verify` — the `core`-tier CI gate — returns `pass` on a repository that violates its own declared contract.** `repository_ir.py:3041-3052` emits `contained_in` for `sym.type in ("method", "field")`, and the owner is computed by `_enclosing_class` (`:3833`), which splits on `#` only. A method is spelled `Type#member` and resolves; a **field is spelled `Type.member`** and is handed back unchanged, so the `enclosing != sym.symbol` guard on the next line drops it. Measured on four repositories: **0 of 14** fields on spring-petclinic, **0 of 821** on mall, **0 of 1 086** on neo4j, **0 of 2 188** on keycloak. The consequence is a silent gate. `verify_rules.ForbiddenEdgeRule._endpoint_matches` is correct by design — it matches a selector against the edge endpoint **or the type that declares it**, resolved through `_owner_type_map`, precisely because *"edges are recorded at member granularity but client invariants are stated at type granularity"* — and it starves: for the shape `@RestController class Ctrl { @Autowired OrderDaoJpa dao; }` the graph holds `injects com.example.Ctrl.dao → com.example.OrderDaoJpa` and the `@RestController` lives on `Ctrl`, which the matcher can never reach. On mall, **176 of 181** `injects` edges originate at a field and are orphaned from their class this way. **How it survived is the second half of this row**: a test named `test_field_has_contained_in_edge` existed for exactly this property and asserted nothing — its filter (`"." in e["from"].split(".")[-1]`) cannot match, because the last dot-segment of an FQN never contains a dot, and `assert len(field_edges) >= 0` is true of every list. It passed green for as long as the defect existed | 5.7.0 (triage of the 21 standing red tests, 2026-08-15) | **Critical** — a gate that cannot fail is worse than no gate: a team switches CI on, sees green, and concludes the contract holds. `ask verify` is `core` tier, which this repository defines as *"safe to gate CI on"* | **closed 5.7.1** — one condition at the emission site. The dot is ambiguous with a package segment, so the last segment is stripped **only when what remains is a type this repository declares** — membership in `_local_classes` is the evidence, never the spelling, and `_enclosing_class` itself is untouched because its `#` behaviour is right for every other caller. **11 of the 21 standing red tests went green with this one edit** (`test_verify_repo.py` ×9, `test_contracts_file.py`, `test_verify_edit_v5.py::TestForbiddenEdge`) — they were never stale tests, they were an unread bug report. Collateral measured and nil: the 23-repository battery is **69 of 69 pairs byte-identical** across `endpoints`/`spring-audit`/`posture`, and `impact` — the one consumer that walks `contained_in` — returns identical band, score, caller and endpoint counts on spring-petclinic (×2), mall and sagan. Two assertions replace the vacuous one: the witness edge, and the general property that **no** member symbol is left without an owner. Suite 8 756 (21 → 10 reds). ⚠ Residue, deliberately not fixed here and filed as its own row: `BeanGraph.injections` is empty on all four repositories measured, because it keys on `frm in beans` where beans are class FQNs and the edges originate at fields and constructors. It has **no consumer in the codebase** — fixing dead structure to move a score is not a fix |
373
378
  | E-14 | **A 3-level Python re-export chain resolves through a documented 2-level limit, and nothing is reported.** `test_reexport_chain_limit`'s own docstring states the contract — *"El tercer nivel NO debe resolverse (chain limit = 2). limitations debe contener algun indicador del limite alcanzado"*. Measured on its fixture (`a/__init__.py` → `b/__init__.py` → `c/module.py`, consumed from the root): the `SymbolLink` for `deep_func` resolves to **`a/b/c/module.py`** with `is_external: false`, and `summary.limitations` carries only `namespace_package:a/b/c` — no chain indicator. Either the limit is not enforced or it is enforced and unreported; both are the shape this ledger calls a confident answer past the edge of what was measured. **Found the same way as E-13 and by the same defect in the suite**: the test ended in `assert ... or True`, which accepted every outcome including this one | 5.7.0 (vacuous-assertion sweep, 2026-08-15) | Low-Medium — Python import resolution is not the Java/Spring core this product is bought for, and no headline field is built on it; the row exists because the property is declared in the code and is not held | open — **tracked, not forgotten**: `test_reexport_chain_beyond_the_limit_is_reported` asserts the intended property under `xfail(strict=True)`, so the day it is fixed the suite turns red and forces this row closed rather than letting the fix land unnoticed |
374
379
  | E-15 | **No repository has ever published a `field_type`: the class-scope type-reference surface keys its output by the field's own FQN, so the first context named in its own acceptance claim is empty everywhere.** Same root as E-13, second consumer, found by triaging the reds E-13 left standing. `repository_ir.py:2381` (`_build_class_type_refs`) does `cls = _enclosing_class(s.symbol)` for a field, and that resolver split on `#` only — so a field indexed under `com.x.Svc.repo` instead of `com.x.Svc`, and `class_type_surface` querying by class FQN found nothing. Measured on the surface's own fixture (`@Autowired private OrderRepository repo;` plus `private java.util.Map<CustomerId, Order> ledger;`): the published contexts are `param_type`, `ctor_param_type`, `return_type` — and **`field_type` and `field_type_arg` are absent entirely**. On real repositories, spring-petclinic published `{ctor_param_type: 3, param_type: 24, return_type: 18}` and sagan `{ctor_param_type: 59, param_type: 103, return_type: 95}`, both with **zero** field contexts. The module's docstring states the claim this falsifies — *"the class's declaration structure — field, field generic arg, constructor param, method param, method return — is recovered as a pure ContextGraph query"* — and the field is named first | 5.7.0 (triage of the reds left standing by E-13, 2026-08-15) | **High** — a shipped acceptance claim for a Semantic IR capability, false in two of its five named contexts, on the two contexts that describe what a class *holds* rather than what it passes | **closed 5.7.1** — E-13's patch was at one emission site; a second consumer with the same root made that the wrong shape, so the resolution is now **one authority**, `_owner_type_of(symbol, symbol_kind)`, and both sites read it. The kind is the evidence: a field symbol is built as `f"{class_fqn}.{fname}"` at the single site that makes one, so the last dot-segment is the member and nothing is inferred from spelling; a caller that does not know the kind gets the `#` behaviour alone, because a bare dotted FQN is ambiguous with a package segment and this function may not guess. `_enclosing_class` survives as a kind-less delegate so its ten existing method-side callers are untouched. Measured after: spring-petclinic gains `field_type: 6, field_type_arg: 3` and sagan `field_type: 22, field_type_arg: 10`, with the other three contexts **byte-identical** — recovered, not double-counted. Collateral nil on the 23-repository battery (**69 of 69 pairs identical**) and on `impact` (4 symbols, 3 repositories, identical band/score/callers/endpoints). **21 standing red tests are now 7**, and 14 of the 21 were this one root |
375
-
380
+ | E-16 | **A second, hand-written test-source filter stands beside the authority and removes production code from the model — the exact substring shape C1-2 closed in 3.2.1.** `find_java_files` asks `is_test_source_file`, which knows that a Java package named `test` under a *declared main source root* is production. Three command entry points then asked again as `[f for f in find_java_files(root) if "/test/" not in f and "/tests/" not in f]` (`repo-ir`, `impact`, `export`), and three more populations did the same by hand (`serializer._mybatis_pairing`, `serializer._bootstrap_structured`, `detectors/java._collect_transactional_classes`). Measured over the 24 golden repositories the second filter removed production sources from four of them — **sa-token 82 of 919 files (8,9 %)**, ofbiz-framework 41 of 1 075 (3,8 %), BroadleafCommerce 4, tutorials 1 — and removed them *silently*: `analysis_gaps` was empty and no payload named the loss. What it cost, on sa-token: `repo-ir` read **837 of 919 files**, published **5 807 symbols** and **154 endpoint symbols**; `ask impact com.pj.test.TestController` — a fully qualified name for a class that is on disk — answered `resolution: ambiguous` over `com.lym.controller.TestController` and `com.pj.controller.TestController`, published `risk_score: 26.0, risk_level: high` for that substitution, and reported `endpoints_affected_count: 8` against `impact-chain`'s **53** for the same symbol. Second defect in the same lines: `--include-tests` was a **no-op** on `repo-ir` and `impact`, because the flag only skipped the redundant pass while `find_java_files` was still called with its default `include_tests=False` | 5.7.3 (cross-command coherence battery, 2026-08-16) | **High** — a repository that names a package `test` is analysed with 9 % of its production classes missing from every CIR consumer, and the blast radius of a symbol is 6,6× smaller than the same product's other answer for it | **closed — on `master`, unreleased.** One authority at all six sites: the three CLI populations read `find_java_files(root, include_tests=include_tests)` and nothing else, the three list filters read `path_filters.is_test_path`. Measured after on sa-token: `repo-ir` **919 files, 6 248 symbols, 381 endpoint symbols, 902 classes** (was 837 / 5 807 / 154 / 820); `impact com.pj.test.TestController` → `resolution: exact`, one matched FQN, **53 endpoints on both commands** — the C1-16 endpoints-affected divergence on this repository closes with it. `--include-tests` now widens the population it names. Collateral nil: the coherence battery over spring-petclinic, spring-boot-admin, sagan, eureka, examples, spaghetti-api and jobrunr returns identical endpoint totals and identical `impact` scores. Regression: `tests/test_one_test_source_authority_e16.py`, 10 assertions — including one that greps `src/` for the substring shape, because the defect is a shape and it has now grown back twice |
381
+ | E-17 | **A fully qualified target that is absent from the model is answered by every other class that shares its class name, with a confident band.** `_resolve_target` reduced every target to its last dotted segment before matching, so `ask impact com.acme.nonexistent.TestController` on sa-token — a package that exists in no repository on disk — returned `resolution: ambiguous`, `matched_fqns` naming **four classes across three unrelated packages**, and `risk_score: 69.87, risk_level: critical` for their union. Nothing in the payload said the name asked about was not there. This is C3-35 — *being more specific made the answer worse* — which was closed for **paths** and never applied to **names** | 5.7.3 (cross-command coherence battery, 2026-08-16) | **High** — the answer is about symbols the caller did not name, and it carries the band a CI gate reads; a typo in a package returns `critical` instead of an error | **closed — on `master`, unreleased.** A target carrying a package is matched as a *qualified* suffix (`fqn == t or fqn.endswith("." + t)`), so `test.TestController` still narrows to the two classes that carry that suffix and `com.a.Widget` resolves to itself alone. When nothing carries the qualified suffix the answer is `not_found` — never another class's numbers — and the near names are offered under `candidates`, which the existing ranking already orders exact-simple-name first, so the answer stays actionable. A **bare** class name keeps the suffix affordance the CLI documents (`ask impact OwnerRepository`), because there the caller has not said which package and ambiguity is the honest reading. Measured after on sa-token: `com.acme.nonexistent.TestController` → `not_found`, `risk_level: unknown`, candidates `[com.pj.test.TestController, com.pj.controller.TestController, …]`; `TestController` → `ambiguous` over the same 4; `com.pj.test.TestController` → `exact`. Regression: `tests/test_qualified_target_resolution_e17.py`, 9 assertions, including the negative control that the bare-name affordance is unchanged |
382
+ | E-18 | **A JAX-RS *client* proxy is published as this repository's HTTP exposure, on an application that declares it serves nothing.** `adorsys/keycloak-config-cli` sets `spring.main.web-application-type=NONE` (`application.properties:2`) and `endpoints` reported **9 endpoints** — all of them RESTEasy client proxies built at `KeycloakProvider.java:221-228` (`getInstance().proxy(cls, uri)`) against *Keycloak's* admin API. A `@Path`-annotated interface is a route declaration in either direction, and nothing asked which direction this one runs in. Cascade: `archetype` won with *"primary surface is HTTP"* and `validation` opened 4 gaps over routes belonging to another product. **Distinct from E-5** — that one put JAX-RS routes in the Spring MVC bucket; this one puts a client in the server population | 5.7.3 (field, keycloak-config-cli @`910728c9`, F-1) | **High** — the whole exposure surface of the answer, and the two commands downstream of it | **closed (local `f6b4506`)** — one structural rule, not a name filter: a type carrying mappings is exposure only if this repository can instantiate it. An **interface no type in the tree implements** (transitive closure over interface `extends`) and that no sub-resource locator returns is not served. Nothing is deleted — `route_surface` keeps the declaration under `scope: declared_not_served`, `NON_SERVED_ROUTE_SCOPES` is the shared authority for the CIR projection and `compute_blast_radius`, and `endpoints` publishes the bucket. `validation` 4 gaps → 0, `archetype` no longer wins on HTTP. Measured on **20 golden repositories: 0 routes excluded outside the field case**. ⚠ The path-fragment filter (`/admin-client/`, name-shaped, VAI) was **not** replaced — doing so moves keycloak's total (695) and needs its own measurement |
383
+ | E-19 | **A nested type inherits the reference set of the file that contains it.** `KeycloakProvider.JacksonProvider` — nine lines whose only statement is `super.locateMapper` — was published as a direct caller of `ResteasyUtil` and `VersionUtil`; `ResourceTypeMapping`, a bare enum, as a caller of `CloneUtil`. Every reference the outer type makes is attributed to every type declared in the same file, so `direct_caller_count` and every score weighting it are inflated by the file's own shape | 5.7.3 (field, keycloak-config-cli, F-7) | Medium — it inflates the one figure a blast radius is read from, and it inflates it more the larger the file | **closed (local `a4c84b8`)** |
384
+ | E-20 | **`explain` does not read edges the graph already holds.** `explain KeycloakProvider` published `outgoing_deps: ["KeycloakConfigProperties"]` — 1 of 4. The three missing (`ResteasyUtil`, `VersionUtil`, `KeycloakProviderException`) are present in the graph and `impact ResteasyUtil` returns the reverse edge, so this is a consumer that queries a narrower relation than the one it names, not a modelling gap | 5.7.3 (field, keycloak-config-cli, F-6) | Medium — `explain` is a *Start here* command, and an incomplete dependency list reads as a complete one | **closed (local `d0fb1aa`)** — a dependency does not have to be injected to be one |
385
+ | E-21 | **The conditional-bean census counts a thing that is not a bean, and publishes one condition for a bean that carries two.** `@interface ConditionalOnKeycloakVersion26OrNewer` — the annotation *declaration* — was counted as a conditional bean (77 against a ground truth of 76) and was also the single `unresolved` entry, so the active/inactive lists stayed clean while the total did not. Separately, `OrganizationRepository` and `OrganizationImportService` each carry two `@Conditional*` annotations and were published with one | 5.7.3 (field, keycloak-config-cli, F-2/F-3) | Medium | **closed (local `66cc75c`)** — the census counts beans, and counts every condition |
386
+ | E-22 | **A build-declared client is matched against the `<properties>` block, so the scope rule is never applied and the citation points at the wrong line.** `export --integrations` reported `unboundid-ldapsdk` as a declared LDAP client citing `pom.xml:102` — a `<unboundid-ldapsdk.version>` property. The dependency is at `pom.xml:412-417` and carries `<scope>test</scope>`, which the payload's own note says it excludes. One wrong region produced both halves: the scan walked every line for the coordinate as a **substring**, and then looked for `<scope>` four lines past *that* line | 5.7.3 (field, keycloak-config-cli, F-9/F-10) | Medium — an evidence-provenance error on an evidence-first product | **closed (local `d9c5498`)** — Maven declarations come from the POM's structure: `<dependency>` blocks outside `<dependencyManagement>`, the artifactId element inside them, scope read from the enclosing block rather than a fixed window. Two further inversions fell out on golden repos: mall reported `okhttp` as a declared client **from the block that excludes it** (`<exclusion>`), and `<dependencyManagement>` pins for modules that never depend on the artifact. The coordinate published is now the one the build declares (`okhttp-jvm`, not `okhttp`) |
387
+ | E-23 | **`matchIfMissing` decides the prose and not the verdict.** Under `--profile dev`, 77 conditional beans stayed `unresolved` while each one's own `reason` said *"Spring registers the bean"*. The condition was evaluated well enough to write the sentence and then not applied to the answer — under-declaration, the mirror of a confident falsehood and cheaper only because it is | 5.7.3 (field, keycloak-config-cli, F-11) | Medium | **closed (local `b383342`)** — an unresolved verdict states what the container would do |
388
+ | E-24 | **A class reached only by reflection is published as `"Low-risk isolated change"`.** `impact OrganizationImportService` → `direct_callers: []`, `risk_level: "low"`, `risk_score: 1.0`. `RealmImportService#invokeOrganizationImportIfAvailable` (`RealmImportService.java:265-278`) invokes it on **every import run** through `Class.forName(<literal>)` + `getBean` + `Method.invoke`, so the call leaves no typed edge. `modernize` did not catch it either (`framework_dispatched: []`, `no_static_callers: 0`), so the safety net never fired. The answer to *"who calls this?"* was `unknown`, not `none` — and a plain text search finds the call site the typed graph cannot | 5.7.3 (field, keycloak-config-cli, F-12) | **High** — the C1-36 failure mode with a second reacher, and this one arrives with a positive conclusion attached | **closed (local `ab3833e`)** — reflection becomes a **fourth signal in `container_wiring`** rather than a parallel authority, so all three consumers (`impact`, `impact-chain`, `pr-impact`) inherit it. Detection reads `literal_facts`, which the IR already carries with the enclosing callee attached — nothing re-scanned, no regex over source text — and needs two independent structural facts, neither a naming convention: the literal is **byte-equal to an in-repo class FQN**, and its enclosing call is a published reflective-lookup API of the JDK or the bean factory. A log line mentioning the simple name does not match. Gated on having no caller **outside** the class, because `_has_incoming_calls` counts self-calls and would have masked every instance (the field class has nine internal call targets and no external caller). `explanation_clause` owns the sentence too: printing *"the container invokes this component"* over a reflectively-dispatched class replaces one false statement with another |
389
+ | E-25 | **`pr-impact` publishes `risk_level: CRITICAL` beside `risk_reason: "No high-risk signals detected"`.** Two adjacent fields of one payload contradicting each other; whichever a reader believes, the other one taught them not to trust the answer | 5.7.3 (field, keycloak-config-cli, F-8) | **High** (trust) | **closed (local `8e2a5a4`)** — a verdict and its reason answer about the same evidence |
390
+ | E-26 | **The integration catalogue has no JAX-RS/RESTEasy client construct, so a tool whose entire purpose is outbound HTTP reports zero integrations.** `export --integrations` on `keycloak-config-cli` → **0**, on a codebase that exists to speak HTTP to Keycloak. This is the surviving half of the F-9 field row; the provenance half closed as E-22 | 5.7.3 (field, keycloak-config-cli, F-9) | Medium — a zero that reads as "no outbound coupling" on the one axis a migration cares about | **closed (local)** — the construct was added the way every other row in the catalogue is admitted: an **FQN-guarded import edge** to a type named by a published specification. Two families, `jakarta`/`javax.ws.rs.client` (`Client`, `ClientBuilder`, `WebTarget`, `Invocation`, `Entity`) and RESTEasy's implementation (`ResteasyClient`, `ResteasyClientBuilder`, `ResteasyClientBuilderImpl`, `ResteasyWebTarget`), plus the declarative half, `@RegisterRestClient`. Nothing is read from the build: the coordinate stays where E-18 put it, in `declared_clients`, and the record here cites the file and line where the client type is imported. `Client`, `Entity` and `Invocation` are ordinary words, so each is admitted **only** from the spec's own client package — which is what keeps `jakarta.ws.rs.core.Response`, returned by a *server* resource, out of an outbound-integration count. Measured: keycloak-config-cli **0 → 3** (`KeycloakProvider.java:66`, `UserProfileRepository.java:41`, `ResteasyUtil.java:42`), and seven golden repositories **byte-identical** — spring-petclinic 0, killbill 4, BroadleafCommerce 10, mall 7, open-banking-gateway 13, sagan 3, spring-boot-admin 21. Regression `tests/test_jaxrs_client_integration_e26.py`, 7 assertions, of which four are negative controls: the server side of the same specification, a same-named type from another package, a repository with no client at all, and a catalogue invariant that every JAX-RS entry carries a package guard |
391
+ | E-27 | **A call made through an *inherited* member is not attributed to the subclass that makes it — the single root of the largest scope error this ledger records.** Two halves, both measured in source at HEAD. **(a) Inherited field receiver:** `_receiver_typed_call_edges` (`repository_ir.py:5770`) does `fmap = field_types.get(owner_cls)` and skips the atom when the receiver is not a field of the owner class *itself*. `AdminBasicEntityController.java:1581/:1631/:1658` call `formService.populateEntityFormFieldValues(...)` on `protected FormBuilderService formService` declared at `AdminAbstractController.java:99` → no edge. **(b) Inherited method target:** `_intra_class_call_edges` (`repository_ir.py:1997`) does `sib = siblings.get(cls)` over the class's own declared methods, so `AdminBasicEntityController.java:1138/:1284` calling `getCriteria(requestParams)` — declared at `AdminAbstractController.java:502`, whose body calls `eps.cleanString(...)` — resolves to nothing. Neither pass climbs the supertype chain, although every supertype is in the model. Confirmed on three surfaces in one session: `impact FormBuilderService` lists 3 direct callers and not the class with 3 call sites; `explain AdminBasicEntityController` omits both dependencies from `outgoing_deps` while listing its 8 subtypes as `incoming_callers`; `impact ExploitProtectionService` misses the class at depth 4 **and** at depth 8 | 5.7.3 (field, BroadleafCommerce @`97705634`, D1) | **Critical** — this is the commercial blocker. The evaluator verified the inheritance chain of every controller outside the cone: of **236 endpoints excluded by default, 209 (88 %) belong to controllers that inherit the member invoking the migrated symbol**. The honest census is 247 IN / 24 UNKNOWN / 2 artefacts, not 38 IN / 235 OUT. It invalidates the *negative* blast radius — the differential promise — and it invalidates it silently, because the positive half of the same run was verified **15/15 exact** against the ground truth | **closed (local)** — both halves resolve against `_supertype_closure`, **one** authority for "what is above this type", derived from the `extends`/`implements` edges the graph already carries (breadth-first, so nearest first; cycle-safe). `_annotation_reference_edges` carried a private copy of the same walk and now shares it. (a) the field map is consulted along the closure, and the declared type is resolved against the class that **declares** the field — an inherited field's simple type name is imported where it is declared, not where it is used. (b) a new post-pass `_inherited_call_edges` resolves an unqualified `m()` / `this.m()` / `super.m()` against the closure, stopping at the **nearest** supertype that declares the name (Java's own rule — a redeclaration lower down shadows one higher up) and leaving a name the class declares itself to the intra-class pass. Both are joins over facts already extracted; no new parse. Acceptance, measured on the field's own repository (BroadleafCommerce @`97705634`, A/B against the parent commit): **`ask impact FormBuilderService .` 3 → 7 direct callers**, with `AdminBasicEntityController` and `AdminBasicOperationsController` among the new ones, `direct_caller_symbol_count` 5 → 21 and `endpoints_affected_count` 32 → 276; **`ask impact ExploitProtectionService .` 38 → 276 endpoints affected**, `security_surface_count` **34 → 247** — the figure this row set as the honest census — and 249 → 322 indirect callers. At method level, the two call sites this row cites go `AdminBasicEntityController#* → FormBuilderService#populateEntityFormFieldValues` **0 → 1** and `AdminBasicEntityController#* → AdminAbstractController#getCriteria` **0 → 2**. Fleet A/B of the CIR call graph: BroadleafCommerce 93 732 → 94 753 (**+1 021**, 607 of them inherited-method), killbill 75 901 → 83 111 (+7 210), keycloak-config-cli 11 310 → 12 210 (+900, of which 480 are the inherited-field half), open-banking-gateway +155, spring-boot-admin +36, spring-petclinic +4, sagan +3, and **mall +0** — the correct answer for a repository whose calls are not inheritance-mediated. ⚠ Method note, because it nearly cost this row its measurement: the editable install puts the **main checkout's** `src` on `sys.path` whatever the cwd, so a worktree A/B run through `run_cli.py` silently measures HEAD **twice**. The baseline must be run with `PYTHONPATH=<worktree>/src`, which precedes site-packages; the first pass of these figures was taken without it and reported no delta at all. Regression `tests/test_inherited_member_calls_e27.py` (11 assertions) asserts the two edges at method level on a fixture where the A/B is clean (base: neither edge; head: both), the transitive reach the inherited body opens, and the negative controls: a name no supertype declares yields no edge, and a same-named method on an unrelated type is never reached |
392
+ | E-28 | **`container_wired` classification compares the *immediate* `extends` clause against the framework vocabulary, so one repository-local level of indirection turns a security filter into `"Low-risk isolated change"`.** `container_wiring._detect` builds `own_supertypes = supertypes.get(fqn)` — the class's directly declared supertypes only — and matches their simple names against `_INTERCEPTION_SUPERTYPES` (`container_wiring.py:104-113`), with no transitive closure over supertypes defined in the repository. `X extends OncePerRequestFilter` is detected; `X extends Y extends OncePerRequestFilter` with `Y` in the tree is not, and `_WIRED_ROLES` is `{"config"}` so a `@Component` stereotype does not rescue it. Measured: `AdminSecurityFilter` (`:63 extends SecurityFilter`, `@Component("blAdminCsrfFilter")` — the filter Broadleaf's own javadoc documents as the admin chain's real one, wired at `AdminSecurityFilter:52` by `<sec:custom-filter ref="blSecurityFilter" before="FORM_LOGIN_FILTER"/>`) returns `risk_level: "low"`, `risk_score: 0.0`, `confidence: "high"`, `blind_spots: []`, *"No callers or endpoints found in the impact chain. Low-risk isolated change."* Its base class `SecurityFilter`, one level up, correctly returns `high` / `null` / `[container_wired]`. The same missing closure explains the omission of `AdminSecurityFilter -> SecurityFilter` and `AdminCsrfFilter -> CsrfFilter` from `spring-audit`'s 20-relation authentication pipeline | 5.7.3 (field, BroadleafCommerce, D2) | **Critical** — C1-36 and E-24 exist to stop exactly this sentence, and it is still reachable by one `extends` hop. A customer excluding that filter from a Spring Security migration retest is exposed, and the failure arrives with `confidence: high` | **closed (local)** — both readers of the clause now read the **chain**, through `supertype_closure` (the authority E-27 established, moved to `cir_graphs` so `container_wiring` can share it without importing the IR). The closure is built over *every* `extends`/`implements` edge, not only the queried classes', because a supertype of a supertype is usually not in `wanted_set` — which is the second reason the old code could not have worked. The immediate supertypes are kept beside it so the **evidence stays exact**: a class that declares the extension point cites `implements X`, a class that inherits it cites `inherits X via Y`, and a reader checking the citation is never sent to a clause the class never wrote. Acceptance, measured on BroadleafCommerce A/B against the parent commit (baseline run with `PYTHONPATH` pinned to the worktree — see the note on E-27): `ask impact AdminSecurityFilter .` goes from `low` / `1.0` / `confidence: high` / *"No callers or dependents found … Low-risk isolated change"* to **`high` / `risk_score: null` / `container_wired: [interception_point]`** citing `inherits org.springframework.web.filter.OncePerRequestFilter via org.broadleafcommerce.common.security.handler.SecurityFilter`; `impact-chain` publishes the same floor. `ask spring-audit .` carries **20 → 42** authentication-pipeline relations, including `AdminSecurityFilter` and `AdminCsrfFilter`, each with the hop published as `inherited_via`. Collateral, measured as a full container-wiring census over 8 repositories (every class, not only a queried one): BroadleafCommerce 576 → 802 classified (interception 115 → 172, reached-through-injected-interface 411 → 587), killbill 178 → 222, open-banking-gateway 171 → 192, spring-boot-admin 104 → 108, sagan 17 → 20, and **spring-petclinic, keycloak-config-cli and mall unchanged** — the correct answer where no local type sits between a class and the SPI. Each addition is the same structural fact one hop further out, and the direction is the safe one: this signal only ever **raises** a floor. Regression `tests/test_container_wired_supertype_chain_e28.py` (8 assertions) covers both consumers, the exact-evidence distinction in both directions, and a plain class that must not be swept in |
393
+ | E-29 | **Three shapes of route false positive in the population every HTTP ratio is keyed on.** All three confirmed against source at HEAD. **(a)** `_parse_route_paths` (`repository_ir.py:4026-4030`) falls back to `re.findall(r'"([^"]*)"', args_str)` after `value=`/`path=` miss, so *any* string literal in the annotation becomes the path: `AdminBasicErrorController:44` `@RequestMapping(produces="text/html")` → path `text/html`, and `BroadleafOauthRegisterController:97` `@RequestMapping(params="action=register")` → path `action=register`. **(b)** `_parse_route_http_methods` (`:4062-4067`) matches `method\s*=\s*\{?\s*((?:RequestMethod\.)?\w+(?:\s*,\s*…)*)` — the alternation keeps consuming past the closing token, so `method=RequestMethod.GET, produces=…` yields `["GET", "PRODUCES"]`, observed verbatim on `AdminCatalogActionsController`. **(c)** `_DSL_ROUTE_RE` (`:6932`) already rejects a **literal** second argument (the B1 fix, openmrs `swapChars.put("/", "slash")`), and the guard is exactly one shape too narrow: `BLVersionResourceResolverDefaultStrategyMap.java:37` is `this.put("/**", new …Strategy(…))` inside `@PostConstruct` **on a subclass of `HashMap`**, whose second argument is an expression — so it is published as `PUT /**`, `source: router_dsl` | 5.7.3 (field, BroadleafCommerce, D3) | Medium — 3 of 273 is a small error rate and a contaminated denominator: every ratio built on `endpoints.total` inherits it, and the customer-facing figure in a report is the one that gets checked | **closed (local `dab7fe5` for (a) and (b), `eb98158` for (c)).** A named `value=`/`path=` still wins outright; what changed is what the *positional* fallback sees — the argument list with every published non-path argument of `@RequestMapping` removed (`_strip_non_path_args`, one authority, used to **reject** candidates and never to admit one by name) — and the `method=` value, now bounded to the braces or to a single token and admitted only if it names a member of `RequestMethod`, whose enum is closed. A `method=` naming an unknown constant yields no verb and `@RequestMapping` falls back to `ANY`, which is the honest answer. The mirror defect was closed in the same pass: an annotation that never named a path no longer reports a `path_expression` it *could not compute*. Measured A/B on a fixture carrying the three field shapes: `/base/text/html` (×2, a path that exists nowhere) and `/base/action=register` → `/base`, and `["GET","PRODUCES"]` → `["GET"]`; the population stays at 4 of 4, because this row is about false positives and dropping a declaration would be the more expensive defect. **(c)** was closed second, and not with the discriminator this row first proposed. Widening the B1 literal guard was rejected by measurement: `put("/items/:id", new UpdateItemRoute(service))` is a real handler registration, so refusing a constructed second argument would trade this false positive for a false negative. The discriminator is the **receiver**: a `put`/`get` inside a type that *is* a collection is that collection's own API, because a router never registers a handler onto a map. Detection matches the **declared supertype** against JDK collection types and published Spring map contracts (`_COLLECTION_SUPERTYPES`), and generic arguments are stripped *first*, which settles two cases at once — a registry merely parameterised by a map (`extends AbstractRegistry<Map<String, Handler>>`) is not one, and a bounded type parameter (`class E<T extends Map<String, X>>`) does not offer its own `extends` to the clause reader. ⚠ The proposed source, `extends_map`, turned out **not** to work, and the first cut passed its own fixture while doing nothing on the real repository: a signature loses its whole supertype clause as soon as the type declares parameters of its own, which is exactly the field shape (`class BLVersionResourceResolverDefaultStrategyMap<T, V> extends HashMap<…>`). The clause is therefore read from the declaration text. **That gap is wider than this row and is filed as E-34.** Measured A/B over **8 golden repositories**: BroadleafCommerce 273 → 272 endpoints with the phantom `PUT /**` gone, and spring-petclinic, spring-boot-admin, open-banking-gateway, killbill, sagan, mall and spring-security-samples **byte-identical** — zero collateral. Regression `tests/test_route_argument_population_e29.py`, 45 assertions, of which 19 are negative controls: every path and verb spelling still resolves, and a genuine imperative router keeps all three of its registrations (lambda, method reference, constructed handler) |
394
+ | E-30 | **`impact-chain` deduplicates affected endpoints by `handler_symbol`, so two distinct HTTP routes sharing one inherited handler method collapse into one.** `GET /com.broadleafcommerce…InventoryImpl/{owningClass}/{collectionField}/typeahead` is present in `impact` and `plan` and absent from `impact-chain` — **37 against 38** for the same symbol in the same session. The route is mounted by `AdminInventoryBasicOperationsController:49` `@RequestMapping(...)` on an inherited method, which is the shape E-3's projection creates and this key cannot distinguish | 5.7.3 (field, BroadleafCommerce, D4) | Low/Medium — small in count, and it is a second cross-command discrepancy on the same symbol beside C1-16's, in the pair a customer compares first | **closed (local)** — the de-duplication was **not** the defect: it was already keyed on `endpoint_id`, which spells `(method, path, controller, handler)`. The loss was one step earlier, in how candidate controllers were collected: `impact-chain` walked from each caller to *its own* class, and for a handler declared by an abstract base the route's controller is a **subclass**, so neither controller was ever considered and every route they mount was dropped. The way in is the handler symbol, now indexed once by `EndpointIndex.controllers_by_handler` rather than re-derived by each consumer. Reproduction is sharper than the field's: on a fixture where one inherited handler is mounted by two controllers, `impact` reported both routes and `impact-chain` reported **none** — 2 vs 0, not 38 vs 37. Field A/B on BroadleafCommerce: `impact-chain ExploitProtectionService` **189 → 239** affected endpoints, `impact-chain FormBuilderService` **186 → 236**. ⚠ The cross-command gap narrows but is not closed — `impact` publishes 276 for the same symbol at the same depth, and that remaining divergence belongs to C1-16, which is open and about units. Regression `tests/test_inherited_handler_routes_e30.py`, 6 assertions, including that the handler is still attributed to the type that declares it (the route is projected, the method is not moved) |
395
+ | E-31 | **A bean reference made from a template is invisible, and — unlike every other thing this product cannot see — it is not declared.** `entityEdit.html:27` invokes `${@blExploitProtectionService.getCsrfTokenParameter()}` and `.getCSRFToken()` through SpEL. The bean is absent from the 78 components `plan` reports, and the symbol's `blind_spots` is `[]`. `NC-007` declares the servlet population and `NC-010` the container-wired one; **no `NC-*` covers templates**, so here a "not found" is presented without the "not looked at" label that is half of what this product sells | 5.7.3 (field, BroadleafCommerce, D5) | Medium — the miss is ordinary; publishing it as an empty result with no non-coverage is the defect | **closed (local)** — declared, which is what the row asked for and what the defect actually was: the miss is ordinary, the unlabelled silence is not. **NC-012** (`blast_radius` surface, `non-coverage-v1` shape) states that a bean reached from a template — a SpEL `@beanName` inside `${…}` — is outside what a call-graph projection looks at, why (a template is not compiled against the bean and lives in a file this analysis does not parse), and what answers it today (`grep -rn '@<beanName>' src/main/resources`, because the syntax is fixed and published). The second half of the row was that the block was not published where the field met the defect: `pr-impact` emitted it, `impact`, `impact-chain` and `plan` did not. All three do now, from the one non-coverage authority, so the eleven rows that were already true of them stopped being invisible too. The README table is generated-checked against the registry and carries the row. Modelling it as a fourth reacher stays **open and unclaimed** — it would be evidence-shaped for the same reason the declaration is, and this row does not pretend it happened. Regression `tests/test_template_bean_non_coverage_e31.py`, 7 assertions, including the shape the field met: a service with no Java caller and a template that names it, where the answer is empty and now says what it did not look at |
396
+ | E-32 | **`bfs_truncated` answers a different question from the one the reader asks, and the two answers disagree exactly where it matters.** `repository_ir.py:8392`: `_bfs_truncated = _effective_depth < max_depth` — it reports whether the **hub guard cut the depth below the requested one**, not whether the closure was exhausted. On `ExploitProtectionService` at the default depth 4 the frontier is still `{2: 89, 3: 84, 4: 76}` wide and the flag is `false`; at depth 8 the same run continues `{5: 18, 6: 5, 7: 2, 8: 2}` and finishes at **276 indirect callers and 39 endpoints** against 249 and 38. A reader takes `bfs_truncated: false` beside a published depth as *the cone is closed* | 5.7.3 (field, BroadleafCommerce, D6) | Medium — it converts a floor into a total on the axis a scope decision is taken from, and it does it in the same payload that gets the answer right | **closed (local)** — the walk now records whether a node dropped at the depth limit still had callers, which is the fact the name promises, and it costs one extra `_walk_callers` per boundary node with a short-circuit at the first live one. Four fields where one stood: `bfs_truncated` (**the closure was not exhausted** — the union, so the flag a reader already consults is the honest one), `closure_complete`, `depth_capped_by_guard` and `frontier_open_at_depth_limit`. The two causes are named apart because their remedies differ — a hub cap is lifted by choosing a lower-fan-in entry point, a live frontier by raising `--depth` — and every block that was written about the hub cap (the truncation reason, its effect note, the sampling prose, `indirect_callers_computed`) stays keyed on the guard, so nothing acquired a wrong reason. The prose says it too, because prose is what a reader quotes: *"the walk stopped at depth=4 with callers still unexplored, so every transitive figure here is a floor"*. Measured on the row's own subject: `impact ExploitProtectionService` at the default depth now publishes `bfs_truncated: true` / `closure_complete: false` / `frontier_open_at_depth_limit: true` beside `depth_reached: 4`, and at `--depth 8` the frontier is **still** open (322 → 349 indirect callers), which is the honest answer and not the one the old flag gave. Regression `tests/test_bfs_closure_completeness_e32.py`, 7 assertions over a chain deeper than the depth queried, with the exhausted case asserted in both directions |
397
+ | E-33 | **`verify-edit`'s contract diff attributes a removed method to the wrong type and misses a `throws` clause removal that breaks callers.** On a real working-tree change it reported `SpringBootBootstrappingServletContextListener#run` as a removed public method — `run()` belonged to an **anonymous inner class** — and did not report that `protected createInitializer(...)` lost `throws PrivilegedActionException`, which breaks every caller that catches it. The verdict itself was right (`break`, `contract_broken: 1 public API change`, correct `head_sha`, `security_delta: false`) | 5.7.3 (field, BroadleafCommerce, D8) | Low — the gate answered correctly; the evidence beside the answer is what is wrong, and this gate's product is its evidence | **closed (local)** — both halves are about what an owner and a signature *are*. **Owner:** the extractor now opens a scope on an anonymous class body (`new Runnable() {`) and names it the way Java does, `Outer$1`, so the members inside are attributed to it and the type itself is a symbol — a member's owner always exists in the model. Both public-surface readers (`verify_edit._nodes_by_fqn`, `contract_diff.extract_public_contract`) skip owners containing `$`: nothing outside can name an anonymous type, so nothing can depend on it, and the discriminator is the owner rather than a naming rule. A creation whose argument list spans lines is not matched — the scope stays with the enclosing type, as before, rather than being guessed. **Signature:** the `throws` clause is captured and published as `(String)->Object throws PrivilegedActionException`, which is DR-1 applied to the other half of the declaration. End-to-end on the field's own shape: `verify-edit` went from *2 public API changes* (one of them the phantom `#run`) to **1**, and that one is the `throws` removal it used to miss entirely, cited from and to. Fleet A/B: symbol population BroadleafCommerce 30 427 → 30 694, killbill 18 513 → 18 887, spring-petclinic 267 → 269, mall 15 752 → 15 757 — the growth is the anonymous types themselves; their members are **re-attributed, not added** — and the endpoint population is byte-identical on all five (268 / 285 / 17 / 240 / 0). Regression `tests/test_contract_evidence_e33.py`, 9 assertions covering both halves and the negative controls (the named type keeps its own members; a method that throws nothing says nothing) |
398
+ | E-34 | **A type that declares parameters of its own has no supertype anywhere in the model — not an edge, not a signature.** Two independent readers of the `extends`/`implements` clause, both written as if a declaration went straight from the type's name to its supertype. **(a)** The `extends`/`implements` **edge** builder (`repository_ir.py:3229-3232`) matches `(?:class\|interface)\s+(\w+)(?:\s+extends\s+…)?(?:\s+implements\s+…)?\s*\{`: after the name it requires whitespace, so `class Generic<T, V> extends Base {` matches **nothing at all** — not a partial match, no edge of either kind. **(b)** `SymbolRecord.signature` drops the clause the same way, so `class B<T, V> extends HashMap<String, V>` is recorded as `class B`, and `extends_map` — derived from that signature at `repository_ir.py:7043` and handed to `_build_route_surface` — inherits the hole. Minimal end-to-end witness: three classes, `Plain extends Base` and `Generic<T, V> extends Base`; `ask explain Base` publishes **`Used By: Plain`** and nothing else. Census over 8 repositories, counting only types whose declaration really does carry a clause: **254 of 8 182 (3,1 %)** lose it — open-banking-gateway 57 of 595 (**9,6 %**), keycloak 144 of 4 356, killbill 25 of 838, BroadleafCommerce 26 of 1 965 | 5.7.3 (found while closing E-29(c), 2026-08-16) | **High** — it is silent, it is repo-wide, and it removes the one relation the two commercial blockers are about to be fixed by. A generic abstract controller loses its inheritance projection (the E-3 family), a generic filter loses the supertype `container_wiring` classifies on (E-28), and any supertype **closure** built for E-27 will walk a chain with holes in it and report the result as complete | **closed (local)** — one root, one fix: the type's own parameter block is now matched as a **balanced** `<…>` span instead of "everything up to the next `>`". The unbalanced form stopped inside `<T, V>` and, to reach the body brace, had to swallow the whole `extends …` clause — which is why the same defect appeared twice from one cause, in `_CLASS_DECL_RE` (the signature) and in the edge builder (`repository_ir.py:3229`, where it matched nothing at all). Balancing is also what settles the bounded-parameter trap without a second rule: the `extends` inside `class E<T extends Map<String, X>>` belongs to the parameter, and the reader now walks past it to the type's own clause. Nesting is bounded at four levels, past which the old unbalanced branch is kept as a **fallback**: it loses the clause as it always did, and the type still matches — a declaration this product cannot fully read must not vanish from the model. ⚠ The signature half was weighed as the row asked and carries no churn: `_compute_stable_id` is built from `(package, class, kind, name)` and never from `signature`, and `contract_diff.extract_public_contract` reads signatures of **method** nodes only, so restoring a type's clause changes no identity and no contract diff. Two consumers that read the clause off `signature` (interface closure `:3688`, `Api`-interface controllers `:6737`) gain the generic types they were silently skipping. Measured A/B over **25 repositories** (61 k `.java`): **0 type declarations lost, 0 gained** — the population is byte-identical — and clause-carrying declarations recovered wherever the shape occurs: dubbo +207, alfresco +130, jenkins +104, keycloak +74, langchain4j +67, BroadleafCommerce +26, killbill +27; keycloak-config-cli and mall +0, which is the correct answer for repositories without the shape. Regression `tests/test_generic_supertype_clause_e34.py`, 15 assertions: the end-to-end witness (`explain Base` now names `Plain`, `Generic` **and** `Bounded`), the `extends` edge in the CIR, the signature, and the negative controls — record components, enums, an unparameterised type, and the past-depth declaration that must still match with no clause. The E-29(c) test that documented this hole from the other side was updated to assert both readers agree |
399
+ | E-35 | **A statement is parsed as a method declaration, and the method it invents is published with a call edge pointing at it.** `return unknownHelper("x");` matches the declaration shape read by `_METHOD_DECL_RE` — no modifiers, "return type" `return`, name `unknownHelper`, parameter list `"x"` — so the symbol table gained `com.a.BasicCtl#unknownHelper` with `signature: ("x")->return`, an `in_degree` of 1 and a `calls` edge from the real method that contains the statement. Census over 5 repositories: **BroadleafCommerce 1 047 of 23 913 methods (4,4 %)**, killbill 846, keycloak-config-cli 76, mall 11, spring-petclinic 6 | 5.7.3 (found while writing the E-27 negative controls, 2026-08-16) | Medium — it inflates every method population and every degree derived from it, and a phantom callee is the shape a reader cannot check: the symbol does not exist to be looked up | **closed (local)** — the discriminator is the return-type slot, because nothing declares a method whose type is `return`. `_STATEMENT_HEADS` lists the keywords that start a statement and can stand where a type is read (`return`, `throw`, `new`, `else`, `assert`, `yield`, `case`, `do`, `instanceof`, `break`, `continue`, `catch`, `finally`); **`void` is deliberately absent — it is a return type**. Measured A/B: phantoms **1 047 → 0** on Broadleaf, 846 → 0 on killbill, 76 → 0 on keycloak-config-cli, and the method population falls by **exactly** that count on each repository, so no real declaration was lost; the endpoint population is byte-identical on all five. Regression `tests/test_statement_is_not_a_declaration_e35.py`, 10 assertions, of which 7 are negative controls — every real declaration in the same file, `void` kept as a type, and a method that returns a constructed object |
376
400
 
377
401
  ## Class P — Product and procurement (not analysis defects)
378
402
 
@@ -426,6 +450,7 @@ provenance: E-3 was found from outside, on the surface where silence is loudest.
426
450
  | CL-28 | *(eval #26 — what carries over rather than opening a row, the CL-20/CL-22/CL-26/CL-27 precedent)* | **B18 is C3-106, closed in 5.1.0 and certified from the outside** — *"corregido casi literalmente como lo propuse"*, with `reason`/`detail` the report did not ask for. **B16 stays F-AV** (4.17.0): the round did not truncate, so `total_defects` was correctly unrenamed; latent for the fourth round. **B8 stays informational** for the third round, by the field's own measurement. **B19/B21/B22 stay C3-102/C3-103/C3-107** — re-reported with a round of ageing, not new facts — and **B20 splits**: the stdout half stays closed (CL-26) and the `-o` half is new and is C3-111. **B17's remainder is C3-109 plus the process half, which is C3-110** — one task, as the report says | Recorded, not re-derived |
427
451
  | CL-29 | *(eval #27 — what carries over rather than opening a row)* | **Three closures certified from the outside, one of them with the implementation the report says it did not ask for**: B17 is C3-109 (*"ya no miente; admite"* — the anchor names its build and that this one was not re-measured), B20 is C3-111 (`OUTPUT_TOO_LARGE` before the build, with a band whose basis states that its own top end under-states), B22 is C3-107 (*"base_cir_hash: null, totales por lado null nunca 0 — la optimización correcta, no la perezosa"*). **B18 stays closed** (C3-106, second consecutive certification). **B16 stays F-AV** (4.17.0), latent for the fifth round: 5.2.0 finishes `spring-audit` in 17,0 s, so nothing truncated and `total_defects` was correctly unrenamed — the rename is conditional on `partial` by design, and a round that never truncates cannot observe it. **B8 stays informational** for the fourth round, by the field's own measurement and their own conclusion that the 280× came from the serial path rather than from workers. **B21 stays C3-103** — re-measured, not re-derived. **B19 does *not* carry over**: the fix it names shipped and reproduces here, and what the field measures is a different hole, **C3-112**. **B10's mechanism shipped as C3-110 and its evidence did not, which is C3-113** | Recorded, not re-derived |
428
452
  | CL-20 | *(non-reproducers from eval #20, recorded so they are not raised a third time — the CL-17/CL-18 precedent)* | **`export .` "requires a mode flag" is the documented behaviour and the error already carries the cure**: verified at HEAD, the rejection publishes `hint: "Pass --c4 for the full architecture export, or one of --by-directory / --module-graph / --integrations for a section."` and `expected: "--c4 \| --by-directory \| --module-graph \| --integrations"`, and `ask export --help` lists all four with a paragraph each. The report's *"el flag no está en la ayuda del comando raíz"* is about `ask --help`, which lists commands rather than each command's flags, by design. · **`trend` and `baseline trend` failing identically is correct**: they are **one command published under two names** (`cli.py` — `"baseline trend" # same command under the group name`), so two different messages would be the defect | **no action** — both verified at 4.16.0. What *is* actionable from the same section of the report is C4-22 (`schema`), which is a real gap and is filed |
453
+ | CL-30 | **Four sentences the product's own positioning invites, and that E-27/E-28 make false today.** From the Spring-migration validation on BroadleafCommerce: *"ASK demuestra qué **no** hay que revisar"*; *"N componentes quedan estructuralmente fuera del alcance"*; *"cierre transitivo completo"*; and — the dangerous one, because it is where a customer would believe it — *"un `risk: low` con 0 callers significa cambio aislado"* | **88 % of the default exclusions were falsified by hand on one repository** (209 of 236 endpoints), and `AdminSecurityFilter` publishes `low` / `0.0` / `confidence: high` over the admin filter chain. What survives the same scrutiny, unchanged: the file surface was **15/15 exact**; every `symbol → component → file → line → commit` chain held; the unit of every figure is published; `MIG-010`/`MIG-031` are true positives with a measurable delta (171 → 170); *"78 components, 1 test"* is a first-order QA conclusion; the evidence is reproducible against a commit; and a **build-boundary** exclusion does hold — the `integration` module is a leaf of the Maven graph, verified over all 10 poms | **reverted (local)** — E-27 and E-28 are closed and the reversal condition this row set was measured, not argued: `ask impact ExploitProtectionService .` on BroadleafCommerce goes from 38 to **276** endpoints affected and from 34 to **247** security declarations — the honest census the evaluator computed by hand — and `ask impact AdminSecurityFilter .` no longer publishes `low` with `confidence: high` over the admin filter chain but `high` with `risk_score: null` and the container-wiring evidence beside it. Two things stay true and are part of the reversal rather than exceptions to it: an exclusion claim is only as good as the closure behind it, so the **field revalidation on the evaluator's repository is still owed** before the differential promise goes into commercial material — what is measured here is the acceptance test this ledger defined, not a second audit; and the positive half needs no rehabilitation, having been 15/15 exact throughout. Original note: until E-27 and E-28 close, the sellable answer is *positive map + uncertainty inventory + retest gap*, with no exclusion claim beyond a verifiable build boundary. The evaluator's own one-minute framing is the defensible one: *"la migración no toca transacciones, toca 5 subsistemas y 78 componentes, y sólo hay 1 test cubriéndolos."* Reversing this row is the acceptance test of E-27/E-28, not a copy decision |
429
454
 
430
455
  ---
431
456