sourcecode 5.4.2__tar.gz → 5.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. {sourcecode-5.4.2 → sourcecode-5.5.1}/CHANGELOG.md +49 -0
  2. {sourcecode-5.4.2 → sourcecode-5.5.1}/PKG-INFO +3 -3
  3. {sourcecode-5.4.2 → sourcecode-5.5.1}/README.md +2 -2
  4. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/USER_GUIDE.md +1 -1
  5. {sourcecode-5.4.2 → sourcecode-5.5.1}/pyproject.toml +1 -1
  6. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/__init__.py +1 -1
  7. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/cache_model.py +9 -0
  8. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/cli.py +71 -7
  9. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/non_coverage.py +21 -0
  10. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/reference_facts.py +51 -11
  11. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/release_info.py +1 -1
  12. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/repository_ir.py +54 -11
  13. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/validation_surface.py +85 -0
  14. {sourcecode-5.4.2 → sourcecode-5.5.1}/.github/workflows/build-windows.yml +0 -0
  15. {sourcecode-5.4.2 → sourcecode-5.5.1}/.github/workflows/perf-gate.yml +0 -0
  16. {sourcecode-5.4.2 → sourcecode-5.5.1}/.gitignore +0 -0
  17. {sourcecode-5.4.2 → sourcecode-5.5.1}/.ruff.toml +0 -0
  18. {sourcecode-5.4.2 → sourcecode-5.5.1}/CLAUDE.md +0 -0
  19. {sourcecode-5.4.2 → sourcecode-5.5.1}/CONTRIBUTING.md +0 -0
  20. {sourcecode-5.4.2 → sourcecode-5.5.1}/LICENSE +0 -0
  21. {sourcecode-5.4.2 → sourcecode-5.5.1}/SECURITY.md +0 -0
  22. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/CACHE.md +0 -0
  23. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/COMMERCIAL-OFFER.md +0 -0
  24. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/CUSTOMER-VALIDATION.md +0 -0
  25. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/DEFECT-LEDGER.md +0 -0
  26. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/DEMO-5MIN.md +0 -0
  27. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/DEVELOPMENT-ROADMAP.md +0 -0
  28. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/EXECUTION-PLAN-12MO.md +0 -0
  29. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/EXECUTIVE-ROADMAP.md +0 -0
  30. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/MANUAL-USUARIO.md +0 -0
  31. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/MODERNIZATION.md +0 -0
  32. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/PRODUCT_AUDIT.md +0 -0
  33. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/PRODUCT_IDENTITY.md +0 -0
  34. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/PRODUCT_TIERS.md +0 -0
  35. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/RC1-CHECKLIST.md +0 -0
  36. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/RELEASE-CHECKLIST.md +0 -0
  37. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/CATALOG-retrieval-intents.md +0 -0
  38. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/DESIGN-knowledge-provider-integration.md +0 -0
  39. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/DESIGN-p5.3b-statement-surface.md +0 -0
  40. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/DESIGN-p5.5-span-index.md +0 -0
  41. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/DESIGN-p6-semantic-impact-engine.md +0 -0
  42. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/DESIGN-p6-semantic-integration-engine.md +0 -0
  43. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/DESIGN-p7-semantic-services.md +0 -0
  44. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/DESIGN-robustness-security-inference.md +0 -0
  45. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/DESIGN-semantic-retrieval.md +0 -0
  46. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/EPV-p6-class-typeref.md +0 -0
  47. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/REPORT-p6-flow-analyzer-comparison.md +0 -0
  48. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/REPORT-p6-integration-detector-comparison.md +0 -0
  49. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/REPORT-p8-flow-analyzer-migration.md +0 -0
  50. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/REPORT-p9-integration-detector-migration.md +0 -0
  51. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/adr/0001-semantic-ir.md +0 -0
  52. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/adr/0002-source-evidence-boundary.md +0 -0
  53. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/adr/0003-architectural-characterization.md +0 -0
  54. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/adr/0004-semantic-retrieval.md +0 -0
  55. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/adr/0005-evidence-reconciliation.md +0 -0
  56. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/adr/0006-performance-harness.md +0 -0
  57. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/adr/0007-composition-semantics.md +0 -0
  58. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/adr/0008-answer-coherence.md +0 -0
  59. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/consumer-disposition-inventory.md +0 -0
  60. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/overview.md +0 -0
  61. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/product-architecture-review.md +0 -0
  62. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/product-strategy-layering.md +0 -0
  63. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/semantic-ir-consumer-matrix.md +0 -0
  64. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/architecture/semantic-ir-roadmap.md +0 -0
  65. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/baseline-ci.yml +0 -0
  66. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/commercial-pipeline-audit.md +0 -0
  67. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/contracts-ci.yml +0 -0
  68. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/contracts.md +0 -0
  69. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/eval/2026-06-15-petclinic-rest-issue11-vets-pagination.md +0 -0
  70. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/eval/2026-06-15-petclinic-rest-issue147-validation.md +0 -0
  71. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/eval/2026-06-15-petclinic-rest-workflow.md +0 -0
  72. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/eval/2026-06-15-phase18-openapi-surface.md +0 -0
  73. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/eval/2026-06-15-phase20-validation-surface.md +0 -0
  74. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/eval/2026-06-16-broadleaf-checkout-impact-fieldtest.md +0 -0
  75. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/eval/2026-06-16-petclinic-rest-issue11-weakness2-closed.md +0 -0
  76. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/eval/2026-06-16-spring-petclinic-issue2333-dead-vets.md +0 -0
  77. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/licensing-schema.md +0 -0
  78. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/migrate-check.md +0 -0
  79. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/README.md +0 -0
  80. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/REGRESSION-GATE.md +0 -0
  81. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/SUMMARY.md +0 -0
  82. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/alfresco__ask__cold.json +0 -0
  83. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/alfresco__ask__warm.json +0 -0
  84. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/broadleaf__ask__cold.json +0 -0
  85. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/broadleaf__ask__warm.json +0 -0
  86. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/eureka__ask__cold.json +0 -0
  87. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/eureka__ask__warm.json +0 -0
  88. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/jenkins__ask__cold.json +0 -0
  89. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/jenkins__ask__warm.json +0 -0
  90. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/jobrunr__ask__cold.json +0 -0
  91. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/jobrunr__ask__warm.json +0 -0
  92. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/keycloak__ask__cold.json +0 -0
  93. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/keycloak__ask__warm.json +0 -0
  94. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/neo4j__ask__cold.json +0 -0
  95. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/neo4j__ask__warm.json +0 -0
  96. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/ofbiz__ask__cold.json +0 -0
  97. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/ofbiz__ask__warm.json +0 -0
  98. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/openmrs__ask__cold.json +0 -0
  99. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/openmrs__ask__warm.json +0 -0
  100. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/petclinic__ask__cold.json +0 -0
  101. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.15/petclinic__ask__warm.json +0 -0
  102. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/SUMMARY.md +0 -0
  103. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/alfresco__ask__cold.json +0 -0
  104. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/alfresco__ask__warm.json +0 -0
  105. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/broadleaf__ask__cold.json +0 -0
  106. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/broadleaf__ask__warm.json +0 -0
  107. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/eureka__ask__cold.json +0 -0
  108. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/eureka__ask__warm.json +0 -0
  109. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/jenkins__ask__cold.json +0 -0
  110. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/jenkins__ask__warm.json +0 -0
  111. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/jobrunr__ask__cold.json +0 -0
  112. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/jobrunr__ask__warm.json +0 -0
  113. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/keycloak__ask__cold.json +0 -0
  114. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/keycloak__ask__warm.json +0 -0
  115. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/neo4j__ask__cold.json +0 -0
  116. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/neo4j__ask__warm.json +0 -0
  117. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/ofbiz__ask__cold.json +0 -0
  118. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/ofbiz__ask__warm.json +0 -0
  119. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/openmrs__ask__cold.json +0 -0
  120. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/openmrs__ask__warm.json +0 -0
  121. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/petclinic__ask__cold.json +0 -0
  122. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/2.5.16/petclinic__ask__warm.json +0 -0
  123. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/gate-latest/README.md +0 -0
  124. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/gate-latest/broadleaf__ask__warm.json +0 -0
  125. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/gate-latest/broadleaf__endpoints__warm.json +0 -0
  126. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/gate-latest/broadleaf__migrate-check__warm.json +0 -0
  127. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/gate-latest/broadleaf__posture__warm.json +0 -0
  128. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/gate-latest/broadleaf__spring-audit__warm.json +0 -0
  129. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/gate-latest/broadleaf__validation__warm.json +0 -0
  130. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/perf/baselines/reference-best/README.md +0 -0
  131. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/posture.md +0 -0
  132. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/privacy.md +0 -0
  133. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/pro-experience-audit.md +0 -0
  134. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/schema.md +0 -0
  135. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/spec/envelope-v1.md +0 -0
  136. {sourcecode-5.4.2 → sourcecode-5.5.1}/docs/spring-audit-ci.yml +0 -0
  137. {sourcecode-5.4.2 → sourcecode-5.5.1}/raw +0 -0
  138. {sourcecode-5.4.2 → sourcecode-5.5.1}/scripts/compare_integration_engines.py +0 -0
  139. {sourcecode-5.4.2 → sourcecode-5.5.1}/scripts/customer_smoke_test.sh +0 -0
  140. {sourcecode-5.4.2 → sourcecode-5.5.1}/scripts/generate_jdk_exports.py +0 -0
  141. {sourcecode-5.4.2 → sourcecode-5.5.1}/scripts/perf_gate.py +0 -0
  142. {sourcecode-5.4.2 → sourcecode-5.5.1}/scripts/perf_harness.py +0 -0
  143. {sourcecode-5.4.2 → sourcecode-5.5.1}/scripts/sync_gate_anchors.py +0 -0
  144. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/adaptive_scanner.py +0 -0
  145. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/archetype.py +0 -0
  146. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/architectural_baseline.py +0 -0
  147. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/architectural_delta.py +0 -0
  148. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/architecture_analyzer.py +0 -0
  149. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/architecture_summary.py +0 -0
  150. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/ast_extractor.py +0 -0
  151. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/audit_report.py +0 -0
  152. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/baseline_autocapture.py +0 -0
  153. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/bundled_docs.py +0 -0
  154. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/cache.py +0 -0
  155. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/call_surface.py +0 -0
  156. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/caller_metrics.py +0 -0
  157. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/caller_reach.py +0 -0
  158. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/canonical_ir.py +0 -0
  159. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/chain_rules.py +0 -0
  160. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/change_plan.py +0 -0
  161. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/cir_graphs.py +0 -0
  162. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/classifier.py +0 -0
  163. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/client_calls.py +0 -0
  164. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/code_notes_analyzer.py +0 -0
  165. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/compare.py +0 -0
  166. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/confidence_analyzer.py +0 -0
  167. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/constraint_diff.py +0 -0
  168. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/consumer_join.py +0 -0
  169. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/container_wiring.py +0 -0
  170. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/context_cache.py +0 -0
  171. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/context_graph.py +0 -0
  172. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/context_scorer.py +0 -0
  173. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/context_summarizer.py +0 -0
  174. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/contract_diff.py +0 -0
  175. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/contract_init.py +0 -0
  176. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/contract_model.py +0 -0
  177. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/contract_pipeline.py +0 -0
  178. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/coverage_parser.py +0 -0
  179. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/data_exposure.py +0 -0
  180. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/data_labels.py +0 -0
  181. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/declarations.py +0 -0
  182. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/defect_identity.py +0 -0
  183. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/degradation.py +0 -0
  184. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/dependency_analyzer.py +0 -0
  185. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/deployment_prefix.py +0 -0
  186. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detach.py +0 -0
  187. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/__init__.py +0 -0
  188. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/base.py +0 -0
  189. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/csproj_parser.py +0 -0
  190. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/dart.py +0 -0
  191. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/dotnet.py +0 -0
  192. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/elixir.py +0 -0
  193. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/go.py +0 -0
  194. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/heuristic.py +0 -0
  195. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/hybrid.py +0 -0
  196. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/java.py +0 -0
  197. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/jvm_ext.py +0 -0
  198. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/nodejs.py +0 -0
  199. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/parsers.py +0 -0
  200. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/php.py +0 -0
  201. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/project.py +0 -0
  202. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/python.py +0 -0
  203. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/ruby.py +0 -0
  204. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/rust.py +0 -0
  205. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/systems.py +0 -0
  206. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/terraform.py +0 -0
  207. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/detectors/tooling.py +0 -0
  208. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/doc_analyzer.py +0 -0
  209. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/dynamic_argument_surface.py +0 -0
  210. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/endpoint_literals.py +0 -0
  211. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/endpoint_metrics.py +0 -0
  212. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/entrypoint_classifier.py +0 -0
  213. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/env_analyzer.py +0 -0
  214. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/envelope.py +0 -0
  215. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/environment_resolution.py +0 -0
  216. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/error_schema.py +0 -0
  217. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/evidence_provider.py +0 -0
  218. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/execution_plan.py +0 -0
  219. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/explain.py +0 -0
  220. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/facts/__init__.py +0 -0
  221. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/facts/registry.json +0 -0
  222. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/file_chunker.py +0 -0
  223. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/file_classifier.py +0 -0
  224. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/filter_surface.py +0 -0
  225. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/format_contract.py +0 -0
  226. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/fqn_utils.py +0 -0
  227. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/gate_anchors.py +0 -0
  228. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/git_analyzer.py +0 -0
  229. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/git_checkout.py +0 -0
  230. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/graph_analyzer.py +0 -0
  231. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/graph_evidence.py +0 -0
  232. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/hibernate_strat.py +0 -0
  233. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/identity_fallback.py +0 -0
  234. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/integration_coordinates.py +0 -0
  235. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/jdk_exports.py +0 -0
  236. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/license.py +0 -0
  237. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/__init__.py +0 -0
  238. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/dependency_import.py +0 -0
  239. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  240. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  241. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  242. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  243. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  244. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/orchestrator.py +0 -0
  245. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/registry.py +0 -0
  246. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/runner.py +0 -0
  247. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp/server.py +0 -0
  248. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/mcp_nudge.py +0 -0
  249. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/metrics_analyzer.py +0 -0
  250. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/migrate_check.py +0 -0
  251. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/migration_blast.py +0 -0
  252. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/openapi_surface.py +0 -0
  253. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/openrewrite_recipe.py +0 -0
  254. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/output_budget.py +0 -0
  255. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/output_encoding.py +0 -0
  256. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/parallel.py +0 -0
  257. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/parse_cache.py +0 -0
  258. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/partial_contract.py +0 -0
  259. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/path_admission.py +0 -0
  260. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/path_filters.py +0 -0
  261. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/perf.py +0 -0
  262. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/phased_run.py +0 -0
  263. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/pipe_contract.py +0 -0
  264. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/posture.py +0 -0
  265. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/pr_comment_renderer.py +0 -0
  266. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/pr_impact.py +0 -0
  267. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/prepare_context.py +0 -0
  268. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/progress.py +0 -0
  269. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/provenance.py +0 -0
  270. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/ranking_engine.py +0 -0
  271. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/readiness_timeline.py +0 -0
  272. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/readonly.py +0 -0
  273. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/reconciliation.py +0 -0
  274. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/redactor.py +0 -0
  275. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/relevance_scorer.py +0 -0
  276. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/remedies.py +0 -0
  277. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/rename_refactor.py +0 -0
  278. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/repo_classifier.py +0 -0
  279. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/__init__.py +0 -0
  280. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/context.py +0 -0
  281. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/errors.py +0 -0
  282. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/executor.py +0 -0
  283. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/planner.py +0 -0
  284. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/query.py +0 -0
  285. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/request.py +0 -0
  286. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/resolution.py +0 -0
  287. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/result.py +0 -0
  288. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/retriever.py +0 -0
  289. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/runtime.py +0 -0
  290. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/steps.py +0 -0
  291. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  292. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/steps_graph.py +0 -0
  293. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/steps_impact.py +0 -0
  294. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/steps_intf.py +0 -0
  295. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/steps_struct.py +0 -0
  296. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  297. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/ris.py +0 -0
  298. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/risk.py +0 -0
  299. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/rule_catalog.py +0 -0
  300. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/rule_pass.py +0 -0
  301. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/runs.py +0 -0
  302. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/runtime_classifier.py +0 -0
  303. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/sarif.py +0 -0
  304. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/scanner.py +0 -0
  305. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/schema.py +0 -0
  306. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  307. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/security_chain.py +0 -0
  308. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/security_config.py +0 -0
  309. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/security_config_scan.py +0 -0
  310. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/security_posture.py +0 -0
  311. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/semantic_analyzer.py +0 -0
  312. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/semantic_impact_engine.py +0 -0
  313. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/semantic_integration_engine.py +0 -0
  314. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/semantic_services.py +0 -0
  315. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/serializer.py +0 -0
  316. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/servlet_surface.py +0 -0
  317. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/source_text.py +0 -0
  318. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/spring_event_topology.py +0 -0
  319. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/spring_findings.py +0 -0
  320. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/spring_impact.py +0 -0
  321. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/spring_model.py +0 -0
  322. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/spring_profiles.py +0 -0
  323. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/spring_properties.py +0 -0
  324. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/spring_security_audit.py +0 -0
  325. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/spring_semantic.py +0 -0
  326. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/spring_tx_analyzer.py +0 -0
  327. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/sql_taint.py +0 -0
  328. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/summarizer.py +0 -0
  329. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/target_admission.py +0 -0
  330. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/telemetry/__init__.py +0 -0
  331. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/telemetry/config.py +0 -0
  332. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/telemetry/consent.py +0 -0
  333. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/telemetry/events.py +0 -0
  334. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/telemetry/filters.py +0 -0
  335. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/telemetry/transport.py +0 -0
  336. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/test_gap_ranking.py +0 -0
  337. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/test_sources.py +0 -0
  338. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/text_input.py +0 -0
  339. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/timeline.py +0 -0
  340. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/token_estimate.py +0 -0
  341. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/tree_utils.py +0 -0
  342. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/type_usage_surface.py +0 -0
  343. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/validation_inference.py +0 -0
  344. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/verify_edit.py +0 -0
  345. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/verify_repo.py +0 -0
  346. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/verify_rules.py +0 -0
  347. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/version_check.py +0 -0
  348. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/waivers.py +0 -0
  349. {sourcecode-5.4.2 → sourcecode-5.5.1}/src/sourcecode/workspace.py +0 -0
  350. {sourcecode-5.4.2 → sourcecode-5.5.1}/supabase/functions/README.md +0 -0
  351. {sourcecode-5.4.2 → sourcecode-5.5.1}/supabase/functions/get-license/index.ts +0 -0
  352. {sourcecode-5.4.2 → sourcecode-5.5.1}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  353. {sourcecode-5.4.2 → sourcecode-5.5.1}/supabase/functions/telemetry/index.ts +0 -0
  354. {sourcecode-5.4.2 → sourcecode-5.5.1}/supabase/sql/license_event_ordering.sql +0 -0
  355. {sourcecode-5.4.2 → sourcecode-5.5.1}/supabase/sql/licensing_schema.sql +0 -0
  356. {sourcecode-5.4.2 → sourcecode-5.5.1}/supabase/sql/telemetry_events.sql +0 -0
