sourcecode 5.5.4__tar.gz → 5.5.5__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.5}/CHANGELOG.md +25 -0
  2. {sourcecode-5.5.4 → sourcecode-5.5.5}/PKG-INFO +3 -3
  3. {sourcecode-5.5.4 → sourcecode-5.5.5}/README.md +2 -2
  4. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/DEFECT-LEDGER.md +5 -1
  5. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/USER_GUIDE.md +1 -1
  6. {sourcecode-5.5.4 → sourcecode-5.5.5}/pyproject.toml +1 -1
  7. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/__init__.py +1 -1
  8. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/cli.py +26 -12
  9. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/repository_ir.py +48 -15
  10. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/security_config_scan.py +7 -1
  11. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/servlet_surface.py +32 -8
  12. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/validation_surface.py +9 -0
  13. {sourcecode-5.5.4 → sourcecode-5.5.5}/.github/workflows/build-windows.yml +0 -0
  14. {sourcecode-5.5.4 → sourcecode-5.5.5}/.github/workflows/perf-gate.yml +0 -0
  15. {sourcecode-5.5.4 → sourcecode-5.5.5}/.gitignore +0 -0
  16. {sourcecode-5.5.4 → sourcecode-5.5.5}/.ruff.toml +0 -0
  17. {sourcecode-5.5.4 → sourcecode-5.5.5}/CLAUDE.md +0 -0
  18. {sourcecode-5.5.4 → sourcecode-5.5.5}/CONTRIBUTING.md +0 -0
  19. {sourcecode-5.5.4 → sourcecode-5.5.5}/LICENSE +0 -0
  20. {sourcecode-5.5.4 → sourcecode-5.5.5}/SECURITY.md +0 -0
  21. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/CACHE.md +0 -0
  22. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/COMMERCIAL-OFFER.md +0 -0
  23. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/CUSTOMER-VALIDATION.md +0 -0
  24. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/DEMO-5MIN.md +0 -0
  25. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/DEVELOPMENT-ROADMAP.md +0 -0
  26. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/EXECUTION-PLAN-12MO.md +0 -0
  27. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/EXECUTIVE-ROADMAP.md +0 -0
  28. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/MANUAL-USUARIO.md +0 -0
  29. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/MODERNIZATION.md +0 -0
  30. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/PRODUCT_AUDIT.md +0 -0
  31. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/PRODUCT_IDENTITY.md +0 -0
  32. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/PRODUCT_TIERS.md +0 -0
  33. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/RC1-CHECKLIST.md +0 -0
  34. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/RELEASE-CHECKLIST.md +0 -0
  35. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/CATALOG-retrieval-intents.md +0 -0
  36. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/DESIGN-knowledge-provider-integration.md +0 -0
  37. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/DESIGN-p5.3b-statement-surface.md +0 -0
  38. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/DESIGN-p5.5-span-index.md +0 -0
  39. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/DESIGN-p6-semantic-impact-engine.md +0 -0
  40. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/DESIGN-p6-semantic-integration-engine.md +0 -0
  41. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/DESIGN-p7-semantic-services.md +0 -0
  42. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/DESIGN-robustness-security-inference.md +0 -0
  43. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/DESIGN-semantic-retrieval.md +0 -0
  44. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/EPV-p6-class-typeref.md +0 -0
  45. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/REPORT-p6-flow-analyzer-comparison.md +0 -0
  46. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/REPORT-p6-integration-detector-comparison.md +0 -0
  47. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/REPORT-p8-flow-analyzer-migration.md +0 -0
  48. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/REPORT-p9-integration-detector-migration.md +0 -0
  49. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/adr/0001-semantic-ir.md +0 -0
  50. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/adr/0002-source-evidence-boundary.md +0 -0
  51. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/adr/0003-architectural-characterization.md +0 -0
  52. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/adr/0004-semantic-retrieval.md +0 -0
  53. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/adr/0005-evidence-reconciliation.md +0 -0
  54. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/adr/0006-performance-harness.md +0 -0
  55. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/adr/0007-composition-semantics.md +0 -0
  56. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/adr/0008-answer-coherence.md +0 -0
  57. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/consumer-disposition-inventory.md +0 -0
  58. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/overview.md +0 -0
  59. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/product-architecture-review.md +0 -0
  60. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/product-strategy-layering.md +0 -0
  61. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/semantic-ir-consumer-matrix.md +0 -0
  62. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/architecture/semantic-ir-roadmap.md +0 -0
  63. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/baseline-ci.yml +0 -0
  64. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/commercial-pipeline-audit.md +0 -0
  65. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/contracts-ci.yml +0 -0
  66. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/contracts.md +0 -0
  67. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/eval/2026-06-15-petclinic-rest-issue11-vets-pagination.md +0 -0
  68. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/eval/2026-06-15-petclinic-rest-issue147-validation.md +0 -0
  69. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/eval/2026-06-15-petclinic-rest-workflow.md +0 -0
  70. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/eval/2026-06-15-phase18-openapi-surface.md +0 -0
  71. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/eval/2026-06-15-phase20-validation-surface.md +0 -0
  72. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/eval/2026-06-16-broadleaf-checkout-impact-fieldtest.md +0 -0
  73. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/eval/2026-06-16-petclinic-rest-issue11-weakness2-closed.md +0 -0
  74. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/eval/2026-06-16-spring-petclinic-issue2333-dead-vets.md +0 -0
  75. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/licensing-schema.md +0 -0
  76. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/migrate-check.md +0 -0
  77. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/README.md +0 -0
  78. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/REGRESSION-GATE.md +0 -0
  79. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/SUMMARY.md +0 -0
  80. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/alfresco__ask__cold.json +0 -0
  81. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/alfresco__ask__warm.json +0 -0
  82. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/broadleaf__ask__cold.json +0 -0
  83. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/broadleaf__ask__warm.json +0 -0
  84. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/eureka__ask__cold.json +0 -0
  85. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/eureka__ask__warm.json +0 -0
  86. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/jenkins__ask__cold.json +0 -0
  87. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/jenkins__ask__warm.json +0 -0
  88. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/jobrunr__ask__cold.json +0 -0
  89. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/jobrunr__ask__warm.json +0 -0
  90. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/keycloak__ask__cold.json +0 -0
  91. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/keycloak__ask__warm.json +0 -0
  92. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/neo4j__ask__cold.json +0 -0
  93. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/neo4j__ask__warm.json +0 -0
  94. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/ofbiz__ask__cold.json +0 -0
  95. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/ofbiz__ask__warm.json +0 -0
  96. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/openmrs__ask__cold.json +0 -0
  97. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/openmrs__ask__warm.json +0 -0
  98. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/petclinic__ask__cold.json +0 -0
  99. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.15/petclinic__ask__warm.json +0 -0
  100. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/SUMMARY.md +0 -0
  101. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/alfresco__ask__cold.json +0 -0
  102. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/alfresco__ask__warm.json +0 -0
  103. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/broadleaf__ask__cold.json +0 -0
  104. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/broadleaf__ask__warm.json +0 -0
  105. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/eureka__ask__cold.json +0 -0
  106. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/eureka__ask__warm.json +0 -0
  107. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/jenkins__ask__cold.json +0 -0
  108. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/jenkins__ask__warm.json +0 -0
  109. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/jobrunr__ask__cold.json +0 -0
  110. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/jobrunr__ask__warm.json +0 -0
  111. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/keycloak__ask__cold.json +0 -0
  112. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/keycloak__ask__warm.json +0 -0
  113. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/neo4j__ask__cold.json +0 -0
  114. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/neo4j__ask__warm.json +0 -0
  115. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/ofbiz__ask__cold.json +0 -0
  116. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/ofbiz__ask__warm.json +0 -0
  117. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/openmrs__ask__cold.json +0 -0
  118. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/openmrs__ask__warm.json +0 -0
  119. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/petclinic__ask__cold.json +0 -0
  120. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/2.5.16/petclinic__ask__warm.json +0 -0
  121. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/gate-latest/README.md +0 -0
  122. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/gate-latest/broadleaf__ask__warm.json +0 -0
  123. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/gate-latest/broadleaf__endpoints__warm.json +0 -0
  124. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/gate-latest/broadleaf__migrate-check__warm.json +0 -0
  125. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/gate-latest/broadleaf__posture__warm.json +0 -0
  126. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/gate-latest/broadleaf__spring-audit__warm.json +0 -0
  127. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/gate-latest/broadleaf__validation__warm.json +0 -0
  128. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/perf/baselines/reference-best/README.md +0 -0
  129. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/posture.md +0 -0
  130. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/privacy.md +0 -0
  131. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/pro-experience-audit.md +0 -0
  132. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/schema.md +0 -0
  133. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/spec/envelope-v1.md +0 -0
  134. {sourcecode-5.5.4 → sourcecode-5.5.5}/docs/spring-audit-ci.yml +0 -0
  135. {sourcecode-5.5.4 → sourcecode-5.5.5}/raw +0 -0
  136. {sourcecode-5.5.4 → sourcecode-5.5.5}/scripts/compare_integration_engines.py +0 -0
  137. {sourcecode-5.5.4 → sourcecode-5.5.5}/scripts/customer_smoke_test.sh +0 -0
  138. {sourcecode-5.5.4 → sourcecode-5.5.5}/scripts/generate_jdk_exports.py +0 -0
  139. {sourcecode-5.5.4 → sourcecode-5.5.5}/scripts/perf_gate.py +0 -0
  140. {sourcecode-5.5.4 → sourcecode-5.5.5}/scripts/perf_harness.py +0 -0
  141. {sourcecode-5.5.4 → sourcecode-5.5.5}/scripts/sync_gate_anchors.py +0 -0
  142. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/adaptive_scanner.py +0 -0
  143. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/archetype.py +0 -0
  144. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/architectural_baseline.py +0 -0
  145. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/architectural_delta.py +0 -0
  146. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/architecture_analyzer.py +0 -0
  147. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/architecture_summary.py +0 -0
  148. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/ast_extractor.py +0 -0
  149. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/audit_report.py +0 -0
  150. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/baseline_autocapture.py +0 -0
  151. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/bundled_docs.py +0 -0
  152. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/cache.py +0 -0
  153. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/cache_model.py +0 -0
  154. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/call_surface.py +0 -0
  155. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/caller_metrics.py +0 -0
  156. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/caller_reach.py +0 -0
  157. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/canonical_ir.py +0 -0
  158. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/chain_rules.py +0 -0
  159. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/change_plan.py +0 -0
  160. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/cir_graphs.py +0 -0
  161. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/classifier.py +0 -0
  162. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/client_calls.py +0 -0
  163. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/code_notes_analyzer.py +0 -0
  164. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/compare.py +0 -0
  165. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/confidence_analyzer.py +0 -0
  166. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/constraint_diff.py +0 -0
  167. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/consumer_join.py +0 -0
  168. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/container_wiring.py +0 -0
  169. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/context_cache.py +0 -0
  170. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/context_graph.py +0 -0
  171. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/context_scorer.py +0 -0
  172. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/context_summarizer.py +0 -0
  173. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/contract_diff.py +0 -0
  174. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/contract_init.py +0 -0
  175. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/contract_model.py +0 -0
  176. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/contract_pipeline.py +0 -0
  177. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/coverage_parser.py +0 -0
  178. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/data_exposure.py +0 -0
  179. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/data_labels.py +0 -0
  180. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/declarations.py +0 -0
  181. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/defect_identity.py +0 -0
  182. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/degradation.py +0 -0
  183. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/dependency_analyzer.py +0 -0
  184. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/deployment_prefix.py +0 -0
  185. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detach.py +0 -0
  186. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/__init__.py +0 -0
  187. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/base.py +0 -0
  188. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/csproj_parser.py +0 -0
  189. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/dart.py +0 -0
  190. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/dotnet.py +0 -0
  191. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/elixir.py +0 -0
  192. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/go.py +0 -0
  193. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/heuristic.py +0 -0
  194. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/hybrid.py +0 -0
  195. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/java.py +0 -0
  196. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/jvm_ext.py +0 -0
  197. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/nodejs.py +0 -0
  198. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/parsers.py +0 -0
  199. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/php.py +0 -0
  200. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/project.py +0 -0
  201. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/python.py +0 -0
  202. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/ruby.py +0 -0
  203. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/rust.py +0 -0
  204. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/systems.py +0 -0
  205. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/terraform.py +0 -0
  206. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/detectors/tooling.py +0 -0
  207. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/doc_analyzer.py +0 -0
  208. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/dynamic_argument_surface.py +0 -0
  209. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/endpoint_literals.py +0 -0
  210. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/endpoint_metrics.py +0 -0
  211. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/entrypoint_classifier.py +0 -0
  212. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/env_analyzer.py +0 -0
  213. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/envelope.py +0 -0
  214. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/environment_resolution.py +0 -0
  215. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/error_schema.py +0 -0
  216. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/evidence_provider.py +0 -0
  217. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/execution_plan.py +0 -0
  218. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/explain.py +0 -0
  219. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/facts/__init__.py +0 -0
  220. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/facts/registry.json +0 -0
  221. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/file_chunker.py +0 -0
  222. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/file_classifier.py +0 -0
  223. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/filter_surface.py +0 -0
  224. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/format_contract.py +0 -0
  225. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/fqn_utils.py +0 -0
  226. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/gate_anchors.py +0 -0
  227. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/git_analyzer.py +0 -0
  228. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/git_checkout.py +0 -0
  229. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/graph_analyzer.py +0 -0
  230. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/graph_evidence.py +0 -0
  231. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/hibernate_strat.py +0 -0
  232. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/identity_fallback.py +0 -0
  233. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/integration_coordinates.py +0 -0
  234. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/jdk_exports.py +0 -0
  235. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/license.py +0 -0
  236. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/manifest_cache.py +0 -0
  237. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/__init__.py +0 -0
  238. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/dependency_import.py +0 -0
  239. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  240. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  241. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  242. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  243. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  244. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/orchestrator.py +0 -0
  245. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/registry.py +0 -0
  246. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/runner.py +0 -0
  247. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp/server.py +0 -0
  248. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/mcp_nudge.py +0 -0
  249. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/metrics_analyzer.py +0 -0
  250. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/migrate_check.py +0 -0
  251. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/migration_blast.py +0 -0
  252. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/non_coverage.py +0 -0
  253. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/openapi_surface.py +0 -0
  254. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/openrewrite_recipe.py +0 -0
  255. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/output_budget.py +0 -0
  256. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/output_encoding.py +0 -0
  257. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/parallel.py +0 -0
  258. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/parse_cache.py +0 -0
  259. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/partial_contract.py +0 -0
  260. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/path_admission.py +0 -0
  261. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/path_filters.py +0 -0
  262. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/perf.py +0 -0
  263. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/phased_run.py +0 -0
  264. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/pipe_contract.py +0 -0
  265. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/posture.py +0 -0
  266. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/pr_comment_renderer.py +0 -0
  267. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/pr_impact.py +0 -0
  268. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/prepare_context.py +0 -0
  269. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/progress.py +0 -0
  270. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/provenance.py +0 -0
  271. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/ranking_engine.py +0 -0
  272. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/readiness_timeline.py +0 -0
  273. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/readonly.py +0 -0
  274. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/reconciliation.py +0 -0
  275. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/redactor.py +0 -0
  276. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/reference_facts.py +0 -0
  277. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/release_info.py +0 -0
  278. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/relevance_scorer.py +0 -0
  279. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/remedies.py +0 -0
  280. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/rename_refactor.py +0 -0
  281. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/repo_classifier.py +0 -0
  282. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/__init__.py +0 -0
  283. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/context.py +0 -0
  284. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/errors.py +0 -0
  285. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/executor.py +0 -0
  286. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/planner.py +0 -0
  287. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/query.py +0 -0
  288. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/request.py +0 -0
  289. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/resolution.py +0 -0
  290. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/result.py +0 -0
  291. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/retriever.py +0 -0
  292. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/runtime.py +0 -0
  293. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/steps.py +0 -0
  294. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  295. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/steps_graph.py +0 -0
  296. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/steps_impact.py +0 -0
  297. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/steps_intf.py +0 -0
  298. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/steps_struct.py +0 -0
  299. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  300. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/ris.py +0 -0
  301. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/risk.py +0 -0
  302. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/rule_catalog.py +0 -0
  303. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/rule_pass.py +0 -0
  304. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/runs.py +0 -0
  305. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/runtime_classifier.py +0 -0
  306. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/sarif.py +0 -0
  307. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/scanner.py +0 -0
  308. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/schema.py +0 -0
  309. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  310. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/security_chain.py +0 -0
  311. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/security_config.py +0 -0
  312. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/security_posture.py +0 -0
  313. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/semantic_analyzer.py +0 -0
  314. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/semantic_impact_engine.py +0 -0
  315. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/semantic_integration_engine.py +0 -0
  316. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/semantic_services.py +0 -0
  317. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/serializer.py +0 -0
  318. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/source_text.py +0 -0
  319. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/spring_event_topology.py +0 -0
  320. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/spring_findings.py +0 -0
  321. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/spring_impact.py +0 -0
  322. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/spring_model.py +0 -0
  323. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/spring_profiles.py +0 -0
  324. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/spring_properties.py +0 -0
  325. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/spring_security_audit.py +0 -0
  326. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/spring_semantic.py +0 -0
  327. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/spring_tx_analyzer.py +0 -0
  328. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/sql_taint.py +0 -0
  329. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/summarizer.py +0 -0
  330. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/target_admission.py +0 -0
  331. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/telemetry/__init__.py +0 -0
  332. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/telemetry/config.py +0 -0
  333. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/telemetry/consent.py +0 -0
  334. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/telemetry/events.py +0 -0
  335. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/telemetry/filters.py +0 -0
  336. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/telemetry/transport.py +0 -0
  337. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/test_gap_ranking.py +0 -0
  338. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/test_sources.py +0 -0
  339. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/text_input.py +0 -0
  340. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/timeline.py +0 -0
  341. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/token_estimate.py +0 -0
  342. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/tree_utils.py +0 -0
  343. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/type_usage_surface.py +0 -0
  344. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/validation_inference.py +0 -0
  345. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/verify_edit.py +0 -0
  346. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/verify_repo.py +0 -0
  347. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/verify_rules.py +0 -0
  348. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/version_check.py +0 -0
  349. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/waivers.py +0 -0
  350. {sourcecode-5.5.4 → sourcecode-5.5.5}/src/sourcecode/workspace.py +0 -0
  351. {sourcecode-5.5.4 → sourcecode-5.5.5}/supabase/functions/README.md +0 -0
  352. {sourcecode-5.5.4 → sourcecode-5.5.5}/supabase/functions/get-license/index.ts +0 -0
  353. {sourcecode-5.5.4 → sourcecode-5.5.5}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  354. {sourcecode-5.5.4 → sourcecode-5.5.5}/supabase/functions/telemetry/index.ts +0 -0
  355. {sourcecode-5.5.4 → sourcecode-5.5.5}/supabase/sql/license_event_ordering.sql +0 -0
  356. {sourcecode-5.5.4 → sourcecode-5.5.5}/supabase/sql/licensing_schema.sql +0 -0
  357. {sourcecode-5.5.4 → sourcecode-5.5.5}/supabase/sql/telemetry_events.sql +0 -0
