sourcecode 5.5.4__tar.gz → 5.5.6__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 (357) hide show
  1. {sourcecode-5.5.4 → sourcecode-5.5.6}/CHANGELOG.md +39 -0
  2. {sourcecode-5.5.4 → sourcecode-5.5.6}/PKG-INFO +3 -3
  3. {sourcecode-5.5.4 → sourcecode-5.5.6}/README.md +2 -2
  4. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/DEFECT-LEDGER.md +5 -1
  5. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/USER_GUIDE.md +1 -1
  6. {sourcecode-5.5.4 → sourcecode-5.5.6}/pyproject.toml +1 -1
  7. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/__init__.py +1 -1
  8. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/cli.py +38 -18
  9. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/repository_ir.py +48 -17
  10. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/security_config_scan.py +7 -1
  11. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/servlet_surface.py +65 -28
  12. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/timeline.py +6 -1
  13. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/validation_surface.py +9 -0
  14. {sourcecode-5.5.4 → sourcecode-5.5.6}/.github/workflows/build-windows.yml +0 -0
  15. {sourcecode-5.5.4 → sourcecode-5.5.6}/.github/workflows/perf-gate.yml +0 -0
  16. {sourcecode-5.5.4 → sourcecode-5.5.6}/.gitignore +0 -0
  17. {sourcecode-5.5.4 → sourcecode-5.5.6}/.ruff.toml +0 -0
  18. {sourcecode-5.5.4 → sourcecode-5.5.6}/CLAUDE.md +0 -0
  19. {sourcecode-5.5.4 → sourcecode-5.5.6}/CONTRIBUTING.md +0 -0
  20. {sourcecode-5.5.4 → sourcecode-5.5.6}/LICENSE +0 -0
  21. {sourcecode-5.5.4 → sourcecode-5.5.6}/SECURITY.md +0 -0
  22. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/CACHE.md +0 -0
  23. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/COMMERCIAL-OFFER.md +0 -0
  24. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/CUSTOMER-VALIDATION.md +0 -0
  25. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/DEMO-5MIN.md +0 -0
  26. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/DEVELOPMENT-ROADMAP.md +0 -0
  27. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/EXECUTION-PLAN-12MO.md +0 -0
  28. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/EXECUTIVE-ROADMAP.md +0 -0
  29. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/MANUAL-USUARIO.md +0 -0
  30. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/MODERNIZATION.md +0 -0
  31. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/PRODUCT_AUDIT.md +0 -0
  32. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/PRODUCT_IDENTITY.md +0 -0
  33. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/PRODUCT_TIERS.md +0 -0
  34. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/RC1-CHECKLIST.md +0 -0
  35. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/RELEASE-CHECKLIST.md +0 -0
  36. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/CATALOG-retrieval-intents.md +0 -0
  37. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/DESIGN-knowledge-provider-integration.md +0 -0
  38. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/DESIGN-p5.3b-statement-surface.md +0 -0
  39. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/DESIGN-p5.5-span-index.md +0 -0
  40. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/DESIGN-p6-semantic-impact-engine.md +0 -0
  41. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/DESIGN-p6-semantic-integration-engine.md +0 -0
  42. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/DESIGN-p7-semantic-services.md +0 -0
  43. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/DESIGN-robustness-security-inference.md +0 -0
  44. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/DESIGN-semantic-retrieval.md +0 -0
  45. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/EPV-p6-class-typeref.md +0 -0
  46. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/REPORT-p6-flow-analyzer-comparison.md +0 -0
  47. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/REPORT-p6-integration-detector-comparison.md +0 -0
  48. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/REPORT-p8-flow-analyzer-migration.md +0 -0
  49. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/REPORT-p9-integration-detector-migration.md +0 -0
  50. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/adr/0001-semantic-ir.md +0 -0
  51. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/adr/0002-source-evidence-boundary.md +0 -0
  52. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/adr/0003-architectural-characterization.md +0 -0
  53. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/adr/0004-semantic-retrieval.md +0 -0
  54. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/adr/0005-evidence-reconciliation.md +0 -0
  55. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/adr/0006-performance-harness.md +0 -0
  56. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/adr/0007-composition-semantics.md +0 -0
  57. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/adr/0008-answer-coherence.md +0 -0
  58. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/consumer-disposition-inventory.md +0 -0
  59. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/overview.md +0 -0
  60. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/product-architecture-review.md +0 -0
  61. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/product-strategy-layering.md +0 -0
  62. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/semantic-ir-consumer-matrix.md +0 -0
  63. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/architecture/semantic-ir-roadmap.md +0 -0
  64. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/baseline-ci.yml +0 -0
  65. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/commercial-pipeline-audit.md +0 -0
  66. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/contracts-ci.yml +0 -0
  67. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/contracts.md +0 -0
  68. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/eval/2026-06-15-petclinic-rest-issue11-vets-pagination.md +0 -0
  69. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/eval/2026-06-15-petclinic-rest-issue147-validation.md +0 -0
  70. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/eval/2026-06-15-petclinic-rest-workflow.md +0 -0
  71. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/eval/2026-06-15-phase18-openapi-surface.md +0 -0
  72. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/eval/2026-06-15-phase20-validation-surface.md +0 -0
  73. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/eval/2026-06-16-broadleaf-checkout-impact-fieldtest.md +0 -0
  74. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/eval/2026-06-16-petclinic-rest-issue11-weakness2-closed.md +0 -0
  75. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/eval/2026-06-16-spring-petclinic-issue2333-dead-vets.md +0 -0
  76. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/licensing-schema.md +0 -0
  77. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/migrate-check.md +0 -0
  78. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/README.md +0 -0
  79. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/REGRESSION-GATE.md +0 -0
  80. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/SUMMARY.md +0 -0
  81. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/alfresco__ask__cold.json +0 -0
  82. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/alfresco__ask__warm.json +0 -0
  83. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/broadleaf__ask__cold.json +0 -0
  84. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/broadleaf__ask__warm.json +0 -0
  85. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/eureka__ask__cold.json +0 -0
  86. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/eureka__ask__warm.json +0 -0
  87. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/jenkins__ask__cold.json +0 -0
  88. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/jenkins__ask__warm.json +0 -0
  89. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/jobrunr__ask__cold.json +0 -0
  90. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/jobrunr__ask__warm.json +0 -0
  91. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/keycloak__ask__cold.json +0 -0
  92. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/keycloak__ask__warm.json +0 -0
  93. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/neo4j__ask__cold.json +0 -0
  94. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/neo4j__ask__warm.json +0 -0
  95. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/ofbiz__ask__cold.json +0 -0
  96. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/ofbiz__ask__warm.json +0 -0
  97. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/openmrs__ask__cold.json +0 -0
  98. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/openmrs__ask__warm.json +0 -0
  99. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/petclinic__ask__cold.json +0 -0
  100. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.15/petclinic__ask__warm.json +0 -0
  101. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/SUMMARY.md +0 -0
  102. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/alfresco__ask__cold.json +0 -0
  103. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/alfresco__ask__warm.json +0 -0
  104. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/broadleaf__ask__cold.json +0 -0
  105. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/broadleaf__ask__warm.json +0 -0
  106. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/eureka__ask__cold.json +0 -0
  107. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/eureka__ask__warm.json +0 -0
  108. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/jenkins__ask__cold.json +0 -0
  109. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/jenkins__ask__warm.json +0 -0
  110. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/jobrunr__ask__cold.json +0 -0
  111. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/jobrunr__ask__warm.json +0 -0
  112. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/keycloak__ask__cold.json +0 -0
  113. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/keycloak__ask__warm.json +0 -0
  114. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/neo4j__ask__cold.json +0 -0
  115. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/neo4j__ask__warm.json +0 -0
  116. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/ofbiz__ask__cold.json +0 -0
  117. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/ofbiz__ask__warm.json +0 -0
  118. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/openmrs__ask__cold.json +0 -0
  119. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/openmrs__ask__warm.json +0 -0
  120. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/petclinic__ask__cold.json +0 -0
  121. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/2.5.16/petclinic__ask__warm.json +0 -0
  122. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/gate-latest/README.md +0 -0
  123. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/gate-latest/broadleaf__ask__warm.json +0 -0
  124. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/gate-latest/broadleaf__endpoints__warm.json +0 -0
  125. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/gate-latest/broadleaf__migrate-check__warm.json +0 -0
  126. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/gate-latest/broadleaf__posture__warm.json +0 -0
  127. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/gate-latest/broadleaf__spring-audit__warm.json +0 -0
  128. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/gate-latest/broadleaf__validation__warm.json +0 -0
  129. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/perf/baselines/reference-best/README.md +0 -0
  130. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/posture.md +0 -0
  131. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/privacy.md +0 -0
  132. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/pro-experience-audit.md +0 -0
  133. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/schema.md +0 -0
  134. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/spec/envelope-v1.md +0 -0
  135. {sourcecode-5.5.4 → sourcecode-5.5.6}/docs/spring-audit-ci.yml +0 -0
  136. {sourcecode-5.5.4 → sourcecode-5.5.6}/raw +0 -0
  137. {sourcecode-5.5.4 → sourcecode-5.5.6}/scripts/compare_integration_engines.py +0 -0
  138. {sourcecode-5.5.4 → sourcecode-5.5.6}/scripts/customer_smoke_test.sh +0 -0
  139. {sourcecode-5.5.4 → sourcecode-5.5.6}/scripts/generate_jdk_exports.py +0 -0
  140. {sourcecode-5.5.4 → sourcecode-5.5.6}/scripts/perf_gate.py +0 -0
  141. {sourcecode-5.5.4 → sourcecode-5.5.6}/scripts/perf_harness.py +0 -0
  142. {sourcecode-5.5.4 → sourcecode-5.5.6}/scripts/sync_gate_anchors.py +0 -0
  143. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/adaptive_scanner.py +0 -0
  144. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/archetype.py +0 -0
  145. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/architectural_baseline.py +0 -0
  146. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/architectural_delta.py +0 -0
  147. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/architecture_analyzer.py +0 -0
  148. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/architecture_summary.py +0 -0
  149. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/ast_extractor.py +0 -0
  150. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/audit_report.py +0 -0
  151. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/baseline_autocapture.py +0 -0
  152. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/bundled_docs.py +0 -0
  153. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/cache.py +0 -0
  154. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/cache_model.py +0 -0
  155. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/call_surface.py +0 -0
  156. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/caller_metrics.py +0 -0
  157. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/caller_reach.py +0 -0
  158. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/canonical_ir.py +0 -0
  159. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/chain_rules.py +0 -0
  160. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/change_plan.py +0 -0
  161. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/cir_graphs.py +0 -0
  162. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/classifier.py +0 -0
  163. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/client_calls.py +0 -0
  164. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/code_notes_analyzer.py +0 -0
  165. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/compare.py +0 -0
  166. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/confidence_analyzer.py +0 -0
  167. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/constraint_diff.py +0 -0
  168. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/consumer_join.py +0 -0
  169. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/container_wiring.py +0 -0
  170. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/context_cache.py +0 -0
  171. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/context_graph.py +0 -0
  172. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/context_scorer.py +0 -0
  173. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/context_summarizer.py +0 -0
  174. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/contract_diff.py +0 -0
  175. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/contract_init.py +0 -0
  176. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/contract_model.py +0 -0
  177. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/contract_pipeline.py +0 -0
  178. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/coverage_parser.py +0 -0
  179. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/data_exposure.py +0 -0
  180. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/data_labels.py +0 -0
  181. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/declarations.py +0 -0
  182. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/defect_identity.py +0 -0
  183. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/degradation.py +0 -0
  184. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/dependency_analyzer.py +0 -0
  185. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/deployment_prefix.py +0 -0
  186. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detach.py +0 -0
  187. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/__init__.py +0 -0
  188. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/base.py +0 -0
  189. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/csproj_parser.py +0 -0
  190. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/dart.py +0 -0
  191. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/dotnet.py +0 -0
  192. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/elixir.py +0 -0
  193. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/go.py +0 -0
  194. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/heuristic.py +0 -0
  195. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/hybrid.py +0 -0
  196. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/java.py +0 -0
  197. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/jvm_ext.py +0 -0
  198. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/nodejs.py +0 -0
  199. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/parsers.py +0 -0
  200. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/php.py +0 -0
  201. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/project.py +0 -0
  202. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/python.py +0 -0
  203. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/ruby.py +0 -0
  204. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/rust.py +0 -0
  205. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/systems.py +0 -0
  206. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/terraform.py +0 -0
  207. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/detectors/tooling.py +0 -0
  208. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/doc_analyzer.py +0 -0
  209. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/dynamic_argument_surface.py +0 -0
  210. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/endpoint_literals.py +0 -0
  211. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/endpoint_metrics.py +0 -0
  212. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/entrypoint_classifier.py +0 -0
  213. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/env_analyzer.py +0 -0
  214. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/envelope.py +0 -0
  215. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/environment_resolution.py +0 -0
  216. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/error_schema.py +0 -0
  217. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/evidence_provider.py +0 -0
  218. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/execution_plan.py +0 -0
  219. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/explain.py +0 -0
  220. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/facts/__init__.py +0 -0
  221. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/facts/registry.json +0 -0
  222. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/file_chunker.py +0 -0
  223. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/file_classifier.py +0 -0
  224. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/filter_surface.py +0 -0
  225. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/format_contract.py +0 -0
  226. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/fqn_utils.py +0 -0
  227. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/gate_anchors.py +0 -0
  228. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/git_analyzer.py +0 -0
  229. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/git_checkout.py +0 -0
  230. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/graph_analyzer.py +0 -0
  231. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/graph_evidence.py +0 -0
  232. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/hibernate_strat.py +0 -0
  233. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/identity_fallback.py +0 -0
  234. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/integration_coordinates.py +0 -0
  235. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/jdk_exports.py +0 -0
  236. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/license.py +0 -0
  237. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/manifest_cache.py +0 -0
  238. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/__init__.py +0 -0
  239. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/dependency_import.py +0 -0
  240. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  241. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  242. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  243. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  244. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  245. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/orchestrator.py +0 -0
  246. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/registry.py +0 -0
  247. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/runner.py +0 -0
  248. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp/server.py +0 -0
  249. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/mcp_nudge.py +0 -0
  250. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/metrics_analyzer.py +0 -0
  251. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/migrate_check.py +0 -0
  252. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/migration_blast.py +0 -0
  253. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/non_coverage.py +0 -0
  254. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/openapi_surface.py +0 -0
  255. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/openrewrite_recipe.py +0 -0
  256. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/output_budget.py +0 -0
  257. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/output_encoding.py +0 -0
  258. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/parallel.py +0 -0
  259. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/parse_cache.py +0 -0
  260. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/partial_contract.py +0 -0
  261. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/path_admission.py +0 -0
  262. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/path_filters.py +0 -0
  263. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/perf.py +0 -0
  264. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/phased_run.py +0 -0
  265. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/pipe_contract.py +0 -0
  266. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/posture.py +0 -0
  267. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/pr_comment_renderer.py +0 -0
  268. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/pr_impact.py +0 -0
  269. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/prepare_context.py +0 -0
  270. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/progress.py +0 -0
  271. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/provenance.py +0 -0
  272. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/ranking_engine.py +0 -0
  273. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/readiness_timeline.py +0 -0
  274. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/readonly.py +0 -0
  275. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/reconciliation.py +0 -0
  276. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/redactor.py +0 -0
  277. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/reference_facts.py +0 -0
  278. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/release_info.py +0 -0
  279. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/relevance_scorer.py +0 -0
  280. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/remedies.py +0 -0
  281. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/rename_refactor.py +0 -0
  282. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/repo_classifier.py +0 -0
  283. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/__init__.py +0 -0
  284. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/context.py +0 -0
  285. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/errors.py +0 -0
  286. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/executor.py +0 -0
  287. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/planner.py +0 -0
  288. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/query.py +0 -0
  289. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/request.py +0 -0
  290. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/resolution.py +0 -0
  291. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/result.py +0 -0
  292. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/retriever.py +0 -0
  293. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/runtime.py +0 -0
  294. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/steps.py +0 -0
  295. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  296. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/steps_graph.py +0 -0
  297. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/steps_impact.py +0 -0
  298. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/steps_intf.py +0 -0
  299. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/steps_struct.py +0 -0
  300. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  301. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/ris.py +0 -0
  302. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/risk.py +0 -0
  303. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/rule_catalog.py +0 -0
  304. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/rule_pass.py +0 -0
  305. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/runs.py +0 -0
  306. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/runtime_classifier.py +0 -0
  307. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/sarif.py +0 -0
  308. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/scanner.py +0 -0
  309. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/schema.py +0 -0
  310. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  311. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/security_chain.py +0 -0
  312. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/security_config.py +0 -0
  313. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/security_posture.py +0 -0
  314. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/semantic_analyzer.py +0 -0
  315. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/semantic_impact_engine.py +0 -0
  316. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/semantic_integration_engine.py +0 -0
  317. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/semantic_services.py +0 -0
  318. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/serializer.py +0 -0
  319. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/source_text.py +0 -0
  320. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/spring_event_topology.py +0 -0
  321. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/spring_findings.py +0 -0
  322. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/spring_impact.py +0 -0
  323. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/spring_model.py +0 -0
  324. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/spring_profiles.py +0 -0
  325. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/spring_properties.py +0 -0
  326. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/spring_security_audit.py +0 -0
  327. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/spring_semantic.py +0 -0
  328. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/spring_tx_analyzer.py +0 -0
  329. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/sql_taint.py +0 -0
  330. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/summarizer.py +0 -0
  331. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/target_admission.py +0 -0
  332. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/telemetry/__init__.py +0 -0
  333. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/telemetry/config.py +0 -0
  334. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/telemetry/consent.py +0 -0
  335. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/telemetry/events.py +0 -0
  336. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/telemetry/filters.py +0 -0
  337. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/telemetry/transport.py +0 -0
  338. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/test_gap_ranking.py +0 -0
  339. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/test_sources.py +0 -0
  340. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/text_input.py +0 -0
  341. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/token_estimate.py +0 -0
  342. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/tree_utils.py +0 -0
  343. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/type_usage_surface.py +0 -0
  344. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/validation_inference.py +0 -0
  345. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/verify_edit.py +0 -0
  346. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/verify_repo.py +0 -0
  347. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/verify_rules.py +0 -0
  348. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/version_check.py +0 -0
  349. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/waivers.py +0 -0
  350. {sourcecode-5.5.4 → sourcecode-5.5.6}/src/sourcecode/workspace.py +0 -0
  351. {sourcecode-5.5.4 → sourcecode-5.5.6}/supabase/functions/README.md +0 -0
  352. {sourcecode-5.5.4 → sourcecode-5.5.6}/supabase/functions/get-license/index.ts +0 -0
  353. {sourcecode-5.5.4 → sourcecode-5.5.6}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  354. {sourcecode-5.5.4 → sourcecode-5.5.6}/supabase/functions/telemetry/index.ts +0 -0
  355. {sourcecode-5.5.4 → sourcecode-5.5.6}/supabase/sql/license_event_ordering.sql +0 -0
  356. {sourcecode-5.5.4 → sourcecode-5.5.6}/supabase/sql/licensing_schema.sql +0 -0
  357. {sourcecode-5.5.4 → sourcecode-5.5.6}/supabase/sql/telemetry_events.sql +0 -0