@@ -2,6 +2,55 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.5.1] — 2026-08-13
6
+
7
+ **External audit corrections and command execution performance.**
8
+
9
+ ### Fixed
10
+ - **Impact callers** now expand accessor and interface frontiers consistently,
11
+ with caller symbols remaining a refinement of caller classes.
12
+ - **Endpoint and validation contracts** now expose test-surface policy, reuse
13
+ servlet declarations, and require real `Validator`/`setValidator` evidence.
14
+ - **Modernization references** now recognize nested types used by their
15
+ enclosing class.
16
+ - **Spring audit output** is bounded by default while explicit full output
17
+ remains available through `--output` and `--compact`.
18
+ - **Repeated command execution** reuses the cached analysis context and reports
19
+ independent cache-layer states for context, parse, snapshot, and RIS.
20
+
21
+ ### Documentation
22
+ - Closed the 2026-08-13 external audit backlog in the roadmap with fix hashes
23
+ and regression coverage.
24
+
25
+ ## [5.5.0] — 2026-08-12
26
+
27
+ **Audit backlog resolved and performance signals made explicit.**
28
+ This release folds in the latest external audit queue: programmatic Spring
29
+ validation now contributes to `validation` gaps and confidence, servlet and
30
+ endpoint publishing preserve extension mappings and `ANY` methods, reference
31
+ classification distinguishes class literals from config wires, and the cache
32
+ model now warns about large repositories against the correct scale. The roadmap
33
+ also records the audit backlog, including the command-execution timing
34
+ regression called out in the report.
35
+
36
+ ### Fixed
37
+ - **Programmatic validation is now surfaced.** `Validator` and `@InitBinder`
38
+ evidence now affect `validation` gaps and overall confidence, and the payload
39
+ carries the non-coverage note for that limit.
40
+ - **Servlet extension mappings stay visible.** Patterns such as `*.ftl` and
41
+ `*.vm` are preserved in the servlet surface instead of being dropped.
42
+ - **Methodless request mappings publish as `ANY`.** `@RequestMapping` without a
43
+ declared method is no longer narrowed to `GET`.
44
+ - **Class literals count as references.** `Foo.class` hits now promote the
45
+ type to `referenced` instead of `unknown_framework_dispatch`.
46
+ - **Large-repository cache advice compares against the right scale.** The size
47
+ warning now tells the operator when the repository exceeds the measured
48
+ reference population.
49
+
50
+ ### Documentation
51
+ - Added the external 2026-08-12 audit queue and its performance backlog to the
52
+ roadmap.
53
+
5
54
  ## [5.4.2] — 2026-08-12