@@ -2,6 +2,31 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.5.5] — 2026-08-13
6
+
7
+ **MSAS performance-audit corrections and read-only hygiene verification.**
8
+
9
+ ### Fixed
10
+ - `spring-audit` now preserves full default JSON when it fits the stdout
11
+ budget; only oversized answers are bounded, and the notice points to
12
+ `--output` for the complete report.
13
+ - SEC-004 ignores import-only deprecated encoders and reports concrete
14
+ construction or factory evidence instead.
15
+ - `pr-impact --files -` fails immediately on a terminal stdin with a pipe
16
+ instruction instead of waiting indefinitely.
17
+ - `ask config` now distinguishes declared custom security annotations from
18
+ mechanisms inferred with Likely confidence.
19
+
20
+ ### Verification
21
+ - Re-ran read-only hygiene over the `spaghetti-api` and `eureka` fixtures.
22
+ The commands leave their checkouts untouched; `cold-start` reports `no_ris`
23
+ explicitly where no semantic model is available.
24
+
25
+ ### Documentation
26
+ - Recorded the MSAS findings, their reproductions, the resolved commits, and
27
+ the remaining bounded-output, schema, timeline, benchmark, and Git-basis
28
+ work in the roadmap.
29
+
5
30
  ## [5.5.4] — 2026-08-13