@@ -2,6 +2,45 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.5.6] — 2026-08-13
6
+
7
+ ### Fixed
8
+ - `impact` now projects from the canonical IR, restoring typed static-method
9
+ callers without merging unrelated service-locator accessors.
10
+ - Legacy servlet descriptors account for mappings whose servlet declaration is
11
+ unavailable, and timeline Git metadata is decoded as UTF-8.
12
+ - Class FQNs remain class FQNs when used as impact seeds.
13
+
14
+ ### Documentation
15
+ - Recorded the 5.5.5 re-audit balance, cache-measurement retractions, fixes,
16
+ and remaining output-budget, schema, timeline-cost, Git-basis and release
17
+ ledger work.
18
+
19
+ ## [5.5.5] — 2026-08-13
20
+
21
+ **MSAS performance-audit corrections and read-only hygiene verification.**
22
+
23
+ ### Fixed
24
+ - `spring-audit` now preserves full default JSON when it fits the stdout
25
+ budget; only oversized answers are bounded, and the notice points to
26
+ `--output` for the complete report.
27
+ - SEC-004 ignores import-only deprecated encoders and reports concrete
28
+ construction or factory evidence instead.
29
+ - `pr-impact --files -` fails immediately on a terminal stdin with a pipe
30
+ instruction instead of waiting indefinitely.
31
+ - `ask config` now distinguishes declared custom security annotations from
32
+ mechanisms inferred with Likely confidence.
33
+
34
+ ### Verification
35
+ - Re-ran read-only hygiene over the `spaghetti-api` and `eureka` fixtures.
36
+ The commands leave their checkouts untouched; `cold-start` reports `no_ris`
37
+ explicitly where no semantic model is available.
38
+
39
+ ### Documentation
40
+ - Recorded the MSAS findings, their reproductions, the resolved commits, and
41
+ the remaining bounded-output, schema, timeline, benchmark, and Git-basis
42
+ work in the roadmap.
43
+
5
44
  ## [5.5.4] — 2026-08-13