6
55
 
7
56
  **External audit fixes and roadmap alignment.**
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 5.4.2
3
+ Version: 5.5.1
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
42
42
 
43
43
  **Context · Impact · Migration · Architecture · Review — everything from one structural model.**
44
44
 
45
- ![Version](https://img.shields.io/badge/version-5.4.2-blue)
45
+ ![Version](https://img.shields.io/badge/version-5.5.1-blue)
46
46
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
47
47
 
48
48
  > **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
@@ -126,7 +126,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
126
126
  # pip / pipx
127
127
  pipx install sourcecode # or: pip install sourcecode
128
128
 
129
- ask version # ask 5.4.2 — and, on a build that has aged,
129
+ ask version # ask 5.5.1 — and, on a build that has aged,
130
130
  # how many releases have probably shipped since
131
131
  ```
132
132
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  **Context · Impact · Migration · Architecture · Review — everything from one structural model.**
6
6
 
7
- ![Version](https://img.shields.io/badge/version-5.4.2-blue)
7
+ ![Version](https://img.shields.io/badge/version-5.5.1-blue)
8
8
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
9
9
 
10
10
  > **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
@@ -88,7 +88,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
88
88
  # pip / pipx
89
89
  pipx install sourcecode # or: pip install sourcecode
90
90
 
91
- ask version # ask 5.4.2 — and, on a build that has aged,
91
+ ask version # ask 5.5.1 — and, on a build that has aged,
92
92
  # how many releases have probably shipped since
93
93
  ```
94
94
 
@@ -136,7 +136,7 @@ pipx install sourcecode # isolated install, no venv needed
136
136
 
137
137
  # Verify
138
138
  ask version
139
- # ask 5.4.2
139
+ # ask 5.5.1
140
140
  ```
141
141
 
142
142
  Requires Python 3.9+.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "5.4.2"
7
+ version = "5.5.1"
8
8
  description = "Persistent structural context and ultra-fast repeated analysis for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "5.4.2"
7
+ __version__ = "5.5.1"
@@ -492,6 +492,7 @@ class Conditioning:
492
492
  scope: str # "3 342 Java files, cache cold"
493
493
  transfers: "Optional[bool]" # do the reference figures apply here?
494
494
  per_command: "dict[str, str]" # command → how it can be run here
495
+ java_files: "Optional[int]" = None # measured scope size, if available
495
496
 
496
497
  def statement(self) -> str:
497
498
  """One sentence about whether the figures below transfer to this scope."""
@@ -500,6 +501,14 @@ class Conditioning:
500
501
  f"This repository ({self.scope}) was not measured, so nothing below "
501
502
  f"is claimed about it: the figures are the reference repository's."
502
503
  )
504
+ if self.java_files is not None and self.java_files > REFERENCE_JAVA_FILES:
505
+ return (
506
+ f"This repository ({self.scope}) is larger than the repository the "
507
+ f"figures below were measured on ({REFERENCE_JAVA_FILES} Java files), "
508
+ f"and what cost tracks here is the build rather than the file count: "
509
+ f"{FIELD_ANCHOR}. Read each figure as the reference's, not as yours; "
510
+ f"the `here:` lines say how each command can be run on this one."
511
+ )
503
512
  if self.transfers:
504
513
  return (
505
514
  f"This repository ({self.scope}) is within the size the figures "
@@ -27,6 +27,7 @@ from sourcecode.phased_run import PhasedRun
27
27
  from sourcecode.partial_contract import PARTIAL_EXIT_CODE as _PARTIAL_EXIT
28
28
  from sourcecode.partial_contract import read_count as _read_count
29
29
  from sourcecode import perf
30
+ from sourcecode.non_coverage import block as _non_coverage_block
30
31
  from sourcecode.caller_metrics import (
31
32
  CALLER_METRIC_RECONCILIATION,
32
33
  CYCLE_METRIC_RECONCILIATION,
@@ -3456,6 +3457,9 @@ def main(
3456
3457
  _core_hash = ""
3457
3458
  _core_flags_str = ""
3458
3459
  _view_flags_str = ""
3460
+ # Reuse the freshness decision for cache metadata; otherwise the root
3461
+ # command repeats a full Git status walk after the analysis completes.
3462
+ _tree_is_dirty: Optional[bool] = None
3459
3463
 
3460
3464
  if not no_cache:
3461
3465
  try:
@@ -3505,6 +3509,8 @@ def main(
3505
3509
  Path(_git_root_str) if _git_root_str else target,
3506
3510
  scope=target,
3507
3511
  )
3512
+ if _tree_sig and _git_sha:
3513
+ _tree_is_dirty = _tree_sig != _git_sha
3508
3514
  if _tree_sig:
3509
3515
  _core_key = f"{_tree_sig}-{_core_h}"
3510
3516
  else:
@@ -3820,7 +3826,11 @@ def main(
3820
3826
  if format == "json":
3821
3827
  try:
3822
3828
  from sourcecode.ris import _has_uncommitted_changes as _huc
3823
- _uncommitted = _huc(target)
3829
+ _uncommitted = (
3830
+ _tree_is_dirty
3831
+ if _tree_is_dirty is not None
3832
+ else _huc(target)
3833
+ )
3824
3834
  except Exception:
3825
3835
  _uncommitted = False
3826
3836
  _data_scope = "COMPACT" if compact else ("AGENT" if agent else "FULL")
@@ -4716,7 +4726,11 @@ def main(
4716
4726
  if format == "json":
4717
4727
  try:
4718
4728
  from sourcecode.ris import _has_uncommitted_changes as _huc_fresh
4719
- _uncommitted_fresh = _huc_fresh(target)
4729
+ _uncommitted_fresh = (
4730
+ _tree_is_dirty
4731
+ if _tree_is_dirty is not None
4732
+ else _huc_fresh(target)
4733
+ )
4720
4734
  # _huc_fresh uses --untracked-files=no — it misses repos where all
4721
4735
  # files are untracked (no staged/unstaged changes to tracked files).
4722
4736
  # Promote to True when _allowed_changed_files contains untracked files.
@@ -6502,6 +6516,10 @@ def endpoints_cmd(
6502
6516
  "the endpoint count, and the endpoint total is carried across as one "
6503
6517
  "number rather than 3 500 rows.",
6504
6518
  ),
6519
+ include_tests: bool = typer.Option(
6520
+ False, "--include-tests",
6521
+ help="Include endpoints from test source and test-fixture modules; excluded by default.",
6522
+ ),
6505
6523
  client_usage: bool = typer.Option(
6506
6524
  False, "--client-usage",
6507
6525
  help="Join the routes against the HTTP calls written in the TypeScript/"
@@ -6562,7 +6580,7 @@ def endpoints_cmd(
6562
6580
  _prog = Progress()
6563
6581
  _prog.start("scanning endpoints")
6564
6582
  try:
6565
- data = _extract_java_endpoints(target)
6583
+ data = _extract_java_endpoints(target, include_tests=include_tests)
6566
6584
  finally:
6567
6585
  _prog.finish()
6568
6586
 
@@ -7287,6 +7305,23 @@ def validation_cmd(
7287
7305
  except Exception:
7288
7306
  pass
7289
7307
 
7308
+ if data.get("programmatic_validation", {}).get("detected"):
7309
+ data.setdefault("non_coverage", _non_coverage_block("validation"))
7310
+ _vc = data.get("validation_confidence")
7311
+ if isinstance(_vc, dict):
7312
+ _vc["overall"] = "Likely"
7313
+ _vc["claim"] = (
7314
+ "Declared constraints are present, but programmatic Spring "
7315
+ "Validator wiring was detected outside this surface's model"
7316
+ )
7317
+ limits = _vc.setdefault("known_precision_limits", [])
7318
+ note = (
7319
+ "Programmatic Spring Validator / @InitBinder wiring is outside the "
7320
+ "declared-constraint surface."
7321
+ )
7322
+ if note not in limits:
7323
+ limits.append(note)
7324
+
7290
7325
  if path_prefix:
7291
7326
  data["endpoints"] = [
7292
7327
  e for e in data.get("endpoints", [])
@@ -8971,9 +9006,26 @@ def spring_audit_cmd(
8971
9006
  # 9,53 s on a 1 303-file repository) and it is the same parse the other
8972
9007
  # knowledge commands share, so it is fetched rather than rebuilt — which
8973
9008
  # is what makes a warm worth anything here (cold == warm before C3-94).
8974
- cir = _ctxcache.shared_cir(
8975
- target, file_list, progress=_work_sink(_prog)
9009
+ _repo_root = _ctxcache.repo_root_of(target)
9010
+ cir, _cir_lookup = _ctxcache.get_or_build_cir(
9011
+ _repo_root, target, file_list, progress=_work_sink(_prog)
8976
9012
  )
9013
+ _cache_state = {
9014
+ "context": "warm" if _cir_lookup.hit else "cold",
9015
+ "parse": "warm" if _cir_lookup.hit else "cold",
9016
+ }
9017
+ try:
9018
+ from sourcecode import cache as _cache_state_mod
9019
+ _cache_status = _cache_state_mod.status(target)
9020
+ _cache_state["snapshot"] = (
9021
+ "warm" if _cache_status.get("cached_cores") else "cold"
9022
+ )
9023
+ _cache_state["ris"] = (
9024
+ "warm" if _cache_status.get("ris_exists") else "cold"
9025
+ )
9026
+ except Exception:
9027
+ _cache_state["snapshot"] = "unknown"
9028
+ _cache_state["ris"] = "unknown"
8977
9029
  # C3-85: the stretch between the last `linking` tick and the first rule
8978
9030
  # tick reports nothing, and what stays on the line is `n/n` — a counter
8979
9031
  # that finished, reading as a stage still running. Measured on keycloak
@@ -9124,7 +9176,18 @@ def spring_audit_cmd(
9124
9176
  "phase that never ran is not evidence there is none."
9125
9177
  )
9126
9178
 
9127
- data = combined.to_compact_dict() if compact else combined.to_dict()
9179
+ _bounded_default = (
9180
+ not compact and output_path is None and format == "json" and not table
9181
+ )
9182
+ data = (
9183
+ combined.to_compact_dict()
9184
+ if compact or _bounded_default
9185
+ else combined.to_dict()
9186
+ )
9187
+ if _bounded_default:
9188
+ data.pop("mode", None)
9189
+ data["default_output"] = "bounded"
9190
+ data.setdefault("metadata", {})["cache_layers"] = _cache_state
9128
9191
  if _stopped_early:
9129
9192
  # C3-87: the block is composed by `PhasedRun.status`, which is the
9130
9193
  # authority `risk` is now bound to as well — one vocabulary for
@@ -14387,7 +14450,8 @@ def _cache_model_conditioning(path: Path) -> Any:
14387
14450
  line += f" ({found.reference})"
14388
14451
  per_command[row.command] = line
14389
14452
  return _cmodel.Conditioning(
14390
- scope=scope.describe(), transfers=transfers, per_command=per_command
14453
+ scope=scope.describe(), transfers=transfers, per_command=per_command,
14454
+ java_files=scope.java_files,
14391
14455
  )
14392
14456
  except Exception:
14393
14457
  # A cost note is never worth failing a command that answers.
@@ -321,6 +321,27 @@ NON_COVERAGE: tuple[NonCoverage, ...] = (
321
321
  "routes) is a measurement this command does not yet make."
322
322
  ),
323
323
  ),
324
+ NonCoverage(
325
+ id="NC-011",
326
+ surface="validation",
327
+ not_covered=(
328
+ "Programmatic Spring request-body validation wired with "
329
+ "`org.springframework.validation.Validator` or `@InitBinder`."
330
+ ),
331
+ why=(
332
+ "This surface reads declared bean-validation constraints recovered from "
333
+ "OpenAPI or DTO annotations. A validator registered in controller code "
334
+ "can enforce body rules without any declarative constraint on the "
335
+ "payload, so treating the result as fully verified would overstate what "
336
+ "the declared-constraint scan proves."
337
+ ),
338
+ instead=(
339
+ "`@InitBinder` / `setValidator(...)` wiring, or a Spring `Validator` "
340
+ "implementation, which this surface reports only as a coverage "
341
+ "boundary. `validation` still shows the declared rules; the programmatic "
342
+ "validator remains outside that model."
343
+ ),
344
+ ),
324
345
  )
325
346
 
326
347
 
@@ -162,7 +162,7 @@ _QUALIFIED_TYPE_RE = re.compile(r"\b[A-Z][A-Za-z0-9_$]*\.[A-Z][A-Za-z0-9_$]*")
162
162
 
163
163
  def _scan_repository(
164
164
  root: Path, simple_names: "set[str]", nested: "dict[str, tuple[str, str]]"
165
- ) -> "tuple[dict[str, str], dict[str, str]]":
165
+ ) -> "tuple[dict[str, str], dict[str, str], dict[str, str]]":
166
166
  """One walk, two questions — `(config-wired names, nested types other code names)`.
167
167
 
168
168
  Both scans need the whole tree, and walking it twice doubled the cost of the
@@ -175,6 +175,7 @@ def _scan_repository(
175
175
  """
176
176
  config_hits: dict[str, str] = {}
177
177
  nested_hits: dict[str, str] = {}
178
+ literal_hits: dict[str, str] = {}
178
179
  pending = set(simple_names)
179
180
  by_qualified: dict[str, list[str]] = {}
180
181
  for fqn, (qualified, _own) in nested.items():
@@ -183,7 +184,7 @@ def _scan_repository(
183
184
  scan_config = bool(pending)
184
185
  scan_java = bool(by_qualified)
185
186
  if not scan_config and not scan_java:
186
- return config_hits, nested_hits
187
+ return config_hits, nested_hits, literal_hits
187
188
 
188
189
  scanned = 0
189
190
  for dirpath, dirnames, filenames in os.walk(root):
@@ -191,7 +192,7 @@ def _scan_repository(
191
192
  for filename in filenames:
192
193
  is_java = filename.endswith(".java")
193
194
  is_config = os.path.splitext(filename)[1].lower() in _CONFIG_REF_EXTS
194
- if not ((is_java and scan_java) or (is_config and scan_config)):
195
+ if not ((is_java and (scan_java or scan_config)) or (is_config and scan_config)):
195
196
  continue
196
197
  if scanned >= _SCAN_MAX_FILES:
197
198
  break
@@ -206,17 +207,40 @@ def _scan_repository(
206
207
  pending.discard(hit)
207
208
  config_hits[hit] = rel
208
209
  scan_config = bool(pending)
210
+ if is_java and scan_config:
211
+ # A `Foo.class` literal is a real static reference, and the
212
+ # field audit found one of these on the annotation-dispatch path.
213
+ class_hits = {
214
+ hit for hit in pending
215
+ if f"{hit}.class" in text
216
+ }
217
+ for hit in class_hits:
218
+ pending.discard(hit)
219
+ literal_hits[hit] = rel
220
+ scan_config = bool(pending)
209
221
  if is_java and scan_java:
210
222
  for hit in set(_QUALIFIED_TYPE_RE.findall(text)) & set(by_qualified):
211
223
  for fqn in by_qualified[hit]:
212
224
  if fqn not in nested_hits and nested[fqn][1] != rel:
213
225
  nested_hits[fqn] = rel
226
+ # Nested types are often used unqualified by their enclosing
227
+ # class (`private Operation operation` or `Map<String, DATATYPE>`).
228
+ # The declaration is not a reference; a second occurrence is.
229
+ for fqn, (qualified, owner_file) in nested.items():
230
+ if owner_file != rel or fqn in nested_hits:
231
+ continue
232
+ nested_name = qualified.rsplit(".", 1)[-1]
233
+ unqualified = re.findall(
234
+ rf"(?<!\.)\b{re.escape(nested_name)}\b", text
235
+ )
236
+ if len(unqualified) > 1:
237
+ nested_hits[fqn] = rel
214
238
  scan_java = len(nested_hits) < len(nested)
215
239
  if not scan_config and not scan_java:
216
- return config_hits, nested_hits
240
+ return config_hits, nested_hits, literal_hits
217
241
  if scanned >= _SCAN_MAX_FILES:
218
242
  break
219
- return config_hits, nested_hits
243
+ return config_hits, nested_hits, literal_hits
220
244
 
221
245
 
222
246
  def analyze_type_references(nodes: "list[dict]", root: Path) -> ReferenceFacts:
@@ -226,7 +250,15 @@ def analyze_type_references(nodes: "list[dict]", root: Path) -> ReferenceFacts:
226
250
  examined, because a method with no callers is a different question.
227
251
  """
228
252
  root = Path(root)
229
- types = [n for n in nodes or [] if n.get("type") in ("class", "interface") and n.get("fqn")]
253
+ def _node_kind(node: dict) -> str:
254
+ kind = str(node.get("symbol_kind") or node.get("kind") or node.get("type") or "")
255
+ if kind in ("class", "interface", "enum", "annotation", "record"):
256
+ return kind
257
+ if kind == "@interface":
258
+ return "annotation"
259
+ return str(node.get("type") or "")
260
+
261
+ types = [n for n in nodes or [] if _node_kind(n) in ("class", "interface", "enum", "annotation", "record") and n.get("fqn")]
230
262
  if not types:
231
263
  return ReferenceFacts(0, {s: 0 for s in STATUSES}, ())
232
264
 
@@ -237,7 +269,7 @@ def analyze_type_references(nodes: "list[dict]", root: Path) -> ReferenceFacts:
237
269
  entries.append(TypeReference(
238
270
  fqn=str(node["fqn"]), status=REFERENCED,
239
271
  basis="incoming edge in the call-graph",
240
- type=str(node.get("type") or ""), role=str(node.get("role") or "other"),
272
+ type=_node_kind(node), role=str(node.get("role") or "other"),
241
273
  ))
242
274
  else:
243
275
  candidates.append(node)
@@ -271,22 +303,30 @@ def analyze_type_references(nodes: "list[dict]", root: Path) -> ReferenceFacts:
271
303
  f"{parts[-2]}.{parts[-1]}",
272
304
  str(node.get("source_file") or "").replace("\\", "/"),
273
305
  )
274
- config_hits, nested_hits = _scan_repository(root, set(by_simple), nested_candidates)
306
+ config_hits, nested_hits, literal_hits = _scan_repository(root, set(by_simple), nested_candidates)
307
+ literal_refs: dict[str, str] = {}
275
308
  for simple, config_file in config_hits.items():
276
309
  for node in by_simple.get(simple, []):
277
310
  dispatch_basis[str(node["fqn"])] = f"named in {config_file}"
311
+ for simple, literal_file in literal_hits.items():
312
+ for node in by_simple.get(simple, []):
313
+ literal_refs[str(node["fqn"])] = literal_file
278
314
 
279
315
  for node in candidates:
280
316
  fqn = str(node["fqn"])
281
317
  common = {
282
318
  "fqn": fqn,
283
- "type": str(node.get("type") or ""),
319
+ "type": _node_kind(node),
284
320
  "role": str(node.get("role") or "other"),
285
321
  }
286
- if fqn in nested_hits:
322
+ if fqn in nested_hits or fqn in literal_refs:
287
323
  entries.append(TypeReference(
288
324
  status=REFERENCED,
289
- basis=f"named as a qualified nested type in {nested_hits[fqn]}",
325
+ basis=(
326
+ f"referenced by its enclosing source in {nested_hits[fqn]}"
327
+ if fqn in nested_hits
328
+ else f"referenced by a class literal in {literal_refs[fqn]}"
329
+ ),
290
330
  **common,
291
331
  ))
292
332
  elif fqn in dispatch_basis:
@@ -27,7 +27,7 @@ from typing import Any, Optional
27
27
  #: The date `__version__` was released, from that version's CHANGELOG entry.
28
28
  #: `tests/test_release_info.py` fails the build if the two disagree, so this is
29
29
  #: a copy of a fact rather than a second authority for it.
30
- RELEASE_DATE = "2026-08-12"
30
+ RELEASE_DATE = "2026-08-13"
31
31
 
32
32
  #: Releases per day, measured over the 30 releases before this one (the same
33
33
  #: computation the test re-runs against the CHANGELOG). Stable across window
@@ -3778,7 +3778,11 @@ def _parse_route_http_method(ann_name: str, args_str: str) -> str:
3778
3778
  if explicit:
3779
3779
  return explicit
3780
3780
  m = re.search(r'method\s*=\s*(?:RequestMethod\.)?(\w+)', args_str or "")
3781
- return m.group(1).upper() if m else ""
3781
+ if m:
3782
+ return m.group(1).upper()
3783
+ if ann_name == "@RequestMapping":
3784
+ return "ANY"
3785
+ return ""
3782
3786
 
3783
3787
 
3784
3788
  def _parse_route_extras(args_str: str) -> dict:
@@ -6358,10 +6362,13 @@ def is_test_source_file(rel_path: str) -> bool:
6358
6362
  # Convenience: find Java files in a repo
6359
6363
  # ---------------------------------------------------------------------------
6360
6364
 
6361
- def extract_java_endpoints(root: Path) -> "dict[str, Any]":
6365
+ def extract_java_endpoints(
6366
+ root: Path, *, include_tests: bool = False
6367
+ ) -> "dict[str, Any]":
6362
6368
  """Extract REST endpoint surface from Java source files.
6363
6369
 
6364
6370
  Canonical endpoint extractor — uses IR symbol extraction + _build_route_surface().
6371
+ Test-fixture modules remain auditable by default and can be included explicitly.
6365
6372
  Security extraction delegated to _route_security_from_sym (single source of truth);
6366
6373
  results stored in route["security_annotations"] by _build_route_surface.
6367
6374
 
@@ -6670,7 +6677,8 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
6670
6677
  # counted with, the real API surface — but they are preserved in their own bucket
6671
6678
  # so nothing is silently dropped and the exclusion is auditable.
6672
6679
  _test_util_endpoints = [e for e in endpoints if e.get("scope") == "test_util"]
6673
- endpoints = [e for e in endpoints if e.get("scope") != "test_util"]
6680
+ if not include_tests:
6681
+ endpoints = [e for e in endpoints if e.get("scope") != "test_util"]
6674
6682
 
6675
6683
  # "no_security_signal" = no recognized security annotation at method OR class level.
6676
6684
  # Note: repos may use framework-level security (e.g. Keycloak itself) with no
@@ -6894,7 +6902,7 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
6894
6902
  pass
6895
6903
  # Surface test-fixture endpoints excluded from the production surface above.
6896
6904
  # Reported separately (not hidden) so the exclusion is transparent and auditable.
6897
- if _test_util_endpoints:
6905
+ if _test_util_endpoints and not include_tests:
6898
6906
  result["test_util_endpoints"] = _test_util_endpoints
6899
6907
  result["test_util_excluded"] = len(_test_util_endpoints)
6900
6908
  result.setdefault("notes", []).append(
@@ -6902,6 +6910,8 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
6902
6910
  f"module(s) under src/main were excluded from the production surface "
6903
6911
  f"(see test_util_endpoints)."
6904
6912
  )
6913
+ elif _test_util_endpoints:
6914
+ result["test_util_included"] = len(_test_util_endpoints)
6905
6915
  # Surface incomplete-endpoint warnings (interface-defined controllers) only when
6906
6916
  # present, to keep output backward-compatible for the common case.
6907
6917
  if endpoint_warnings:
@@ -6960,6 +6970,17 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
6960
6970
  if any(r.get("framework") == "jax_rs" for r in routes):
6961
6971
  _nonspring.pop("jax_rs", None)
6962
6972
 
6973
+ # Servlet mappings are a URL declaration population, not a count of
6974
+ # HttpServlet classes. Reuse the servlet extractor so `endpoints` and
6975
+ # `endpoints --servlets` publish the same authoritative census.
6976
+ try:
6977
+ from sourcecode.servlet_surface import build_servlet_surface
6978
+
6979
+ _nonspring["servlets"] = int(
6980
+ build_servlet_surface(root).get("declarations", 0)
6981
+ )
6982
+ except Exception:
6983
+ pass
6963
6984
  _nonspring_total = sum(_nonspring.values())
6964
6985
  if _nonspring_total:
6965
6986
  _frameworks = [k for k, v in _nonspring.items() if v]
@@ -7260,6 +7281,23 @@ def compute_blast_radius(
7260
7281
  # class-level node traversable at all (C1-16), and rebuilding it per node
7261
7282
  # would make the walk quadratic in the graph.
7262
7283
  _rg_index = _class_method_index(reverse_graph)
7284
+
7285
+ def _walk_callers(_node: str) -> list[str]:
7286
+ """Expand method frontiers through their owning class as well.
7287
+
7288
+ Service-locator accessors enter the frontier as ``Class#accessor`` while
7289
+ callers of the class are indexed under its method keys. Keeping the
7290
+ exact lookup preserves method-level reach; adding the enclosing class
7291
+ lookup prevents that frontier from becoming a false terminal node.
7292
+ """
7293
+ candidates = list(_all_callers_from_rg(
7294
+ _node, reverse_graph, _rg_index, _impl_graph
7295
+ ))
7296
+ if "#" in _node:
7297
+ candidates.extend(_all_callers_from_rg(
7298
+ _enclosing_class(_node), reverse_graph, _rg_index, _impl_graph
7299
+ ))
7300
+ return list(dict.fromkeys(candidates))
7263
7301
  # Callers inject the interface, not the implementation, so a walk that does not
7264
7302
  # know the implements relation stops one hop short of every DI consumer. Without
7265
7303
  # it this traversal reached 200 classes and **no controller at all** for
@@ -7294,14 +7332,14 @@ def compute_blast_radius(
7294
7332
  _expanded.add(_sym)
7295
7333
  matched_fqns = _expanded
7296
7334
  for seed in matched_fqns:
7297
- _seed_callers = _all_callers_from_rg(seed, reverse_graph, _rg_index, _impl_graph)
7335
+ _seed_callers = _walk_callers(seed)
7298
7336
  if len(_seed_callers) > _HUB_CALLER_THRESHOLD and max_depth > 1:
7299
7337
  _effective_depth = 1
7300
7338
  _hub_class_guard = True
7301
7339
  break
7302
7340
 
7303
7341
  for seed in matched_fqns:
7304
- callers = _all_callers_from_rg(seed, reverse_graph, _rg_index, _impl_graph)
7342
+ callers = _walk_callers(seed)
7305
7343
  for c in callers:
7306
7344
  if c not in all_affected:
7307
7345
  all_affected[c] = 1
@@ -7387,7 +7425,7 @@ def compute_blast_radius(
7387
7425
  node, depth = queue.pop(0)
7388
7426
  if depth >= _effective_depth:
7389
7427
  continue
7390
- for caller in _all_callers_from_rg(node, reverse_graph, _rg_index, _impl_graph):
7428
+ for caller in _walk_callers(node):
7391
7429
  if caller not in visited:
7392
7430
  visited.add(caller)
7393
7431
  all_affected[caller] = depth + 1
@@ -7410,7 +7448,7 @@ def compute_blast_radius(
7410
7448
  _snode, _sdepth = _sample_queue.pop(0)
7411
7449
  if _sdepth >= _HUB_SAMPLE_DEPTH:
7412
7450
  continue
7413
- for _scaller in _all_callers_from_rg(_snode, reverse_graph, _rg_index, _impl_graph):
7451
+ for _scaller in _walk_callers(_snode):
7414
7452
  if _scaller not in _sample_visited:
7415
7453
  _sample_visited.add(_scaller)
7416
7454
  all_affected[_scaller] = _sdepth + 1
@@ -7611,7 +7649,12 @@ def compute_blast_radius(
7611
7649
  _direct_caller_classes = sorted(
7612
7650
  caller_classes(direct_callers, exclude=matched_fqns)
7613
7651
  )
7614
- n_direct_symbols = len(direct_callers)
7652
+ _direct_class_set = set(_direct_caller_classes)
7653
+ _direct_caller_symbols = sorted(
7654
+ symbol for symbol in direct_callers
7655
+ if _enclosing_class(symbol) in _direct_class_set
7656
+ )
7657
+ n_direct_symbols = len(_direct_caller_symbols)
7615
7658
  n_direct_classes = len(_direct_caller_classes)
7616
7659
  n_direct = n_direct_symbols
7617
7660
  n_indirect = len(indirect_callers)
@@ -7823,8 +7866,8 @@ def compute_blast_radius(
7823
7866
  else _direct_caller_classes[:_direct_cap]
7824
7867
  ),
7825
7868
  "direct_caller_symbols": (
7826
- sorted(direct_callers) if _direct_cap is None
7827
- else sorted(direct_callers)[:_direct_cap]
7869
+ _direct_caller_symbols if _direct_cap is None
7870
+ else _direct_caller_symbols[:_direct_cap]
7828
7871
  ),
7829
7872
  "indirect_callers": _indirect_summary,
7830
7873
  "container_wired": [w.to_dict() for w in _container_wired],