6
31
 
7
32
  **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.5
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.5-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.5 — 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.5-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.5 — 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.5
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.5"
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.5"
@@ -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():
@@ -8867,7 +8875,8 @@ def spring_audit_cmd(
8867
8875
  help=(
8868
8876
  "Bounded summary: caps `findings` and the per-endpoint security projection "
8869
8877
  "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."
8878
+ "whole population. Stdout also switches to this form only when the full "
8879
+ "JSON exceeds its 50 KB budget; use --output for the complete report."
8871
8880
  ),
8872
8881
  ),
8873
8882
  table: bool = typer.Option(
@@ -9204,17 +9213,19 @@ def spring_audit_cmd(
9204
9213
  "phase that never ran is not evidence there is none."
9205
9214
  )
9206
9215
 
9216
+ _full_data = combined.to_dict()
9207
9217
  _bounded_default = (
9208
9218
  not compact and output_path is None and format == "json" and not table
9219
+ and len(_json.dumps(_full_data, ensure_ascii=False).encode("utf-8")) > 50_000
9209
9220
  )
9210
- data = (
9211
- combined.to_compact_dict()
9212
- if compact or _bounded_default
9213
- else combined.to_dict()
9214
- )
9221
+ data = combined.to_compact_dict() if compact or _bounded_default else _full_data
9215
9222
  if _bounded_default:
9216
9223
  data.pop("mode", None)
9217
9224
  data["default_output"] = "bounded"
9225
+ data.pop("compact_note", None)
9226
+ data["stdout_budget_note"] = (
9227
+ "Capped by the stdout payload budget; use --output <file> for the complete report."
9228
+ )
9218
9229
  data.setdefault("metadata", {})["cache_layers"] = _cache_state
9219
9230
  if _stopped_early:
9220
9231
  # C3-87: the block is composed by `PhasedRun.status`, which is the
@@ -9249,17 +9260,19 @@ def spring_audit_cmd(
9249
9260
  elif format == "github-comment":
9250
9261
  output = _render_spring_audit_github_comment(combined, min_severity)
9251
9262
  else:
9263
+ _full_data = combined.to_dict()
9252
9264
  _bounded_default = (
9253
9265
  not compact and output_path is None and format == "json" and not table
9266
+ and len(_json.dumps(_full_data, ensure_ascii=False).encode("utf-8")) > 50_000
9254
9267
  )
9255
- data = (
9256
- combined.to_compact_dict()
9257
- if compact or _bounded_default
9258
- else combined.to_dict()
9259
- )
9268
+ data = combined.to_compact_dict() if compact or _bounded_default else _full_data
9260
9269
  if _bounded_default:
9261
9270
  data.pop("mode", None)
9262
9271
  data["default_output"] = "bounded"
9272
+ data.pop("compact_note", None)
9273
+ data["stdout_budget_note"] = (
9274
+ "Capped by the stdout payload budget; use --output <file> for the complete report."
9275
+ )
9263
9276
  output = _serialize_dict(data, format)
9264
9277
 
9265
9278
  # F-AV: our own readers ask for the count under either spelling — the break
@@ -12870,7 +12883,8 @@ def config_cmd(
12870
12883
  _answer.say(f"Declaration: none at {_declaration_path}")
12871
12884
  _answer.say(
12872
12885
  " Nothing is declared, so `data-exposure` answers "
12873
- "`answered: false` and no custom security annotation is recognised."
12886
+ "`answered: false`. Custom security annotations may still be inferred "
12887
+ "from usage with Likely confidence; declare them to make that policy Declared."
12874
12888
  )
12875
12889
  else:
12876
12890
  _answer.say(f"Declaration: {_declaration_path}")
@@ -6448,6 +6448,7 @@ def extract_java_endpoints(
6448
6448
  # calls. One candidate list keeps that work bounded and reusable below.
6449
6449
  _config_files = sorted(root.rglob("application*.*"))
6450
6450
  _excluded_test_source_routes = 0
6451
+ _excluded_test_patterns: set[str] = set()
6451
6452
  if not include_tests:
6452
6453
  _route_marker = _re.compile(
6453
6454
  r"@(GetMapping|PostMapping|PutMapping|DeleteMapping|PatchMapping|RequestMapping|GET|POST|PUT|DELETE|PATCH|Path)\b"
@@ -6456,6 +6457,15 @@ def extract_java_endpoints(
6456
6457
  _candidate_rel = _rel(_candidate)
6457
6458
  if not is_test_source_file(_candidate_rel):
6458
6459
  continue
6460
+ _candidate_parts = _candidate_rel.lower().split("/")
6461
+ if "test-suite" in _candidate_parts:
6462
+ _excluded_test_patterns.add("test-suite/")
6463
+ elif "src/test/" in _candidate_rel.lower():
6464
+ _excluded_test_patterns.add("src/test/")
6465
+ elif "src/it/" in _candidate_rel.lower():
6466
+ _excluded_test_patterns.add("src/it/")
6467
+ else:
6468
+ _excluded_test_patterns.add("test-source classification")
6459
6469
  try:
6460
6470
  _excluded_test_source_routes += len(
6461
6471
  _route_marker.findall(_candidate.read_text(encoding="utf-8", errors="replace"))
@@ -6962,6 +6972,11 @@ def extract_java_endpoints(
6962
6972
  result["test_util_included"] = len(_test_util_endpoints)
6963
6973
  if _excluded_test_source_routes:
6964
6974
  result["test_source_excluded"] = _excluded_test_source_routes
6975
+ result["test_source_excluded_detail"] = {
6976
+ "count": _excluded_test_source_routes,
6977
+ "patterns_matched": sorted(_excluded_test_patterns),
6978
+ "basis": "path classification; use --include-tests to include these routes",
6979
+ }
6965
6980
  result.setdefault("notes", []).append(
6966
6981
  f"{_excluded_test_source_routes} route annotation(s) from test source trees "
6967
6982
  "were excluded; use --include-tests to include them."
@@ -7187,6 +7202,10 @@ _DISPLAY_INDIRECT_CALLERS = 50
7187
7202
  HUB_CALLER_THRESHOLD = 500
7188
7203
  HUB_SAMPLE_SIZE = 20
7189
7204
  HUB_SAMPLE_DEPTH = 2
7205
+ # A frontier is a hub for this query when it materially exceeds the target's
7206
+ # own fan-in, even if it does not reach the global safety cap above.
7207
+ HUB_RELATIVE_FANIN_MULTIPLIER = 3
7208
+ HUB_RELATIVE_FANIN_FLOOR = 20
7190
7209
 
7191
7210
 
7192
7211
  def _symbol_listing_advice(root: "Optional[Path]") -> str:
@@ -7339,21 +7358,15 @@ def compute_blast_radius(
7339
7358
  _rg_index = _class_method_index(reverse_graph)
7340
7359
 
7341
7360
  def _walk_callers(_node: str) -> list[str]:
7342
- """Expand method frontiers through their owning class as well.
7361
+ """Return the one-authority caller set for this exact frontier node.
7343
7362
 
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.
7363
+ `caller_reach` expands class nodes through their method keys, which is
7364
+ required for DI-normalized class frontiers. It deliberately does not
7365
+ invert that containment for a method: callers of `Locator#getA` are a
7366
+ subset of callers of `Locator`, and adding the latter leaks users of
7367
+ every other locator accessor into the blast cone.
7348
7368
  """
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))
7369
+ return _all_callers_from_rg(_node, reverse_graph, _rg_index, _impl_graph)
7357
7370
  # Callers inject the interface, not the implementation, so a walk that does not
7358
7371
  # know the implements relation stops one hop short of every DI consumer. Without
7359
7372
  # it this traversal reached 200 classes and **no controller at all** for
@@ -7473,6 +7486,26 @@ def compute_blast_radius(
7473
7486
  if _effective_depth > 1:
7474
7487
  queue.append((c, 1))
7475
7488
 
7489
+ # A locator accessor can be a narrow method frontier on a broad class hub.
7490
+ # Measure the owner only to classify that frontier, never to expand it: the
7491
+ # latter would merge callers of unrelated accessors (Q-01).
7492
+ _seed_fan_in = max(1, len(set(direct_callers)))
7493
+ _hub_frontier_cutoff = max(
7494
+ HUB_RELATIVE_FANIN_FLOOR,
7495
+ _seed_fan_in * HUB_RELATIVE_FANIN_MULTIPLIER,
7496
+ )
7497
+
7498
+ def _is_hub_frontier(_node: str) -> bool:
7499
+ _fan_in = len(_walk_callers(_node))
7500
+ if "#" in _node:
7501
+ _fan_in = max(
7502
+ _fan_in,
7503
+ len(_all_callers_from_rg(
7504
+ _enclosing_class(_node), reverse_graph, _rg_index, _impl_graph
7505
+ )),
7506
+ )
7507
+ return _fan_in > _hub_frontier_cutoff
7508
+
7476
7509
  # BFS for indirect callers
7477
7510
  indirect_callers: list[str] = []
7478
7511
  _indirect_parent: dict[str, str] = {}
@@ -7484,7 +7517,7 @@ def compute_blast_radius(
7484
7517
  if depth >= _effective_depth:
7485
7518
  continue
7486
7519
  _node_callers = _walk_callers(node)
7487
- if len(_node_callers) > _HUB_CALLER_THRESHOLD:
7520
+ if _is_hub_frontier(node):
7488
7521
  _hub_frontier_nodes.add(node)
7489
7522
  for caller in _node_callers:
7490
7523
  if caller not in visited:
@@ -7735,7 +7768,7 @@ def compute_blast_radius(
7735
7768
  if _depth >= _effective_depth:
7736
7769
  continue
7737
7770
  _next = _walk_callers(_node)
7738
- if len(_next) > _HUB_CALLER_THRESHOLD:
7771
+ if _is_hub_frontier(_node):
7739
7772
  continue
7740
7773
  for _caller in _next:
7741
7774
  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,12 +83,12 @@ 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
+ return [], 0
92
92
  classes: "dict[str, str]" = {}
93
93
  for elem in root.iter():
94
94
  if _local(elem.tag) != "servlet":
@@ -98,10 +98,17 @@ def _from_web_xml(xml_path: Path, relative: str) -> "list[ServletMapping]":
98
98
  if name:
99
99
  classes[name] = klass
100
100
  out: "list[ServletMapping]" = []
101
+ undeclared_servlet_mappings = 0
101
102
  for elem in root.iter():
102
103
  if _local(elem.tag) != "servlet-mapping":
103
104
  continue
104
105
  name = _child_text(elem, "servlet-name")
106
+ if not name or name not in classes:
107
+ undeclared_servlet_mappings += sum(
108
+ 1 for child in elem.iter()
109
+ if _local(child.tag) == "url-pattern" and (child.text or "").strip()
110
+ )
111
+ continue
105
112
  for child in elem.iter():
106
113
  if _local(child.tag) != "url-pattern" or not (child.text or "").strip():
107
114
  continue
@@ -122,10 +129,13 @@ def _from_web_xml(xml_path: Path, relative: str) -> "list[ServletMapping]":
122
129
  pattern_match = re.search(r"<url-pattern\b[^>]*>\s*([^<]+?)\s*</url-pattern>", block, re.DOTALL | re.IGNORECASE)
123
130
  if not pattern_match:
124
131
  continue
132
+ servlet_name = name_match.group(1).strip() if name_match else ""
133
+ if not servlet_name or servlet_name not in classes:
134
+ continue
125
135
  candidate = ServletMapping(
126
136
  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 "",
137
+ servlet_name=servlet_name,
138
+ servlet_class=classes[servlet_name],
129
139
  source_file=relative,
130
140
  source="web.xml",
131
141
  )
@@ -133,7 +143,7 @@ def _from_web_xml(xml_path: Path, relative: str) -> "list[ServletMapping]":
133
143
  out.append(candidate)
134
144
  except Exception:
135
145
  pass
136
- return out
146
+ return out, undeclared_servlet_mappings
137
147
 
138
148
 
139
149
  def _from_registration_beans(java_path: Path, relative: str) -> "list[ServletMapping]":
@@ -178,6 +188,7 @@ def build_servlet_surface(
178
188
 
179
189
  root = Path(root)
180
190
  mappings: "list[ServletMapping]" = []
191
+ undeclared_servlet_mappings = 0
181
192
  if files is None:
182
193
  candidates: list[Path] = []
183
194
  for directory, dirs, names in os.walk(root):
@@ -209,7 +220,9 @@ def build_servlet_surface(
209
220
  except ValueError:
210
221
  relative = Path(path).as_posix()
211
222
  if path.name == "web.xml":
212
- mappings.extend(_from_web_xml(path, relative))
223
+ parsed, omitted = _from_web_xml(path, relative)
224
+ mappings.extend(parsed)
225
+ undeclared_servlet_mappings += omitted
213
226
  elif path.suffix == ".java":
214
227
  mappings.extend(_from_registration_beans(path, relative))
215
228
 
@@ -221,6 +234,7 @@ def build_servlet_surface(
221
234
  _unique: dict[str, ServletMapping] = {}
222
235
  for mapping in sorted(mappings, key=lambda m: (m.url_pattern, m.source_file, m.source)):
223
236
  _unique.setdefault(mapping.url_pattern, mapping)
237
+ declared_mappings = list(mappings)
224
238
  mappings = list(_unique.values())
225
239
  patterns = sorted(_unique)
226
240
  by_source: "dict[str, int]" = {}
@@ -232,15 +246,25 @@ def build_servlet_surface(
232
246
  )]
233
247
  shown = rows if limit is None else rows[:limit]
234
248
 
249
+ display_omitted = len(rows) - len(shown)
250
+ omitted_total = undeclared_servlet_mappings + display_omitted
251
+ omitted_by_reason: dict[str, int] = {}
252
+ if undeclared_servlet_mappings:
253
+ omitted_by_reason["servlet_not_declared"] = undeclared_servlet_mappings
254
+ if display_omitted:
255
+ omitted_by_reason["display_limit"] = display_omitted
256
+
235
257
  return {
236
258
  "schema_version": SCHEMA_VERSION,
237
259
  "population_key": POPULATION,
238
260
  "total_url_patterns": len(patterns),
239
- "declarations": len(mappings),
261
+ "declarations": len(declared_mappings),
262
+ "declarations_unit": "live servlet-mapping declarations before URL-pattern deduplication",
240
263
  "by_source": dict(sorted(by_source.items())),
241
264
  "url_patterns": patterns,
242
265
  "mappings": shown,
243
- "mappings_omitted": len(rows) - len(shown),
266
+ "mappings_omitted": omitted_total,
267
+ "mappings_omitted_by_reason": omitted_by_reason,
244
268
  "population": (
245
269
  "servlet mappings declared in `web.xml` or registered as a "
246
270
  "`ServletRegistrationBean` — HTTP surface that is NOT a Spring handler "
@@ -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
File without changes
File without changes