6
45
 
7
46
  **Re-audit performance and HTTP contract corrections.**
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 5.5.4
3
+ Version: 5.5.6
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.5.4-blue)
45
+ ![Version](https://img.shields.io/badge/version-5.5.6-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.5.4 — and, on a build that has aged,
129
+ ask version # ask 5.5.6 — 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.5.4-blue)
7
+ ![Version](https://img.shields.io/badge/version-5.5.6-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.5.4 — and, on a build that has aged,
91
+ ask version # ask 5.5.6 — and, on a build that has aged,
92
92
  # how many releases have probably shipped since
93
93
  ```
94
94
 
@@ -24,6 +24,10 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
24
24
  | AS-02 | Route projection emitted declarations from abstract Spring controllers, creating phantom routes and inflating the unprotected census. | 5.4.2 re-audit | Low | **closed 5.4.2** — abstract declarers are excluded while concrete subtype projection remains admitted; the route-surface regression covers both sides. |
25
25
  | AS-03 | The distributed ledger could leave `C3-123` open after the 5.4.1 build already excluded static assets. | 5.4.2 re-audit | Informational | **closed 5.4.2** — the stale state was synchronized to the wheel it documents. |
26
26
  | AS-04 | An unconstrained `@RequestMapping` was described by consumers as GET although Spring admits every verb. | 5.5.1 audit | Medium | **closed 5.5.1** — primary `method` is `ANY`; 5.5.4 adds the concrete `methods` list so consumers do not need to infer its expansion. |
27
+ | AS-05 | Test-source endpoint exclusion was neither visible nor reversible for modules whose test code lives below `src/main`. | 5.5.1 audit | High | **closed 5.5.3** — `--include-tests` changes admission and the production answer counts exclusions; 5.5.4 recovery adds the matched path basis. |
28
+ | AS-06 | `@InitBinder` alone was treated as evidence of programmatic validation, contaminating gaps and confidence. | 5.5.1 audit | High | **closed 5.5.1** — only a Spring `Validator` implementation or `setValidator` wiring declares the limit; 5.5.4 recovery publishes that population separately from annotation validators. |
29
+ | AS-07 | Nested types used as field types were reported statically unreferenced by modernization. | 5.5.1 audit | Medium | **closed 5.5.1** — field-type references contribute an incoming static edge. |
30
+ | AS-08 | `spring-audit` severity census could sum to zero beside nonzero findings. | 5.5.4 re-audit | High | **closed 5.5.4** — severity partitions reconcile to `total_findings`; this is asserted in the audit battery. |
27
31
 
28
32
  ---
29
33
 
@@ -46,7 +50,7 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
46
50
  | 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 |
47
51
  | 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 |
48
52
  | 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 |
49
- | 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) | **High** — this is the number a risk decision is taken with: *"¿el blast radius son 484 o 611 clases?"* | **closed 4.10.0** the caller-axis unit fix closed in 3.5.0 (`caller_metrics.caller_classes`, unit-named figures, generated note, no `len(display_list)` in `compare`). The endpoint reach half then bound `impact`, `impact-chain` and `migrate-check --blast-radius` to `caller_reach`, crossing class→method keys and DI interface boundaries without admitting structural `implements`/`extends` edges as callers. The remaining asymmetry is now closed too: `impact` expands class/interface seeds to in-repo subtypes before BFS, using `ImplementationGraph` and a class-aware seed resolver so a base-class query reaches callers of subclass members the same way `impact-chain` does. Tests cover DI parity, endpoint count/list parity, caller-unit stability and the base-class subtype regression fixture |
53
+ | 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. |
50
54
  | 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 |
51
55
  | 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) |
52
56
  | 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 |
@@ -136,7 +136,7 @@ pipx install sourcecode # isolated install, no venv needed
136
136
 
137
137
  # Verify
138
138
  ask version
139
- # ask 5.5.4
139
+ # ask 5.5.6
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.5.4"
7
+ version = "5.5.6"
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.5.4"
7
+ __version__ = "5.5.6"
@@ -2081,6 +2081,14 @@ def _resolve_files_option(
2081
2081
 
2082
2082
  raw = str(files).replace(_text_input.BOM_CHAR, "").strip()
2083
2083
  if raw == "-":
2084
+ if sys.stdin.isatty():
2085
+ _emit_error_json(
2086
+ INVALID_INPUT_CODE,
2087
+ "--files - reads the file list from stdin, but stdin is a terminal.",
2088
+ hint=f"Pipe the list: git diff --name-only main | ask {command} . --files -",
2089
+ expected="Piped stdin, a list file, or a comma-separated file list.",
2090
+ )
2091
+ raise typer.Exit(code=1)
2084
2092
  return _text_input.parse_path_lines(_text_input.read_stdin_text()), "stdin"
2085
2093
  as_path = Path(raw)
2086
2094
  if as_path.is_file():
@@ -6303,9 +6311,8 @@ def impact_cmd(
6303
6311
 
6304
6312
  _enforce_format("impact", format)
6305
6313
 
6306
- from sourcecode.repository_ir import (
6307
- build_repo_ir, find_java_files, compute_blast_radius,
6308
- )
6314
+ from sourcecode.repository_ir import find_java_files, compute_blast_radius
6315
+ from sourcecode.context_graph import ContextGraph
6309
6316
  from sourcecode.output_budget import BUDGET_IMPACT
6310
6317
 
6311
6318
  # Legacy-compat: old syntax was `impact <path> <target>`.
@@ -6358,9 +6365,16 @@ def impact_cmd(
6358
6365
  return
6359
6366
 
6360
6367
  _prog = Progress()
6361
- _prog.start(f"building IR ({len(file_list)} files) for impact analysis")
6368
+ _prog.start(f"building CIR ({len(file_list)} files) for impact analysis")
6362
6369
  try:
6363
- ir = build_repo_ir(file_list, root)
6370
+ # `impact-chain` resolves callers from the canonical IR, whose typed body
6371
+ # facts contribute method-level call edges. Reading the legacy parse-only
6372
+ # IR here made `impact` miss static calls (including locator accessors).
6373
+ from sourcecode import context_cache as _ctxcache
6374
+ try:
6375
+ cir = _ctxcache.shared_cir(root, file_list, progress=_work_sink(_prog))
6376
+ except Exception:
6377
+ cir = ContextGraph.build(file_list, root, progress=_work_sink(_prog)).cir
6364
6378
  finally:
6365
6379
  _prog.finish()
6366
6380
 
@@ -6371,7 +6385,7 @@ def impact_cmd(
6371
6385
  # skipped the budget when writing to a file (C2-2).
6372
6386
  _to_file = output_path is not None
6373
6387
  result = compute_blast_radius(
6374
- ir, target, max_depth=depth, list_limit=0 if _to_file else None, root=root
6388
+ cir._raw_ir, target, max_depth=depth, list_limit=0 if _to_file else None, root=root
6375
6389
  )
6376
6390
  result = _apply_budget(result, BUDGET_IMPACT, label="impact", output_path=output_path)
6377
6391
  if _misread and result.get("resolution") == "not_found":
@@ -8867,7 +8881,8 @@ def spring_audit_cmd(
8867
8881
  help=(
8868
8882
  "Bounded summary: caps `findings` and the per-endpoint security projection "
8869
8883
  "to their top 5, most-severe first. Every count is still measured over the "
8870
- "whole population. The full report can exceed 1 MB on a monolith."
8884
+ "whole population. Stdout also switches to this form only when the full "
8885
+ "JSON exceeds its 50 KB budget; use --output for the complete report."
8871
8886
  ),
8872
8887
  ),
8873
8888
  table: bool = typer.Option(
@@ -9204,17 +9219,19 @@ def spring_audit_cmd(
9204
9219
  "phase that never ran is not evidence there is none."
9205
9220
  )
9206
9221
 
9222
+ _full_data = combined.to_dict()
9207
9223
  _bounded_default = (
9208
9224
  not compact and output_path is None and format == "json" and not table
9225
+ and len(_json.dumps(_full_data, ensure_ascii=False).encode("utf-8")) > 50_000
9209
9226
  )
9210
- data = (
9211
- combined.to_compact_dict()
9212
- if compact or _bounded_default
9213
- else combined.to_dict()
9214
- )
9227
+ data = combined.to_compact_dict() if compact or _bounded_default else _full_data
9215
9228
  if _bounded_default:
9216
9229
  data.pop("mode", None)
9217
9230
  data["default_output"] = "bounded"
9231
+ data.pop("compact_note", None)
9232
+ data["stdout_budget_note"] = (
9233
+ "Capped by the stdout payload budget; use --output <file> for the complete report."
9234
+ )
9218
9235
  data.setdefault("metadata", {})["cache_layers"] = _cache_state
9219
9236
  if _stopped_early:
9220
9237
  # C3-87: the block is composed by `PhasedRun.status`, which is the
@@ -9249,17 +9266,19 @@ def spring_audit_cmd(
9249
9266
  elif format == "github-comment":
9250
9267
  output = _render_spring_audit_github_comment(combined, min_severity)
9251
9268
  else:
9269
+ _full_data = combined.to_dict()
9252
9270
  _bounded_default = (
9253
9271
  not compact and output_path is None and format == "json" and not table
9272
+ and len(_json.dumps(_full_data, ensure_ascii=False).encode("utf-8")) > 50_000
9254
9273
  )
9255
- data = (
9256
- combined.to_compact_dict()
9257
- if compact or _bounded_default
9258
- else combined.to_dict()
9259
- )
9274
+ data = combined.to_compact_dict() if compact or _bounded_default else _full_data
9260
9275
  if _bounded_default:
9261
9276
  data.pop("mode", None)
9262
9277
  data["default_output"] = "bounded"
9278
+ data.pop("compact_note", None)
9279
+ data["stdout_budget_note"] = (
9280
+ "Capped by the stdout payload budget; use --output <file> for the complete report."
9281
+ )
9263
9282
  output = _serialize_dict(data, format)
9264
9283
 
9265
9284
  # F-AV: our own readers ask for the count under either spelling — the break
@@ -12870,7 +12889,8 @@ def config_cmd(
12870
12889
  _answer.say(f"Declaration: none at {_declaration_path}")
12871
12890
  _answer.say(
12872
12891
  " Nothing is declared, so `data-exposure` answers "
12873
- "`answered: false` and no custom security annotation is recognised."
12892
+ "`answered: false`. Custom security annotations may still be inferred "
12893
+ "from usage with Likely confidence; declare them to make that policy Declared."
12874
12894
  )
12875
12895
  else:
12876
12896
  _answer.say(f"Declaration: {_declaration_path}")
@@ -3833,8 +3833,6 @@ def _route_annotation_name(sym: SymbolRecord) -> str:
3833
3833
  def _enclosing_class(fqn: str) -> str:
3834
3834
  if "#" in fqn:
3835
3835
  return fqn.split("#")[0]
3836
- if "." in fqn:
3837
- return fqn.rsplit(".", 1)[0]
3838
3836
  return fqn
3839
3837
 
3840
3838
 
@@ -6448,6 +6446,7 @@ def extract_java_endpoints(
6448
6446
  # calls. One candidate list keeps that work bounded and reusable below.
6449
6447
  _config_files = sorted(root.rglob("application*.*"))
6450
6448
  _excluded_test_source_routes = 0
6449
+ _excluded_test_patterns: set[str] = set()
6451
6450
  if not include_tests:
6452
6451
  _route_marker = _re.compile(
6453
6452
  r"@(GetMapping|PostMapping|PutMapping|DeleteMapping|PatchMapping|RequestMapping|GET|POST|PUT|DELETE|PATCH|Path)\b"
@@ -6456,6 +6455,15 @@ def extract_java_endpoints(
6456
6455
  _candidate_rel = _rel(_candidate)
6457
6456
  if not is_test_source_file(_candidate_rel):
6458
6457
  continue
6458
+ _candidate_parts = _candidate_rel.lower().split("/")
6459
+ if "test-suite" in _candidate_parts:
6460
+ _excluded_test_patterns.add("test-suite/")
6461
+ elif "src/test/" in _candidate_rel.lower():
6462
+ _excluded_test_patterns.add("src/test/")
6463
+ elif "src/it/" in _candidate_rel.lower():
6464
+ _excluded_test_patterns.add("src/it/")
6465
+ else:
6466
+ _excluded_test_patterns.add("test-source classification")
6459
6467
  try:
6460
6468
  _excluded_test_source_routes += len(
6461
6469
  _route_marker.findall(_candidate.read_text(encoding="utf-8", errors="replace"))
@@ -6962,6 +6970,11 @@ def extract_java_endpoints(
6962
6970
  result["test_util_included"] = len(_test_util_endpoints)
6963
6971
  if _excluded_test_source_routes:
6964
6972
  result["test_source_excluded"] = _excluded_test_source_routes
6973
+ result["test_source_excluded_detail"] = {
6974
+ "count": _excluded_test_source_routes,
6975
+ "patterns_matched": sorted(_excluded_test_patterns),
6976
+ "basis": "path classification; use --include-tests to include these routes",
6977
+ }
6965
6978
  result.setdefault("notes", []).append(
6966
6979
  f"{_excluded_test_source_routes} route annotation(s) from test source trees "
6967
6980
  "were excluded; use --include-tests to include them."
@@ -7187,6 +7200,10 @@ _DISPLAY_INDIRECT_CALLERS = 50
7187
7200
  HUB_CALLER_THRESHOLD = 500
7188
7201
  HUB_SAMPLE_SIZE = 20
7189
7202
  HUB_SAMPLE_DEPTH = 2
7203
+ # A frontier is a hub for this query when it materially exceeds the target's
7204
+ # own fan-in, even if it does not reach the global safety cap above.
7205
+ HUB_RELATIVE_FANIN_MULTIPLIER = 3
7206
+ HUB_RELATIVE_FANIN_FLOOR = 20
7190
7207
 
7191
7208
 
7192
7209
  def _symbol_listing_advice(root: "Optional[Path]") -> str:
@@ -7339,21 +7356,15 @@ def compute_blast_radius(
7339
7356
  _rg_index = _class_method_index(reverse_graph)
7340
7357
 
7341
7358
  def _walk_callers(_node: str) -> list[str]:
7342
- """Expand method frontiers through their owning class as well.
7359
+ """Return the one-authority caller set for this exact frontier node.
7343
7360
 
7344
- Service-locator accessors enter the frontier as ``Class#accessor`` while
7345
- callers of the class are indexed under its method keys. Keeping the
7346
- exact lookup preserves method-level reach; adding the enclosing class
7347
- lookup prevents that frontier from becoming a false terminal node.
7361
+ `caller_reach` expands class nodes through their method keys, which is
7362
+ required for DI-normalized class frontiers. It deliberately does not
7363
+ invert that containment for a method: callers of `Locator#getA` are a
7364
+ subset of callers of `Locator`, and adding the latter leaks users of
7365
+ every other locator accessor into the blast cone.
7348
7366
  """
7349
- candidates = list(_all_callers_from_rg(
7350
- _node, reverse_graph, _rg_index, _impl_graph
7351
- ))
7352
- if "#" in _node:
7353
- candidates.extend(_all_callers_from_rg(
7354
- _enclosing_class(_node), reverse_graph, _rg_index, _impl_graph
7355
- ))
7356
- return list(dict.fromkeys(candidates))
7367
+ return _all_callers_from_rg(_node, reverse_graph, _rg_index, _impl_graph)
7357
7368
  # Callers inject the interface, not the implementation, so a walk that does not
7358
7369
  # know the implements relation stops one hop short of every DI consumer. Without
7359
7370
  # it this traversal reached 200 classes and **no controller at all** for
@@ -7473,6 +7484,26 @@ def compute_blast_radius(
7473
7484
  if _effective_depth > 1:
7474
7485
  queue.append((c, 1))
7475
7486
 
7487
+ # A locator accessor can be a narrow method frontier on a broad class hub.
7488
+ # Measure the owner only to classify that frontier, never to expand it: the
7489
+ # latter would merge callers of unrelated accessors (Q-01).
7490
+ _seed_fan_in = max(1, len(set(direct_callers)))
7491
+ _hub_frontier_cutoff = max(
7492
+ HUB_RELATIVE_FANIN_FLOOR,
7493
+ _seed_fan_in * HUB_RELATIVE_FANIN_MULTIPLIER,
7494
+ )
7495
+
7496
+ def _is_hub_frontier(_node: str) -> bool:
7497
+ _fan_in = len(_walk_callers(_node))
7498
+ if "#" in _node:
7499
+ _fan_in = max(
7500
+ _fan_in,
7501
+ len(_all_callers_from_rg(
7502
+ _enclosing_class(_node), reverse_graph, _rg_index, _impl_graph
7503
+ )),
7504
+ )
7505
+ return _fan_in > _hub_frontier_cutoff
7506
+
7476
7507
  # BFS for indirect callers
7477
7508
  indirect_callers: list[str] = []
7478
7509
  _indirect_parent: dict[str, str] = {}
@@ -7484,7 +7515,7 @@ def compute_blast_radius(
7484
7515
  if depth >= _effective_depth:
7485
7516
  continue
7486
7517
  _node_callers = _walk_callers(node)
7487
- if len(_node_callers) > _HUB_CALLER_THRESHOLD:
7518
+ if _is_hub_frontier(node):
7488
7519
  _hub_frontier_nodes.add(node)
7489
7520
  for caller in _node_callers:
7490
7521
  if caller not in visited:
@@ -7735,7 +7766,7 @@ def compute_blast_radius(
7735
7766
  if _depth >= _effective_depth:
7736
7767
  continue
7737
7768
  _next = _walk_callers(_node)
7738
- if len(_next) > _HUB_CALLER_THRESHOLD:
7769
+ if _is_hub_frontier(_node):
7739
7770
  continue
7740
7771
  for _caller in _next:
7741
7772
  if _caller not in _without_hubs_seen:
@@ -276,7 +276,13 @@ def _scan_java(source: str, rel: str) -> "list[SecurityConfigObservation]":
276
276
  ))
277
277
  break # one observation per algorithm per file
278
278
  for encoder in _DEPRECATED_ENCODERS:
279
- m = re.search(re.escape(encoder), source)
279
+ # An import only makes the type available; it is not evidence that
280
+ # a password encoder is used. Require construction or the legacy
281
+ # factory call so the witness opens on a remediation site.
282
+ m = re.search(
283
+ rf"(?:new\s+{re.escape(encoder)}\b|{re.escape(encoder)}\s*\.\s*getInstance\s*\()",
284
+ source,
285
+ )
280
286
  if m:
281
287
  out.append(SecurityConfigObservation(
282
288
  rule="SEC-004",
@@ -83,49 +83,72 @@ def _child_text(elem: "ET.Element", name: str) -> str:
83
83
  return ""
84
84
 
85
85
 
86
- def _from_web_xml(xml_path: Path, relative: str) -> "list[ServletMapping]":
86
+ def _from_web_xml(xml_path: Path, relative: str) -> "tuple[list[ServletMapping], int]":
87
87
  try:
88
88
  text = xml_path.read_text(encoding="utf-8", errors="replace")
89
89
  root = ET.fromstring(text)
90
90
  except Exception:
91
- return []
91
+ # Legacy descriptors can contain constructs ElementTree rejects. Keep the
92
+ # lexical fallback below, including its omission census; returning here
93
+ # made an undeclared live mapping look as though it had never existed.
94
+ root = None
92
95
  classes: "dict[str, str]" = {}
93
- for elem in root.iter():
94
- if _local(elem.tag) != "servlet":
95
- continue
96
- name = _child_text(elem, "servlet-name")
97
- klass = _child_text(elem, "servlet-class")
98
- if name:
99
- classes[name] = klass
96
+ if root is not None:
97
+ for elem in root.iter():
98
+ if _local(elem.tag) != "servlet":
99
+ continue
100
+ name = _child_text(elem, "servlet-name")
101
+ klass = _child_text(elem, "servlet-class")
102
+ if name:
103
+ classes[name] = klass
100
104
  out: "list[ServletMapping]" = []
101
- for elem in root.iter():
102
- if _local(elem.tag) != "servlet-mapping":
103
- continue
104
- name = _child_text(elem, "servlet-name")
105
- for child in elem.iter():
106
- if _local(child.tag) != "url-pattern" or not (child.text or "").strip():
105
+ undeclared_servlet_mappings = 0
106
+ if root is not None:
107
+ for elem in root.iter():
108
+ if _local(elem.tag) != "servlet-mapping":
107
109
  continue
108
- out.append(ServletMapping(
109
- url_pattern=child.text.strip(),
110
- servlet_name=name,
111
- servlet_class=classes.get(name, ""),
112
- source_file=relative,
113
- source="web.xml",
114
- ))
110
+ name = _child_text(elem, "servlet-name")
111
+ if not name or name not in classes:
112
+ undeclared_servlet_mappings += sum(
113
+ 1 for child in elem.iter()
114
+ if _local(child.tag) == "url-pattern" and (child.text or "").strip()
115
+ )
116
+ continue
117
+ for child in elem.iter():
118
+ if _local(child.tag) != "url-pattern" or not (child.text or "").strip():
119
+ continue
120
+ out.append(ServletMapping(
121
+ url_pattern=child.text.strip(),
122
+ servlet_name=name,
123
+ servlet_class=classes.get(name, ""),
124
+ source_file=relative,
125
+ source="web.xml",
126
+ ))
115
127
  # Keep a tolerant lexical fallback for descriptors containing legacy XML
116
128
  # constructs that ElementTree rejects or partially normalizes. Comments are
117
129
  # removed first so commented deployment examples never become live routes.
118
130
  try:
119
131
  active = re.sub(r"<!--.*?-->", "", text, flags=re.DOTALL)
132
+ if root is None:
133
+ for block in re.findall(r"<servlet\b[^>]*>(.*?)</servlet>", active, re.DOTALL | re.IGNORECASE):
134
+ name_match = re.search(r"<servlet-name\b[^>]*>\s*([^<]+?)\s*</servlet-name>", block, re.DOTALL | re.IGNORECASE)
135
+ class_match = re.search(r"<servlet-class\b[^>]*>\s*([^<]+?)\s*</servlet-class>", block, re.DOTALL | re.IGNORECASE)
136
+ if name_match:
137
+ classes[name_match.group(1).strip()] = class_match.group(1).strip() if class_match else ""
120
138
  for block in re.findall(r"<servlet-mapping\b[^>]*>(.*?)</servlet-mapping>", active, re.DOTALL | re.IGNORECASE):
121
139
  name_match = re.search(r"<servlet-name\b[^>]*>\s*([^<]+?)\s*</servlet-name>", block, re.DOTALL | re.IGNORECASE)
122
140
  pattern_match = re.search(r"<url-pattern\b[^>]*>\s*([^<]+?)\s*</url-pattern>", block, re.DOTALL | re.IGNORECASE)
123
141
  if not pattern_match:
124
142
  continue
143
+ servlet_name = name_match.group(1).strip() if name_match else ""
144
+ if not servlet_name or servlet_name not in classes:
145
+ if root is None:
146
+ undeclared_servlet_mappings += 1
147
+ continue
125
148
  candidate = ServletMapping(
126
149
  url_pattern=pattern_match.group(1).strip(),
127
- servlet_name=name_match.group(1).strip() if name_match else "",
128
- servlet_class=classes.get(name_match.group(1).strip(), "") if name_match else "",
150
+ servlet_name=servlet_name,
151
+ servlet_class=classes[servlet_name],
129
152
  source_file=relative,
130
153
  source="web.xml",
131
154
  )
@@ -133,7 +156,7 @@ def _from_web_xml(xml_path: Path, relative: str) -> "list[ServletMapping]":
133
156
  out.append(candidate)
134
157
  except Exception:
135
158
  pass
136
- return out
159
+ return out, undeclared_servlet_mappings
137
160
 
138
161
 
139
162
  def _from_registration_beans(java_path: Path, relative: str) -> "list[ServletMapping]":
@@ -178,6 +201,7 @@ def build_servlet_surface(
178
201
 
179
202
  root = Path(root)
180
203
  mappings: "list[ServletMapping]" = []
204
+ undeclared_servlet_mappings = 0
181
205
  if files is None:
182
206
  candidates: list[Path] = []
183
207
  for directory, dirs, names in os.walk(root):
@@ -209,7 +233,9 @@ def build_servlet_surface(
209
233
  except ValueError:
210
234
  relative = Path(path).as_posix()
211
235
  if path.name == "web.xml":
212
- mappings.extend(_from_web_xml(path, relative))
236
+ parsed, omitted = _from_web_xml(path, relative)
237
+ mappings.extend(parsed)
238
+ undeclared_servlet_mappings += omitted
213
239
  elif path.suffix == ".java":
214
240
  mappings.extend(_from_registration_beans(path, relative))
215
241
 
@@ -221,6 +247,7 @@ def build_servlet_surface(
221
247
  _unique: dict[str, ServletMapping] = {}
222
248
  for mapping in sorted(mappings, key=lambda m: (m.url_pattern, m.source_file, m.source)):
223
249
  _unique.setdefault(mapping.url_pattern, mapping)
250
+ declared_mappings = list(mappings)
224
251
  mappings = list(_unique.values())
225
252
  patterns = sorted(_unique)
226
253
  by_source: "dict[str, int]" = {}
@@ -232,15 +259,25 @@ def build_servlet_surface(
232
259
  )]
233
260
  shown = rows if limit is None else rows[:limit]
234
261
 
262
+ display_omitted = len(rows) - len(shown)
263
+ omitted_total = undeclared_servlet_mappings + display_omitted
264
+ omitted_by_reason: dict[str, int] = {}
265
+ if undeclared_servlet_mappings:
266
+ omitted_by_reason["servlet_not_declared"] = undeclared_servlet_mappings
267
+ if display_omitted:
268
+ omitted_by_reason["display_limit"] = display_omitted
269
+
235
270
  return {
236
271
  "schema_version": SCHEMA_VERSION,
237
272
  "population_key": POPULATION,
238
273
  "total_url_patterns": len(patterns),
239
- "declarations": len(mappings),
274
+ "declarations": len(declared_mappings),
275
+ "declarations_unit": "live servlet-mapping declarations before URL-pattern deduplication",
240
276
  "by_source": dict(sorted(by_source.items())),
241
277
  "url_patterns": patterns,
242
278
  "mappings": shown,
243
- "mappings_omitted": len(rows) - len(shown),
279
+ "mappings_omitted": omitted_total,
280
+ "mappings_omitted_by_reason": omitted_by_reason,
244
281
  "population": (
245
282
  "servlet mappings declared in `web.xml` or registered as a "
246
283
  "`ServletRegistrationBean` — HTTP surface that is NOT a Spring handler "
@@ -211,7 +211,12 @@ class Sample:
211
211
 
212
212
  def _git(repo: Path, *args: str) -> subprocess.CompletedProcess:
213
213
  return subprocess.run(
214
- ["git", "-C", str(repo), *args], capture_output=True, text=True, check=False,
214
+ ["git", "-C", str(repo), "-c", "i18n.logOutputEncoding=utf-8", *args],
215
+ capture_output=True,
216
+ text=True,
217
+ encoding="utf-8",
218
+ errors="replace",
219
+ check=False,
215
220
  )
216
221
 
217
222
 
@@ -614,7 +614,16 @@ def build_validation_surface(
614
614
  "summary": {
615
615
  "endpoints_with_body": len(out_endpoints),
616
616
  "validated_fields": total_validated_fields,
617
+ # This catalog contains annotation-based ConstraintValidator types.
618
+ # Spring Validator wiring is detected separately below and must not
619
+ # be read as zero just because it belongs to another population.
617
620
  "custom_validators_declared": len(custom_list),
621
+ "custom_validators_declared_unit": "annotation-based ConstraintValidator declarations",
622
+ "programmatic_validators_declared": len(programmatic_evidence),
623
+ "programmatic_validators_declared_unit": (
624
+ "detected org.springframework.validation.Validator implementations "
625
+ "or setValidator wiring; rules are not modeled per endpoint"
626
+ ),
618
627
  "custom_validators_linked": len(custom_used),
619
628
  "gaps": len(gaps),
620
629
  },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes