sourcecode 5.8.4__tar.gz → 5.8.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.

Potentially problematic release.


This version of sourcecode might be problematic. Click here for more details.

Files changed (374) hide show
  1. {sourcecode-5.8.4 → sourcecode-5.8.5}/CHANGELOG.md +117 -5
  2. {sourcecode-5.8.4 → sourcecode-5.8.5}/PKG-INFO +3 -3
  3. {sourcecode-5.8.4 → sourcecode-5.8.5}/README.md +2 -2
  4. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/CACHE.md +13 -7
  5. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/DEFECT-LEDGER.md +13 -8
  6. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/DEVELOPMENT-ROADMAP.md +195 -0
  7. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/EXECUTIVE-ROADMAP.md +43 -12
  8. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/USER_GUIDE.md +1 -1
  9. {sourcecode-5.8.4 → sourcecode-5.8.5}/pyproject.toml +1 -1
  10. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/__init__.py +1 -1
  11. sourcecode-5.8.5/src/sourcecode/breaking_changes.py +292 -0
  12. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/cache.py +33 -1
  13. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/cli.py +69 -13
  14. sourcecode-5.8.5/src/sourcecode/output_ceiling.py +500 -0
  15. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/parse_cache.py +147 -0
  16. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/release_info.py +1 -1
  17. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/repository_ir.py +20 -0
  18. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/schema_registry.py +25 -0
  19. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/timeline.py +35 -4
  20. sourcecode-5.8.4/src/sourcecode/breaking_changes.py +0 -163
  21. sourcecode-5.8.4/src/sourcecode/output_ceiling.py +0 -248
  22. {sourcecode-5.8.4 → sourcecode-5.8.5}/.github/workflows/build-windows.yml +0 -0
  23. {sourcecode-5.8.4 → sourcecode-5.8.5}/.github/workflows/perf-gate.yml +0 -0
  24. {sourcecode-5.8.4 → sourcecode-5.8.5}/.gitignore +0 -0
  25. {sourcecode-5.8.4 → sourcecode-5.8.5}/.ruff.toml +0 -0
  26. {sourcecode-5.8.4 → sourcecode-5.8.5}/CLAUDE.md +0 -0
  27. {sourcecode-5.8.4 → sourcecode-5.8.5}/CONTRIBUTING.md +0 -0
  28. {sourcecode-5.8.4 → sourcecode-5.8.5}/LICENSE +0 -0
  29. {sourcecode-5.8.4 → sourcecode-5.8.5}/NOTICE +0 -0
  30. {sourcecode-5.8.4 → sourcecode-5.8.5}/SECURITY.md +0 -0
  31. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/COMMERCIAL-OFFER.md +0 -0
  32. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/CUSTOMER-VALIDATION.md +0 -0
  33. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/DEMO-5MIN.md +0 -0
  34. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/EXECUTION-PLAN-12MO.md +0 -0
  35. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/MANUAL-USUARIO.md +0 -0
  36. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/MODERNIZATION.md +0 -0
  37. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/PRODUCT_AUDIT.md +0 -0
  38. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/PRODUCT_IDENTITY.md +0 -0
  39. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/PRODUCT_TIERS.md +0 -0
  40. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/RC1-CHECKLIST.md +0 -0
  41. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/RELEASE-CHECKLIST.md +0 -0
  42. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/CATALOG-retrieval-intents.md +0 -0
  43. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/DESIGN-knowledge-provider-integration.md +0 -0
  44. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/DESIGN-p5.3b-statement-surface.md +0 -0
  45. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/DESIGN-p5.5-span-index.md +0 -0
  46. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/DESIGN-p6-semantic-impact-engine.md +0 -0
  47. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/DESIGN-p6-semantic-integration-engine.md +0 -0
  48. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/DESIGN-p7-semantic-services.md +0 -0
  49. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/DESIGN-robustness-security-inference.md +0 -0
  50. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/DESIGN-semantic-retrieval.md +0 -0
  51. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/EPV-p6-class-typeref.md +0 -0
  52. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/REPORT-p6-flow-analyzer-comparison.md +0 -0
  53. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/REPORT-p6-integration-detector-comparison.md +0 -0
  54. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/REPORT-p8-flow-analyzer-migration.md +0 -0
  55. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/REPORT-p9-integration-detector-migration.md +0 -0
  56. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/adr/0001-semantic-ir.md +0 -0
  57. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/adr/0002-source-evidence-boundary.md +0 -0
  58. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/adr/0003-architectural-characterization.md +0 -0
  59. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/adr/0004-semantic-retrieval.md +0 -0
  60. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/adr/0005-evidence-reconciliation.md +0 -0
  61. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/adr/0006-performance-harness.md +0 -0
  62. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/adr/0007-composition-semantics.md +0 -0
  63. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/adr/0008-answer-coherence.md +0 -0
  64. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/consumer-disposition-inventory.md +0 -0
  65. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/overview.md +0 -0
  66. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/product-architecture-review.md +0 -0
  67. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/product-strategy-layering.md +0 -0
  68. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/semantic-ir-consumer-matrix.md +0 -0
  69. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/architecture/semantic-ir-roadmap.md +0 -0
  70. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/baseline-ci.yml +0 -0
  71. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/commercial-pipeline-audit.md +0 -0
  72. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/contracts-ci.yml +0 -0
  73. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/contracts.md +0 -0
  74. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/eval/2026-06-15-petclinic-rest-issue11-vets-pagination.md +0 -0
  75. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/eval/2026-06-15-petclinic-rest-issue147-validation.md +0 -0
  76. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/eval/2026-06-15-petclinic-rest-workflow.md +0 -0
  77. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/eval/2026-06-15-phase18-openapi-surface.md +0 -0
  78. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/eval/2026-06-15-phase20-validation-surface.md +0 -0
  79. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/eval/2026-06-16-broadleaf-checkout-impact-fieldtest.md +0 -0
  80. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/eval/2026-06-16-petclinic-rest-issue11-weakness2-closed.md +0 -0
  81. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/eval/2026-06-16-spring-petclinic-issue2333-dead-vets.md +0 -0
  82. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/licensing-schema.md +0 -0
  83. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/migrate-check.md +0 -0
  84. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/README.md +0 -0
  85. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/REGRESSION-GATE.md +0 -0
  86. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/SUMMARY.md +0 -0
  87. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/alfresco__ask__cold.json +0 -0
  88. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/alfresco__ask__warm.json +0 -0
  89. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/broadleaf__ask__cold.json +0 -0
  90. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/broadleaf__ask__warm.json +0 -0
  91. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/eureka__ask__cold.json +0 -0
  92. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/eureka__ask__warm.json +0 -0
  93. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/jenkins__ask__cold.json +0 -0
  94. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/jenkins__ask__warm.json +0 -0
  95. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/jobrunr__ask__cold.json +0 -0
  96. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/jobrunr__ask__warm.json +0 -0
  97. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/keycloak__ask__cold.json +0 -0
  98. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/keycloak__ask__warm.json +0 -0
  99. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/neo4j__ask__cold.json +0 -0
  100. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/neo4j__ask__warm.json +0 -0
  101. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/ofbiz__ask__cold.json +0 -0
  102. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/ofbiz__ask__warm.json +0 -0
  103. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/openmrs__ask__cold.json +0 -0
  104. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/openmrs__ask__warm.json +0 -0
  105. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/petclinic__ask__cold.json +0 -0
  106. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.15/petclinic__ask__warm.json +0 -0
  107. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/SUMMARY.md +0 -0
  108. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/alfresco__ask__cold.json +0 -0
  109. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/alfresco__ask__warm.json +0 -0
  110. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/broadleaf__ask__cold.json +0 -0
  111. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/broadleaf__ask__warm.json +0 -0
  112. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/eureka__ask__cold.json +0 -0
  113. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/eureka__ask__warm.json +0 -0
  114. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/jenkins__ask__cold.json +0 -0
  115. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/jenkins__ask__warm.json +0 -0
  116. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/jobrunr__ask__cold.json +0 -0
  117. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/jobrunr__ask__warm.json +0 -0
  118. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/keycloak__ask__cold.json +0 -0
  119. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/keycloak__ask__warm.json +0 -0
  120. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/neo4j__ask__cold.json +0 -0
  121. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/neo4j__ask__warm.json +0 -0
  122. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/ofbiz__ask__cold.json +0 -0
  123. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/ofbiz__ask__warm.json +0 -0
  124. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/openmrs__ask__cold.json +0 -0
  125. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/openmrs__ask__warm.json +0 -0
  126. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/petclinic__ask__cold.json +0 -0
  127. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/2.5.16/petclinic__ask__warm.json +0 -0
  128. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/gate-latest/README.md +0 -0
  129. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/gate-latest/broadleaf__ask__warm.json +0 -0
  130. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/gate-latest/broadleaf__endpoints__warm.json +0 -0
  131. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/gate-latest/broadleaf__migrate-check__warm.json +0 -0
  132. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/gate-latest/broadleaf__posture__warm.json +0 -0
  133. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/gate-latest/broadleaf__spring-audit__warm.json +0 -0
  134. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/gate-latest/broadleaf__validation__warm.json +0 -0
  135. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/perf/baselines/reference-best/README.md +0 -0
  136. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/posture.md +0 -0
  137. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/privacy.md +0 -0
  138. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/pro-experience-audit.md +0 -0
  139. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/schema.md +0 -0
  140. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/spec/envelope-v1.md +0 -0
  141. {sourcecode-5.8.4 → sourcecode-5.8.5}/docs/spring-audit-ci.yml +0 -0
  142. {sourcecode-5.8.4 → sourcecode-5.8.5}/scripts/compare_integration_engines.py +0 -0
  143. {sourcecode-5.8.4 → sourcecode-5.8.5}/scripts/customer_smoke_test.sh +0 -0
  144. {sourcecode-5.8.4 → sourcecode-5.8.5}/scripts/generate_jdk_exports.py +0 -0
  145. {sourcecode-5.8.4 → sourcecode-5.8.5}/scripts/perf_gate.py +0 -0
  146. {sourcecode-5.8.4 → sourcecode-5.8.5}/scripts/perf_harness.py +0 -0
  147. {sourcecode-5.8.4 → sourcecode-5.8.5}/scripts/sync_gate_anchors.py +0 -0
  148. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/adaptive_scanner.py +0 -0
  149. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/archetype.py +0 -0
  150. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/architectural_baseline.py +0 -0
  151. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/architectural_delta.py +0 -0
  152. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/architecture_analyzer.py +0 -0
  153. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/architecture_summary.py +0 -0
  154. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/ast_extractor.py +0 -0
  155. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/audit_report.py +0 -0
  156. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/baseline_autocapture.py +0 -0
  157. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/build_modules.py +0 -0
  158. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/bundled_docs.py +0 -0
  159. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/cache_model.py +0 -0
  160. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/call_surface.py +0 -0
  161. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/caller_metrics.py +0 -0
  162. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/caller_reach.py +0 -0
  163. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/canonical_ir.py +0 -0
  164. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/chain_rules.py +0 -0
  165. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/change_plan.py +0 -0
  166. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/cir_graphs.py +0 -0
  167. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/classifier.py +0 -0
  168. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/client_calls.py +0 -0
  169. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/code_notes_analyzer.py +0 -0
  170. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/compare.py +0 -0
  171. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/confidence_analyzer.py +0 -0
  172. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/constraint_diff.py +0 -0
  173. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/consumer_join.py +0 -0
  174. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/container_wiring.py +0 -0
  175. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/context_cache.py +0 -0
  176. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/context_graph.py +0 -0
  177. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/context_scorer.py +0 -0
  178. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/context_summarizer.py +0 -0
  179. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/contract_diff.py +0 -0
  180. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/contract_init.py +0 -0
  181. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/contract_model.py +0 -0
  182. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/contract_pipeline.py +0 -0
  183. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/coverage_parser.py +0 -0
  184. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/data_exposure.py +0 -0
  185. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/data_labels.py +0 -0
  186. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/declarations.py +0 -0
  187. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/defect_identity.py +0 -0
  188. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/degradation.py +0 -0
  189. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/dependency_analyzer.py +0 -0
  190. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/deployment_prefix.py +0 -0
  191. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detach.py +0 -0
  192. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/__init__.py +0 -0
  193. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/base.py +0 -0
  194. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/csproj_parser.py +0 -0
  195. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/dart.py +0 -0
  196. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/dotnet.py +0 -0
  197. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/elixir.py +0 -0
  198. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/go.py +0 -0
  199. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/heuristic.py +0 -0
  200. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/hybrid.py +0 -0
  201. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/java.py +0 -0
  202. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/jvm_ext.py +0 -0
  203. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/nodejs.py +0 -0
  204. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/parsers.py +0 -0
  205. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/php.py +0 -0
  206. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/project.py +0 -0
  207. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/python.py +0 -0
  208. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/ruby.py +0 -0
  209. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/rust.py +0 -0
  210. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/systems.py +0 -0
  211. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/terraform.py +0 -0
  212. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/detectors/tooling.py +0 -0
  213. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/doc_analyzer.py +0 -0
  214. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/dynamic_argument_surface.py +0 -0
  215. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/empty_reach.py +0 -0
  216. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/endpoint_disposition.py +0 -0
  217. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/endpoint_literals.py +0 -0
  218. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/endpoint_metrics.py +0 -0
  219. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/entrypoint_classifier.py +0 -0
  220. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/env_analyzer.py +0 -0
  221. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/envelope.py +0 -0
  222. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/environment_resolution.py +0 -0
  223. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/error_schema.py +0 -0
  224. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/evidence_provider.py +0 -0
  225. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/execution_plan.py +0 -0
  226. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/explain.py +0 -0
  227. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/facts/__init__.py +0 -0
  228. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/facts/registry.json +0 -0
  229. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/file_chunker.py +0 -0
  230. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/file_classifier.py +0 -0
  231. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/filter_surface.py +0 -0
  232. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/format_contract.py +0 -0
  233. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/fqn_utils.py +0 -0
  234. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/gate_anchors.py +0 -0
  235. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/git_analyzer.py +0 -0
  236. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/git_checkout.py +0 -0
  237. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/graph_analyzer.py +0 -0
  238. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/graph_evidence.py +0 -0
  239. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/hibernate_strat.py +0 -0
  240. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/identity_fallback.py +0 -0
  241. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/integration_coordinates.py +0 -0
  242. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/jdk_exports.py +0 -0
  243. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/license.py +0 -0
  244. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/manifest_cache.py +0 -0
  245. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/__init__.py +0 -0
  246. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/dependency_import.py +0 -0
  247. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  248. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  249. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  250. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  251. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  252. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/orchestrator.py +0 -0
  253. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/registry.py +0 -0
  254. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/runner.py +0 -0
  255. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp/server.py +0 -0
  256. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/mcp_nudge.py +0 -0
  257. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/metrics_analyzer.py +0 -0
  258. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/migrate_check.py +0 -0
  259. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/migration_blast.py +0 -0
  260. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/non_coverage.py +0 -0
  261. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/openapi_surface.py +0 -0
  262. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/openrewrite_recipe.py +0 -0
  263. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/output_bounds.py +0 -0
  264. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/output_budget.py +0 -0
  265. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/output_encoding.py +0 -0
  266. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/output_sink.py +0 -0
  267. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/parallel.py +0 -0
  268. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/partial_contract.py +0 -0
  269. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/path_admission.py +0 -0
  270. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/path_filters.py +0 -0
  271. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/perf.py +0 -0
  272. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/phased_run.py +0 -0
  273. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/pipe_contract.py +0 -0
  274. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/posture.py +0 -0
  275. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/pr_comment_renderer.py +0 -0
  276. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/pr_impact.py +0 -0
  277. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/prepare_context.py +0 -0
  278. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/product_info.py +0 -0
  279. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/progress.py +0 -0
  280. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/provenance.py +0 -0
  281. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/ranking_engine.py +0 -0
  282. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/readiness_timeline.py +0 -0
  283. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/readonly.py +0 -0
  284. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/reconciliation.py +0 -0
  285. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/redactor.py +0 -0
  286. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/reference_facts.py +0 -0
  287. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/relevance_scorer.py +0 -0
  288. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/remedies.py +0 -0
  289. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/rename_refactor.py +0 -0
  290. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/repo_classifier.py +0 -0
  291. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/__init__.py +0 -0
  292. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/context.py +0 -0
  293. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/errors.py +0 -0
  294. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/executor.py +0 -0
  295. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/planner.py +0 -0
  296. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/query.py +0 -0
  297. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/request.py +0 -0
  298. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/resolution.py +0 -0
  299. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/result.py +0 -0
  300. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/retriever.py +0 -0
  301. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/runtime.py +0 -0
  302. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/steps.py +0 -0
  303. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  304. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/steps_graph.py +0 -0
  305. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/steps_impact.py +0 -0
  306. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/steps_intf.py +0 -0
  307. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/steps_struct.py +0 -0
  308. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  309. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/ris.py +0 -0
  310. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/risk.py +0 -0
  311. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/risk_model.py +0 -0
  312. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/rule_catalog.py +0 -0
  313. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/rule_pass.py +0 -0
  314. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/runs.py +0 -0
  315. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/runtime_classifier.py +0 -0
  316. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/sarif.py +0 -0
  317. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/scanner.py +0 -0
  318. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/schema.py +0 -0
  319. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/schemas/core-analysis-v1.schema.json +0 -0
  320. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/schemas/endpoints-v1.schema.json +0 -0
  321. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  322. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/schemas/pr-impact-v1.schema.json +0 -0
  323. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/schemas/verify-edit-v1.schema.json +0 -0
  324. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/schemas/verify-v1.schema.json +0 -0
  325. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/security_chain.py +0 -0
  326. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/security_config.py +0 -0
  327. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/security_config_scan.py +0 -0
  328. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/security_posture.py +0 -0
  329. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/semantic_analyzer.py +0 -0
  330. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/semantic_impact_engine.py +0 -0
  331. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/semantic_integration_engine.py +0 -0
  332. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/semantic_services.py +0 -0
  333. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/serializer.py +0 -0
  334. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/servlet_surface.py +0 -0
  335. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/source_text.py +0 -0
  336. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/spring_event_topology.py +0 -0
  337. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/spring_findings.py +0 -0
  338. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/spring_impact.py +0 -0
  339. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/spring_model.py +0 -0
  340. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/spring_profiles.py +0 -0
  341. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/spring_properties.py +0 -0
  342. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/spring_security_audit.py +0 -0
  343. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/spring_semantic.py +0 -0
  344. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/spring_tx_analyzer.py +0 -0
  345. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/sql_taint.py +0 -0
  346. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/summarizer.py +0 -0
  347. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/target_admission.py +0 -0
  348. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/telemetry/__init__.py +0 -0
  349. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/telemetry/config.py +0 -0
  350. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/telemetry/consent.py +0 -0
  351. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/telemetry/events.py +0 -0
  352. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/telemetry/filters.py +0 -0
  353. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/telemetry/transport.py +0 -0
  354. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/test_gap_ranking.py +0 -0
  355. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/test_sources.py +0 -0
  356. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/text_input.py +0 -0
  357. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/token_estimate.py +0 -0
  358. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/tree_utils.py +0 -0
  359. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/type_usage_surface.py +0 -0
  360. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/validation_inference.py +0 -0
  361. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/validation_surface.py +0 -0
  362. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/verify_edit.py +0 -0
  363. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/verify_repo.py +0 -0
  364. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/verify_rules.py +0 -0
  365. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/version_check.py +0 -0
  366. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/waivers.py +0 -0
  367. {sourcecode-5.8.4 → sourcecode-5.8.5}/src/sourcecode/workspace.py +0 -0
  368. {sourcecode-5.8.4 → sourcecode-5.8.5}/supabase/functions/README.md +0 -0
  369. {sourcecode-5.8.4 → sourcecode-5.8.5}/supabase/functions/get-license/index.ts +0 -0
  370. {sourcecode-5.8.4 → sourcecode-5.8.5}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  371. {sourcecode-5.8.4 → sourcecode-5.8.5}/supabase/functions/telemetry/index.ts +0 -0
  372. {sourcecode-5.8.4 → sourcecode-5.8.5}/supabase/sql/license_event_ordering.sql +0 -0
  373. {sourcecode-5.8.4 → sourcecode-5.8.5}/supabase/sql/licensing_schema.sql +0 -0
  374. {sourcecode-5.8.4 → sourcecode-5.8.5}/supabase/sql/telemetry_events.sql +0 -0
@@ -2,6 +2,86 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.8.5] — 2026-08-18
6
+
7
+ *Six rows, and the theme is that four of them were closed by **measuring** rather
8
+ than by reasoning — including the one that had been closed twice before on
9
+ reasoning. `ASK-09`'s fifth cycle turned out to be one line of behaviour nobody
10
+ had measured: the hint derived with no Click context returns the field's sentence
11
+ byte for byte, about a command that declares two bounding flags. The `+66 %`
12
+ regression `ASK-17` was holding does not reproduce under controlled conditions
13
+ (+2,3 %), and the mechanism it suspected is worth ~3 %. `timeline`'s 198 s is 83 %
14
+ the analysis it re-runs per tree and 17 % git. And two of the six were found by
15
+ the measurements themselves rather than reported by anyone.*
16
+
17
+ ### Fixed
18
+ - **A ceiling hint that could not read the command's flags now says so** (ASK-09,
19
+ fifth cycle, second false closure). Measured on the previous tree:
20
+ `build_hint(None)` — the guard reached with no Click context — returns *"No
21
+ smaller inline variant is available with the current flags"*, the field's
22
+ sentence byte for byte, about a command that declares `--compact` and
23
+ `--limit`. `_declared_and_used(None)` answered `([], set())` and the prefix rule
24
+ spelled that *absence of a reading* as a *claim about the command*; a
25
+ `CliRunner` always has a context, which is why nine batteries stayed green
26
+ through three cycles. Four halves close it: a **second, context-free authority**
27
+ (the command tree resolved from `sys.argv`, consulted only when the context
28
+ yields no bounding flag); **observability** — every `OUTPUT_TOO_LARGE` refusal
29
+ publishes `hint_basis` (`derived_from`, `command`, `declared_flags`,
30
+ `flags_already_passed`, `context_found`), AST-enforced across all four guards;
31
+ a **loud failure** when nothing could be resolved, instead of a confident
32
+ falsehood; and the collateral the row named — the `--force` bypass read the same
33
+ thread-local and degraded on the identical path. On the field's own subject
34
+ (`tutorials`, 24 074 `.java`, real 50K ceiling): `estimated_tokens: 241 527`,
35
+ 0,06 % from the reporter's 241 394, hint naming both flags.
36
+ - **The shared parse store names which repository is cold** (ASK-17). It is
37
+ content-addressed across repositories, so it published one total for every
38
+ repository the machine had analysed and no attribution — and the field paid
39
+ 457,6 s on a cold `--compact` to discover an eviction. Both halves were already
40
+ held and thrown away: the walk knows the repository and computes the key. The
41
+ run records the pairs, `cache status` intersects them with the entry walk it
42
+ already performs, and coverage is published per repository. The index is merged
43
+ rather than replaced (a `--changed-only` run must not publish *"12 of 12 cached"*
44
+ about a cold repository), carries its basis, and is neither an entry nor
45
+ evictable.
46
+ - **`timeline` publishes where its clock goes** (C3-128). Attribution before
47
+ tuning, the rule ASK-16 proved: BroadleafCommerce, `--since HEAD~5 --watch
48
+ posture`, wall 38 921 ms — `measure:posture` 82,9 %, `materialise_tree` 13,8 %,
49
+ `release_tree` 3,3 %, unaccounted 0,0 %. So five sixths of the cost is the
50
+ analysis re-run per tree by construction; there is no timeline-specific overhead
51
+ to remove, and the gain belongs to the metric being sampled.
52
+ - **The declared `schema_version` renames get a date** (B20). `BC-002`: announced
53
+ 5.8.5, takes effect 6.0.0, printed by `ask schema breaking-changes-v1`, carried
54
+ in `## Upgrading`, and projected into `schemas-v1` as `pending_renames_window` —
55
+ one fact, two surfaces. The registry's policy is generalised rather than
56
+ loosened: a declared change is `kind: exit_code` (must move an exit code) or
57
+ `kind: contract` (must move a published value *and* name when). Correction to
58
+ the reported cause: **seven** shapes spell their version `1.0`, not two.
59
+ - **Every cache store names the variable that moves it** (E-37, found while
60
+ measuring ASK-17). Two variables cover three stores whose defaults are siblings
61
+ under one home, so an operator who empties *the cache* can empty one and be
62
+ served answers out of the other — which is exactly what happened: 2,4 s against
63
+ 113 s, with an empty parse store. `cache status` now publishes `base_env` per
64
+ store from one authority and prints it beside each path; `docs/CACHE.md` carries
65
+ a column instead of a sentence. The assertions are behavioural.
66
+
67
+ ### Measured, not fixed
68
+ - **The `+66 %` cold regression `ASK-17` was holding does not reproduce.**
69
+ `tutorials` (24 073 `.java`), root `--compact`, both cache bases isolated and
70
+ emptied between runs, `ASK_PARSE_CACHE_MAX_MB` fixed: 5.7.2 112,0 / 112,6 s
71
+ against 113,8 / 115,2 s here, **+2,3 %**. With the store pre-filled to 489 MB
72
+ against the default 512 MB budget — the audit's own condition, isolated as the
73
+ only variable — **117,0 s, +2,7 %**. So permanent LRU sweeping costs ~3 %, not
74
+ 66 %. What the measurement *does* confirm is the row's own claim: one
75
+ 24 073-file repository leaves 470 MB in a 512 MB budget, so a second repository
76
+ of any size guarantees a permanently sweeping store. Sizing rule, measured:
77
+ ~20 KB of store per Java file.
78
+
79
+ ### Found on the way
80
+ - **B21**: six of the thirteen shapes being renamed name no producing command
81
+ (`emitted_by: []`) beside seven that do, which reads as *"nothing emits this"* —
82
+ i.e. tells the affected consumer it is unaffected. Each row now carries
83
+ `emitted_by_basis` naming the gap instead of shipping the empty list as a fact.
84
+
5
85
  ## [5.8.4] — 2026-08-17
6
86
 
7
87
  *Four rows, and three of them were reported with the wrong cause — which is the
@@ -213,11 +293,43 @@ cap the message names.*
213
293
 
214
294
  ## Upgrading — incompatible changes
215
295
 
216
- *A change that turns `exit 0` into `exit 1` is a breaking change, and it belongs
217
- here rather than in a line of a release section. Declared in
218
- `sourcecode.breaking_changes`, printed by `ask schema breaking-changes-v1`, and
219
- named in the `--help` of every command it can fire on, so an upgrade can be
220
- planned instead of discovered by a red pipeline.*
296
+ *A change that breaks a caller belongs here rather than in a line of a release
297
+ section. Two kinds qualify: one that turns `exit 0` into `exit 1`, and one that
298
+ changes a **published value** while every exit code stays 0. Declared in
299
+ `sourcecode.breaking_changes`, printed by `ask schema breaking-changes-v1`, and
300
+ for the exit-code kind — named in the `--help` of every command it can fire on,
301
+ so an upgrade can be planned instead of discovered by a red pipeline.*
302
+
303
+ ### BC-002 — `schema_version` values are renamed to their canonical names (announced 5.8.5, takes effect 6.0.0)
304
+
305
+ **Who is affected.** Any consumer that dispatches on the emitted
306
+ `schema_version` string, or validates a payload by matching it. **No exit code
307
+ moves**: a run that answered 0 still answers 0, and what changes is a value
308
+ inside the payload.
309
+
310
+ Thirteen shapes spell their version in a form that does not follow the naming
311
+ rule (`1.0`, `1.1`, `final-v1`, …). Each already carries the name it becomes —
312
+ `ask schema schemas-v1` publishes them as `pending_renames`, and the list is read
313
+ from the registry rather than copied here. **Seven of them spell it `1.0`
314
+ today** — `core-analysis-v1`, `impact-chain-v1`, `pr-impact-v1`,
315
+ `migration-blast-v1`, `spring-impact-v1`, `event-topology-v1` and
316
+ `test-gap-ranking-v1` — so that identifier does not name a contract on its own;
317
+ the registry publishes that as `ambiguous_identifiers`, and `ask schema 1.0`
318
+ resolves to all seven with the canonical name to pin for each.
319
+
320
+ **What was missing until now was the date.** The renames were declared without a
321
+ cut-off, so a consumer could read what would change and not when (B20). The
322
+ window: the emitted value is unchanged for the whole of 5.x, and becomes the
323
+ canonical name in **6.0.0**.
324
+
325
+ **Pin the canonical name today** — `ask schema <canonical>` already resolves, and
326
+ a consumer written against it needs no change in 6.0.0. That is the point of
327
+ announcing a major ahead. **Stop dispatching on `1.0`**: it is ambiguous by
328
+ construction, so match the canonical name or the `subject` the registry publishes
329
+ beside it.
330
+
331
+ The rename is deliberately **not** made early. Renaming an emitted value before
332
+ its window is exactly the incompatible change this policy exists to prevent.
221
333
 
222
334
  ### BC-001 — stdout over the output ceiling exits 1 (since 5.7.0)
223
335
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 5.8.4
3
+ Version: 5.8.5
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  Project-URL: Homepage, https://github.com/HarounDominique/sourcecode
6
6
  Project-URL: Repository, https://github.com/HarounDominique/sourcecode
@@ -49,7 +49,7 @@ Description-Content-Type: text/markdown
49
49
 
50
50
  **Context · Impact · Migration · Architecture · Review — everything from one structural model.**
51
51
 
52
- ![Version](https://img.shields.io/badge/version-5.8.4-blue)
52
+ ![Version](https://img.shields.io/badge/version-5.8.5-blue)
53
53
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
54
54
 
55
55
  Copyright (c) 2026 Dominique Haroun. All rights reserved. ASK Engine 5.7.0 and
@@ -137,7 +137,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
137
137
  # pip / pipx
138
138
  pipx install sourcecode # or: pip install sourcecode
139
139
 
140
- ask version # ask 5.8.4 — and, on a build that has aged,
140
+ ask version # ask 5.8.5 — and, on a build that has aged,
141
141
  # how many releases have probably shipped since
142
142
  ```
143
143
 
@@ -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.8.4-blue)
7
+ ![Version](https://img.shields.io/badge/version-5.8.5-blue)
8
8
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
9
9
 
10
10
  Copyright (c) 2026 Dominique Haroun. All rights reserved. ASK Engine 5.7.0 and
@@ -92,7 +92,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
92
92
  # pip / pipx
93
93
  pipx install sourcecode # or: pip install sourcecode
94
94
 
95
- ask version # ask 5.8.4 — and, on a build that has aged,
95
+ ask version # ask 5.8.5 — and, on a build that has aged,
96
96
  # how many releases have probably shipped since
97
97
  ```
98
98
 
@@ -37,13 +37,19 @@ Two things invalidate everything, everywhere:
37
37
 
38
38
  ## Where it lives
39
39
 
40
- | Path | Holds |
41
- |---|---|
42
- | `~/.sourcecode/cache/<repo-id>/` | snapshot cache (`core-*`, `view-*`), the RIS, `prepare-context` task answers |
43
- | `~/.sourcecode/ctx-*.json.gz` | the shared Canonical IR |
44
- | `~/.sourcecode/parse-cache-v1/` | the per-file parse cache |
45
-
46
- `SOURCECODE_CACHE_DIR` and `SOURCECODE_CONTEXT_CACHE_DIR` move them. Nothing leaves the machine.
40
+ | Path | Holds | Moved by |
41
+ |---|---|---|
42
+ | `~/.sourcecode/cache/<repo-id>/` | snapshot cache (`core-*`, `view-*`), the RIS, `prepare-context` task answers | `SOURCECODE_CACHE_DIR` |
43
+ | `~/.sourcecode/ctx-*.json.gz` | the shared Canonical IR | `SOURCECODE_CONTEXT_CACHE_DIR` |
44
+ | `~/.sourcecode/parse-cache-v1/` | the per-file parse cache | `SOURCECODE_CONTEXT_CACHE_DIR` |
45
+
46
+ Two variables, three stores, and the column says which moves which — because the paths are
47
+ siblings under one home and the split is not derivable from them. **Redirecting or emptying one
48
+ and not the other leaves the other serving answers**: measured on 2026-08-18 while taking the
49
+ `ASK-17` A/B, a cold run with only `SOURCECODE_CONTEXT_CACHE_DIR` redirected answered from the
50
+ snapshot store in 2,4 s against 113 s, with an empty parse store — a cold measurement that had
51
+ silently become a warm one (E-37). `ask cache status` names the variable beside each store for
52
+ the same reason. Nothing leaves the machine.
47
53
 
48
54
  The cache is keyed to **the path you analysed**: `cache warm ./service-a` warms `service-a`, not
49
55
  the enclosing monorepo, and `cache status` / `cache clear` resolve to that same scope.
@@ -41,7 +41,7 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
41
41
  | B15 | **A write that could not finish left its temporary inside the caller's repository.** The payload goes to `.<name>.tmp-<pid>-<ns>` beside the destination and is renamed onto it — atomic, and correct — but when the rename failed nothing removed the temporary. Field matrix of five bad destinations on Windows: **13,9 MB of untracked files inside the repository** (2 757 334 B per attempt plus a 153 481 B `.partial`), as `??` in `git status`. Three edges came with it: a discard sink (`nul`, `NUL`, `CON`, `/dev/null`) was reported as an I/O error for successfully throwing output away; the OS `strerror` was interpolated into our English message, so the same matrix produced two `es-ES` messages and one English; and `-o ./missing/out.json` created the tree in silence while the hint told the caller to check that the directory exists. | 5.6.1 audit, re-reported and widened in the 5.7.2 audits | **High** — it is what breaks the read-only premise the product is sold on, and the auditor grades it blocking for any pre-CI security review | **closed 5.8.1** — one authority, `sourcecode.output_sink`. The temporary is removed in a **`finally`**, so the guarantee does not depend on enumerating the exit paths correctly (which is how five escape routes were missed). A discard sink answers `written: false`, `sink: "null-device"`, `exit 0`, is matched only on the platform that reserves the name (a POSIX file called `nul` is a file), and a run writing there keeps its checkpoint in the partials directory instead of `nul.partial`. The platform's message travels in `os_error: {raw, errno, errno_name, locale}`, never inside the contract sentence, and the error publishes `cleaned_up: [...]` — what the run took back with it, so "read-only" is checkable rather than trusted. Parent directories are created, deliberately, and the hint says so. `phased_run` writes through the same authority and registers its checkpoint as this run's, so a failed answer removes it. Regression `tests/test_output_sink_leaves_nothing_b15.py`, 12 assertions, including the end-to-end matrix leaving the tree byte-identical and a negative control that cleanup can only ever remove what this run wrote. |
42
42
  | ASK-10 | **A command with an output ceiling and no flag that can get under it.** `posture spring-boot` returned 355 032 B / `exit 0` in 5.6.1; the unified ceiling made it `exit 1` — the right call — and left no in-band way out: not `--limit`, not `--top-n`, not `--min-severity`, not `--compact`. The only routes were `--output FILE` (the answer leaves the reader's context) and `ASK_MAX_OUTPUT_TOKENS=0` (the guard off). `exit 1` on **2 of 8** corpus repositories, and `thingsboard` returned `exit 0` in 5.7.2 — so the trap closes as the product improves: the growth was **better detection**, `unresolved` 64 120 → 184 864 B because conditional beans now name `@ConditionalOnExpression` instead of a generic reason. `validation` inherits it. | 5.7.2 audits, escalated to high and blocking by the 5.8.0 re-audit | **High** — structural rather than incidental: every future gain in exhaustiveness pushes another command at the ceiling | **closed 5.8.1** — `posture` and `validation` declare `--limit N` and `--compact`, both already in the ceiling's vocabulary, so the hint offers them without being taught (verified: with the ceiling forced, both now answer *"Use --compact …, --limit N …"*). Bounding is one authority, `sourcecode.output_bounds`: it walks the payload so a `--diff` answer is bounded on **both** sides, touches lists and never counts (`summary.unresolved` is an int under the same name), and every cut publishes the `*_cap` contract — `{total, shown, omitted}` plus the registered effect — because a flag that cuts without declaring the cut is B14 again. **Nothing is bounded by default**: a payload that shrinks silently between releases is what AS-16 was filed for. Measured: `posture keycloak-config-cli` 53 115 → 4 605 B under `--compact`, summary byte-identical. The invariant is now derived from the CLI registry (`tests/test_bounding_flag_registry_ask10.py`): a command that can exceed the ceiling and declares no bounding flag fails the battery until somebody decides, with `cli.COMMANDS_WITHOUT_BOUNDING` carrying the exemptions and their reasons — including, on the record, the repository-scale commands that have not yet been measured against the ceiling. Behaviour battery `tests/test_output_bounds_ask10.py`, 13 assertions. |
43
43
  | B4 | **`verify-edit` reported two repository-wide builds beside two empty lists, and nothing in the payload could explain them.** Reported as the empty-diff short circuit failing: clean worktree, `changed_files: []`, `changed_build_files: []`, `head_model_cache_hit: true`, `model_built: true`, 23 468 ms with `ASK_PROGRESS` never leaving *diffing working tree vs HEAD*. | 5.6.1 audit, re-reported through the 5.7.2 and 5.8.0 audits | High | **closed 5.8.1 — and the reported cause is not the cause.** Measured on BroadleafCommerce (2 985 `.java`) with a clean tree: **0,47 s, `model_built: false`** — the short circuit (C3-32 / C3-102 / AS-13) fires, and the row's stated acceptance (`< 2 000 ms`, `model_built: false`, `verdict: pass`) already held. The reported shape reproduces exactly with **one untracked `zz.yml`**: both lists empty, `model_built: true`, **16,3 s**. `_can_move_a_verdict` admits any suffix an axis may read — deliberately, since C3-14 — so a `.yml` that is not `application*` is neither a changed `.java` nor a changed build file, and appeared in **no** published list. The change set was never empty; the payload could not say so. `analysis.model_forced_by: {count, paths, reason}` is that sentence, and `basis` no longer reads *"0 changed .java file(s) and 0 changed build file(s)"* beside two full builds. Regression `tests/test_verify_edit_names_what_forced_the_model_b4.py`, 5 assertions, including the negative control that a changed `.java` file is not explained twice. ⚠ Still open and now separable: the two-build cost itself when a forcing path exists (the parse cache serves the parse; the IR assembly is paid twice), tracked as the profiling half. |
44
- | ASK-09 / B16-sv | **Three output-ceiling guards written outside the authority that owns them.** Reported as `endpoints`, `cold-start` and `posture` claiming *"No smaller inline variant is available with the current flags"* while `--compact` fits (10 194 B ≈ 2,5 K tokens). | 5.7.2 audits (corpus `ASK-09`, `saint-server` `B16-sv` — one row, two origin IDs) | High (corpus: medium) | **closed 5.8.4 — does not reproduce, measured on the reporter's own subject, and the residue it was hiding is fixed.** `tutorials` (24 073 `.java`), no environment variable, the real 50K ceiling: `endpoints` answers *"Use --compact …, --limit N …"* at `estimated_tokens: 241 041`, `cold-start` answers *"Use --compact …"* at 230 518, and `posture` does not arise — 134 431 B ≈ 33K tokens, `exit 0`, no hint at all. Three cycles of byte-identical text were measured against a build, not against this tree. **What the row's rewritten acceptance did buy is the battery**: `tests/test_ceiling_hint_at_real_scale_ask09.py` builds a repository that genuinely overflows (1 200 handler mappings, ~107K tokens), *discovers* the population by running every path-only command that declares a bounding flag rather than listing them, and asserts that no such command denies a flag it declares — parametrised by (command, repository), never by (command, artificial ceiling). It is verified red: forcing the *"nothing smaller"* prefix fails it. It also refuses to be vacuous — the fixture must overflow, the population must contain the two commands the field named, and the flag the hint offers must actually get the answer under the ceiling when run. ⚠ **The residue the old fixture could not see**: `repo-ir`'s stdout guard compared against a constant bound at import, so `ASK_MAX_OUTPUT_TOKENS=0` — which `breaking-changes-v1` declares as restoring pre-5.7.0 behaviour *exactly*, and which `repo-ir`'s own hint offers — left it refusing while every other command printed, and its refusal published no `estimated_tokens`, no `ceiling_tokens` and no `estimate_basis`, so the number it refused on existed only inside an English sentence. Both now come from `output_ceiling.exceeds`, the authority: one refusal, one shape, one ceiling that the caller can actually move. Measured: `=0` now exits 0, a lowered ceiling bites, and the payload carries its measurement. **History, kept because the row was closed twice on it:** REOPENED 5.8.2 — the closure was verified against the wrong path: against the unified emit seam, with the ceiling forced, `endpoints` answers *"Use --compact …, --limit N …"* and `cold-start` *"Use --compact …"* — the derivation reads `ctx.command.params` and works. What did reproduce is the half the row itself names: `repo-ir` carried **two** hand-written hints (stdout and the separate MB guard on a written file) and `migrate-check` a **third**, `_migrate_check_output_hint` — a hand-maintained copy of `build_hint`, same list, same prefix rule. A hand-written escape list cannot know which flags the caller already passed, which is the reported defect seen from the other side. `output_ceiling` now owns the whole vocabulary: `--force` as a **bypass** (offered only where the command declares it), `--gzip` as a **file-only** bound, `ways_out(destination="file")` for the MB guard — where `--output FILE` is not an escape and the token env var does not apply — and the *"nothing smaller"* prefix decided by counting the **bounding** escapes rather than the sentence, so a command whose only remaining escape is `--force` no longer opens with "Use". `_migrate_check_output_hint` is deleted. Regression `tests/test_ceiling_hint_one_authority_ask09.py`, 7 assertions, two of which are structural: `cli.py` may not contain the prefix sentence, and every `OUTPUT_TOO_LARGE` hint in it must come from `build_hint` (AST-checked). ⚠ **Reopened by the 5.8.2 re-audits, which report it as `B16` (saint-server) and `ASK-09` (corpus) — one defect, two origin IDs, like `B16-sv` before it.** The fix landed and the *hint* half did not: `ask endpoints tutorials` (24 073 `.java`) still answers *"No smaller inline variant is available with the current flags"* at `estimated_tokens: 240 908`, while `--compact` (45 902 B ≈ 11,5 K) and `--limit 50` (66 765 B ≈ 16,7 K) both fit with 4× of margin and are both declared on `endpoints`. Byte-identical text since 5.7.2, third cycle. **The diagnosis is the valuable part and it is about our method, not the code**: the regression assertion forces a low ceiling (`ASK_MAX_OUTPUT_TOKENS=1`) over a *small* fixture payload, and the path that fails is the *natural* rebase of a 240 K-token payload against the real 50 K ceiling — so the battery validates one route while the defect lives in another, and a green test certified a defect the field reproduces in three consecutive cycles. `posture` received `--limit`/`--compact` in the same release this row was closed in and *does* answer correctly, which is why the derivation looked proven. Suspected: a short-circuit that decides the hint before `ctx.command.params` is consulted, reachable only at a size the fixture never reaches (compare `ASK_MAX_OUTPUT_TOKENS=2000 ask endpoints <small>` against `ask endpoints <24k-file repo>`). **Acceptance, restated so the same closure cannot repeat**: the hint names `--limit` and `--compact` with an unforced ceiling on a repository that genuinely exceeds 50 K, and the regression is parametrised by `(command, repository)` over a fixture that really overflows — never by `(command, artificial ceiling)`. |
44
+ | ASK-09 / B16-sv | **Three output-ceiling guards written outside the authority that owns them.** Reported as `endpoints`, `cold-start` and `posture` claiming *"No smaller inline variant is available with the current flags"* while `--compact` fits (10 194 B ≈ 2,5 K tokens). | 5.7.2 audits (corpus `ASK-09`, `saint-server` `B16-sv` — one row, two origin IDs) | High (corpus: medium) | **closed 5.8.5 — the fifth cycle diagnosed by measurement instead of by hypothesis, and the derivation given a second authority that no thread boundary can take away.** The ledger asked for the difference between our 241 041 tokens and the reporter's 241 394 to be found in *the environment or the entry point*, and for no third fixture. It was found in the entry point, and it was measured here, on `HEAD` before the fix: `output_ceiling.build_hint(None)` — the guard reached with **no Click context** — returned *"No smaller inline variant is available with the current flags; --output FILE …, ASK_MAX_OUTPUT_TOKENS=0 …"*, the field's sentence byte for byte, about a command that declares two bounding flags. `_declared_and_used(None)` answered `([], set())` and the prefix rule spelled that **absence of a reading** as a **claim about the command**. Nothing about the repository, the size or the command was ever the subject; a `CliRunner` fixture always has a context, which is why nine batteries stayed green across three cycles. **Four things close it.** (1) **A second, context-free authority**: `output_ceiling.resolve()` reads Click's context first — the only source that can tell `--limit 50` from the 50 nobody typed — and the **command tree resolved from `sys.argv`** second, consulted only when the context produced no bounding flag at all, which is the signature of both failures the row suspects (no context; or a context pointing at a group or wrapper whose `params` are not the leaf's). The tree is registered by the entry point as a *provider*, so the Typer→Click conversion is paid only by a run already over the ceiling, and never on the answer path. (2) **Observability, which is the half the ledger said ends the cycle**: every `OUTPUT_TOO_LARGE` refusal — the emit seam, `repo-ir`'s stdout guard, `repo-ir`'s MB guard and `migrate-check`, all four — publishes `hint_basis: {derived_from, command, declared_flags, flags_already_passed, context_found}`, so **one** field run names which authority answered and what it read, instead of a fourth round of guessing. Enforced structurally: an `OUTPUT_TOO_LARGE` refusal in `cli.py` with no `hint_basis` fails the battery (AST-checked). (3) **A failed derivation fails loudly**: with nothing resolved, the hint opens *"The flags that would bound this answer could not be read from the running invocation, so this list may be missing a smaller inline view — check `--help` for the command"* and keeps the universal escapes, which hold whatever the command is. The *"no smaller inline variant"* sentence is now reserved for what it claims: a command that **was** read and declares nothing bounding, or whose bounding flags this run already passed. (4) **The collateral the row named**: `_flag_was_given` — which governs the `--force` bypass and the `--limit`-versus-`--output` reading — delegates to the same authority (`flag_was_passed`), so a run that typed `--force` and reached the guard off-context is no longer refused with its bypass silently removed. **Measured, before and after, through the real entry point** (`run_cli.py`, real ceiling, `click.get_current_context` degraded to `None` for the ceiling module only — the shape of the hypothesis, not a stub of the answer): before, the field's sentence; after, *"Use --compact …, --limit N …"* with `derived_from: "argv"`, `context_found: false`, and `--force` no longer refused on the same route. **And on the field's own subject**, `tutorials` @24 074 `.java`, no `ASK_MAX_*`, the real 50 K ceiling: `estimated_tokens: 241 527` — 0,06 % from the reporter's 241 394, so the same tree and the same payload — hint *"Use --compact …, --limit N …"*, `hint_basis.derived_from: "click"`, `context_found: true`. That is the reading this platform gives; the reporter's platform is where it degrades, and the refusal now carries the field that says which. Regression `tests/test_ceiling_hint_without_context_ask09.py`, 10 assertions, **verified red against `HEAD`** (6 failed, 4 errored) — the condition no `CliRunner` can produce, asserted end-to-end in a subprocess. ⚠ **What is still owed, and it is not a code change**: the acceptance run on Windows 11 / pipx against the real clone. It is no longer a blocking unknown — if the sentence reappears there, `hint_basis` names the cause in one run. **History of the reopening, kept because the row was closed twice on it:** **reopened 5.8.4 — fifth cycle, and the second closure declared against an artefact instead of against the reported subject.** Both 5.8.4 re-audits reproduce it, independently, on different machines. Corpus, Windows 11 / pipx / Git Bash, `tutorials` (24 073 `.java`, ~600 Maven modules), no `ASK_MAX_*` in the environment, no `tutorials/.ask`, the real 50K ceiling: `ask endpoints tutorials` exits 1 with the byte-identical *"No smaller inline variant is available with the current flags"* at `estimated_tokens: 241 394`, while `--compact` (44 973 B ≈ 11,2K) and `--limit 50` (65 038 B ≈ 16,3K) both exit 0 with 3–4× of margin and are both declared on `endpoints`. `saint-server` round 9 reports the same sentence from `cold-start` and `endpoints`, with `--compact` measured at 76 053 B and 10 230 B — **0,4 % of the payload the hint tells the caller to dump**. **The number is what makes this diagnosable at last**: our closure measured 241 041 tokens on the tree the reporter measures 241 394 on — 0,15 % apart, so it is the same repository and the same payload, and the difference lives in the environment or the entry point, not in the subject. That difference IS the finding. ⚠ **Do not build a third fixture.** Hypotheses for the agent that fixes this, cheapest discriminator first: (1) `exceeds()` calls `build_hint()` with no context, so `_declared_and_used` recovers it from `click.get_current_context(silent=True)`, which is **thread-local** — any emission that reaches the guard off the invoking thread, after the context is popped, or through a shim entry point sees `ctx is None`, gets `([], set())` back, and the hint degrades **silently** into the false sentence; a `CliRunner` fixture always has a context, which is exactly why every battery we own is green; (2) `ctx.command` resolving to a wrapper or group rather than the leaf command, so its `params` do not carry `--compact` — the reported environment is a pipx console script on Windows, which no run of ours covers; (3) a short-circuit deciding the hint before `ctx.command.params` is consulted, reachable only above a size no fixture reaches. **The fix that ends the cycle is not the sentence, it is the observability**: the `OUTPUT_TOO_LARGE` payload must publish what the hint was derived from — `declared_flags`, `flags_already_passed`, and whether a context was found at all — so one field run names the cause instead of a fourth round of guessing, and a context-less hint must fail loudly rather than claim *"nothing smaller"*, which is a confident falsehood in the exact sense this repository forbids. ⚠ The same context lookup governs `_flag_was_given("force")`, so the `--force` bypass is suspected to fail on the identical path and has never been exercised in the field. **Acceptance, unchanged and now carrying a platform**: `ask endpoints tutorials` with no environment variable names `--limit` and `--compact`, verified on Windows/pipx against a real clone (`git clone --depth 50 --single-branch https://github.com/eugenp/tutorials.git`), never against a synthetic repository. **What the 5.8.4 closure did buy, and it is real: the battery, and the `repo-ir` residue it uncovered.** History of both closures, kept because the row was closed twice on them: `tutorials` (24 073 `.java`), no environment variable, the real 50K ceiling: `endpoints` answers *"Use --compact …, --limit N …"* at `estimated_tokens: 241 041`, `cold-start` answers *"Use --compact …"* at 230 518, and `posture` does not arise — 134 431 B ≈ 33K tokens, `exit 0`, no hint at all. Three cycles of byte-identical text were measured against a build, not against this tree. **What the row's rewritten acceptance did buy is the battery**: `tests/test_ceiling_hint_at_real_scale_ask09.py` builds a repository that genuinely overflows (1 200 handler mappings, ~107K tokens), *discovers* the population by running every path-only command that declares a bounding flag rather than listing them, and asserts that no such command denies a flag it declares — parametrised by (command, repository), never by (command, artificial ceiling). It is verified red: forcing the *"nothing smaller"* prefix fails it. It also refuses to be vacuous — the fixture must overflow, the population must contain the two commands the field named, and the flag the hint offers must actually get the answer under the ceiling when run. ⚠ **The residue the old fixture could not see**: `repo-ir`'s stdout guard compared against a constant bound at import, so `ASK_MAX_OUTPUT_TOKENS=0` — which `breaking-changes-v1` declares as restoring pre-5.7.0 behaviour *exactly*, and which `repo-ir`'s own hint offers — left it refusing while every other command printed, and its refusal published no `estimated_tokens`, no `ceiling_tokens` and no `estimate_basis`, so the number it refused on existed only inside an English sentence. Both now come from `output_ceiling.exceeds`, the authority: one refusal, one shape, one ceiling that the caller can actually move. Measured: `=0` now exits 0, a lowered ceiling bites, and the payload carries its measurement. **History, kept because the row was closed twice on it:** REOPENED 5.8.2 — the closure was verified against the wrong path: against the unified emit seam, with the ceiling forced, `endpoints` answers *"Use --compact …, --limit N …"* and `cold-start` *"Use --compact …"* — the derivation reads `ctx.command.params` and works. What did reproduce is the half the row itself names: `repo-ir` carried **two** hand-written hints (stdout and the separate MB guard on a written file) and `migrate-check` a **third**, `_migrate_check_output_hint` — a hand-maintained copy of `build_hint`, same list, same prefix rule. A hand-written escape list cannot know which flags the caller already passed, which is the reported defect seen from the other side. `output_ceiling` now owns the whole vocabulary: `--force` as a **bypass** (offered only where the command declares it), `--gzip` as a **file-only** bound, `ways_out(destination="file")` for the MB guard — where `--output FILE` is not an escape and the token env var does not apply — and the *"nothing smaller"* prefix decided by counting the **bounding** escapes rather than the sentence, so a command whose only remaining escape is `--force` no longer opens with "Use". `_migrate_check_output_hint` is deleted. Regression `tests/test_ceiling_hint_one_authority_ask09.py`, 7 assertions, two of which are structural: `cli.py` may not contain the prefix sentence, and every `OUTPUT_TOO_LARGE` hint in it must come from `build_hint` (AST-checked). ⚠ **Reopened by the 5.8.2 re-audits, which report it as `B16` (saint-server) and `ASK-09` (corpus) — one defect, two origin IDs, like `B16-sv` before it.** The fix landed and the *hint* half did not: `ask endpoints tutorials` (24 073 `.java`) still answers *"No smaller inline variant is available with the current flags"* at `estimated_tokens: 240 908`, while `--compact` (45 902 B ≈ 11,5 K) and `--limit 50` (66 765 B ≈ 16,7 K) both fit with 4× of margin and are both declared on `endpoints`. Byte-identical text since 5.7.2, third cycle. **The diagnosis is the valuable part and it is about our method, not the code**: the regression assertion forces a low ceiling (`ASK_MAX_OUTPUT_TOKENS=1`) over a *small* fixture payload, and the path that fails is the *natural* rebase of a 240 K-token payload against the real 50 K ceiling — so the battery validates one route while the defect lives in another, and a green test certified a defect the field reproduces in three consecutive cycles. `posture` received `--limit`/`--compact` in the same release this row was closed in and *does* answer correctly, which is why the derivation looked proven. Suspected: a short-circuit that decides the hint before `ctx.command.params` is consulted, reachable only at a size the fixture never reaches (compare `ASK_MAX_OUTPUT_TOKENS=2000 ask endpoints <small>` against `ask endpoints <24k-file repo>`). **Acceptance, restated so the same closure cannot repeat**: the hint names `--limit` and `--compact` with an unforced ceiling on a repository that genuinely exceeds 50 K, and the regression is parametrised by `(command, repository)` over a fixture that really overflows — never by `(command, artificial ceiling)`. |
45
45
  | ASK-12 | **A field describing an exclusion named three production trees as excluded.** `spring-audit spring-boot` published `patterns_matched: [… "core/", "module/", "cli/" …]` — 4 254 + 1 625 + 76 production files. Read literally, whole modules had left the audit population; measured, the exclusion was *conservative* (3 650 excluded against 4 214 files actually under a test root). The auditor filed an over-exclusion false positive on the strength of the field and then withdrew it. Cause: the constructor's fallback named `parts[0]`, the first path segment, when no source-root marker matched — and the same published field had **two constructors** with different shapes and different `basis` strings (`cli.py` for `spring-audit`, `repository_ir.py` for `endpoints`) over the one authority both already shared. | 5.7.2 audits | Low — a reporting defect, not an exclusion one, which is why it cost an auditor a round instead of costing a finding | **closed 5.8.1** — one producer, `repository_ir.excluded_test_source_detail`, beside `is_test_source_file`, which is the authority that decides the fact. `patterns_matched` can now only hold a **declared test root** — a directory whose files are excluded in their entirety — and everything else is published as a `rules_matched` census with a `rules_meaning` sentence per rule, so a per-file rule can no longer be read as a whole-directory claim. A surface counting something other than files says so (`unit`, `files_matched`), because `endpoints` counts route annotations. Regression `tests/test_test_source_exclusion_one_producer_ask12.py`, 14 assertions, two of them structural: `cli.py` may not compose the key at all, and exactly one function in `repository_ir.py` may (AST-checked). |
46
46
  | B14 | **`--compact` made the answer smaller by making it unattributable.** `cold-start --compact` took 1 968 807 characters to 16 452 (−99,2 %) and removed **11 of 19 keys** on the way — `schema_version`, `repo_id`, `current_git_head` and the 331-character `hotspots` among them — in a document whose weight was **97,7 % `endpoints`**, and cut the endpoint list to 30 with no `*_cap`. The view an agent is told to inject could not say which repository, which commit or which schema it described. | 5.6.1 audit; AS-15 closed the root `--compact` half in 5.7.0, this is the `cold-start` half, re-reported against 5.8.0 | High | **closed 5.8.1** — three rules, in the shared bound rather than invented per command. `COMPACT_IMMUTABLE_KEYS` is the minimum no `--compact` may remove (provenance is contract, not content). Every collection that was cut declares `{total, shown, omitted}` with the registered effect — measured on BroadleafCommerce, `endpoints_cap` now publishes `total: 272, shown: 30, omitted: 242` where the cut was silent. And trimming goes by **weight**: a section already under 2 KB is kept rather than dropped by a fixed list, which is what recovers `hotspots`; everything actually removed is named with its size in `compact_omitted`. Regression `tests/test_compact_keeps_identity_b14.py`, 12 assertions, including that nothing removed claims nothing. |
47
47
  | B2 | **A risk band and a rule severity share a vocabulary and measure different things.** They disagreed on **34 of 50 rows (68 %)** — 5 defects rated `medium` band as `critical`, 29 rated `high` band as `medium` — and both numbers were right: severity is what the rule says about the defect, the band is what the composition says about it in *this* repository (reach, access, write effect). One surface on, the collision became a contradiction a reader could not resolve: `audit-report` announced **14 critical** over bands while `--min-severity critical` filtered severities and returned **0**. | 5.6.1 audit, reproduced through 5.8.0 | Medium | **closed 5.8.1** — the band now also has a name that cannot be mistaken for a severity: `risk_tier: R1…R5`, published **beside** `band` and never instead of it, so no consumer loses a field it reads. One mapping (`risk_model.tier`), one sentence (`TIER_CROSSWALK_NOTE`) travelling with every count — it names both axes, states that they disagree by construction, gives the crosswalk, and points at `--min-band` as the flag that filters tiers rather than `--min-severity`. `risk` publishes `by_tier` beside `by_band`; `audit-report` leads with tiers, keeps bands under the legacy spelling, renders the note, and derives tiers from bands when it is handed an older bundle. Regression `tests/test_risk_tier_is_not_a_severity_b2.py`, 12 assertions, including that no tier shares a spelling with any severity and that a summary with no census still says *not measured* rather than zero. ⚠ The row's `reach`/`access`/`score_raw` per row was already shipped (`factors`, `endpoints_reached`, `reach_scope`); `--min-band` already existed on `risk` and `enrich`. |
@@ -53,14 +53,19 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
53
53
  | P0-1 | **`impact` publishes a confident zero on a class the container wires by string.** `ask impact IdOverrideTableGenerator . --depth 6` @ `8645873661` answers `direct_callers: []`, `risk_level: low`, `confidence_score: 1.0`, `closure_complete: true`, no `analysis_warnings`, *"No callers or dependents found … Low-risk isolated change."* — on a Hibernate id generator that 135 entities bind by string in `@GenericGenerator`, while `ask explain` lists **119 referencing classes** and `ask impact-chain`, in the same run, publishes `confidence: low` with the CH-005 blind spot named. Three commands, one symbol, three verdicts, and the unhedged one is the sentence a migration scope is cut with | Broadleaf adversarial audit 2026-08-17 (§13) | **High** — the failure mode that destroys the offer in one line: it excludes from scope the exact component a Hibernate migration turns on | **closed 5.8.3** (`90f10b2`) — the reading of an empty result is now one authority, `sourcecode.empty_reach`: the five codes (CH-005, CH-003, G-2, P1-B, P1-E), the precedence between them, the words each is published in and the confidence they force. Only the detectors stay with their substrate, and CH-003's moved whole because it only ever read graph nodes, which both callers hold. `impact` publishes `blind_spots` with the chain's codes, `analysis_warnings` with byte-identical text, `confidence_score` capped below the `medium` band, `closure_complete: false` with `unmodeled_inbound_edges` as the third cause beside E-32's two, and an explanation that states the blind spot instead of asserting isolation. Measured on the audited commit: confidence 1.0 → 0.4, `closure_complete` true → false, `blind_spots: [framework_di]` naming the Hibernate supertype. Negative controls hold — a stereotype bean with no callers keeps its confident answer. Battery `tests/test_empty_reach_authority_p01.py`, 14 assertions, including a cross-command invariant that fails if the two payloads disagree on the codes or on the words |
54
54
  | P0-2 | **Endpoint reach is not monotone: a callee reports less than its caller.** `ask impact BasicPersistenceModule . --depth 6` → 268 endpoints; `ask impact CriteriaTranslator . --depth {6,8}` → **0**, with `BasicPersistenceModule` a direct caller of `CriteriaTranslator` and the walk reporting `closure_complete` for itself. Breaks the property a tech lead assumes without asking — if A calls B, the reach of B contains the reach of A — and invites excluding the core of the change | Broadleaf adversarial audit 2026-08-17 (§13) | **High** | **closed 5.8.3** (`41c3150`, `a6cbe21`) — **the reported cause is not the cause**: the hub-frontier heuristic expands its nodes in the main walk and the closure was genuinely exhausted. Two expansions applied to the SEED and to nothing else — the subtype expansion (E-27's rule) and the interface bridge for `implements` edges carrying an unresolved short name, which `ImplementationGraph` cannot see. Reach depended on where the walk started. Both now live in `caller_reach.ReachExpansion`, applied at every node by the traversal both commands share. Measured A/B, depth 6, same cache: `CriteriaTranslator` **0 → 274** endpoints, `BasicPersistenceModule` 268 → 274, `Money` 60 → 277, and `AdminSecurityFilter`/`IdOverrideTableGenerator`/`SkuCustomPersistenceHandler` unchanged at 0. Cost flat (0.09–0.16 s/query vs 0.08–0.23 s). `impact-chain` takes the same object: 0 → 239 endpoints at the same depth. ⚠ The subtype half narrows to the corresponding member and the DI bridge does not — see `C1-25`, which the first cut of this fix reopened. Battery `tests/test_reach_monotonicity_p02.py`, 8 assertions |
55
55
  | P0-3 | **The negative set derived from `pr-impact` is wrong in ≥54 % and does not exist as a state.** `ask pr-impact . --files -` over the Hibernate surface → 258 affected endpoints; the complement against `ask endpoints` is 13, of which **3 are reachable according to ASK itself** (`impact-chain 'AdminBasicEntityController#viewEntityForm'` returns `/user-management/{id}`) and 4 more through `ProductHandlerMapping`/`CategoryHandlerMapping`/`SkuHandlerMapping` → `blCatalogService` → `ProductDaoImpl`, which is in the changed set | Broadleaf adversarial audit 2026-08-17 (§6.2, §13) | **High** — it is literally the deliverable (*"what NOT to review"*), and it would fail on the routes a QA lead checks first | **closed 5.8.3** (`1803ebb`) — three states with one authority (`sourcecode.endpoint_disposition`): `affected`, `unknown` (with the reason), `unaffected` (with its basis), partitioning the declared route population so nothing is subtracted. Three blockers demote `unaffected`, each repository-wide because none is a statement about one route: a container-wired class inside the cone (the audit's handler-mapping chain, caught through the `container_wiring` authority, never by a name), a closure that was not exhausted, a changed file that mapped to no class. Closing it needed two missing edges, both found by reproducing §6.2: **`super.m()` produced no call edge when the overriding class declared `m` itself** — which is what an override IS — and **a request handler was not callable**, because handlers carry `symbol_kind: "endpoint"` and `_type_indices` indexed only `"method"`. Re-run on the audited commit: **affected 254 · unknown 14 · unaffected 0 of 268**, with every one of the 7 rows the audit proved reachable now `affected`. Batteries `tests/test_endpoint_disposition_p03.py` (9 assertions) |
56
- | B15-res | **The `.partial` is not removed on the error path.** B15 closed the atomic-temporary half in 5.8.1 and it holds — the null device answers `exit 0` with no residue, the OS locale is isolated in `os_error`, the hint names the parent-directory decision — but a fourth artefact was outside the `finally`: `ask spring-audit . -o ./saint-server` against an existing directory leaves `?? saint-server.partial` in the caller's repository. The auditor has cleaned the user's repository by hand in four consecutive sessions. | 5.8.0 → 5.8.2 re-audits (residual of `B15`) | Medium — small in code, large in claim: **the pitch is read-only deterministic analysis**, and a pre-CI security review blocks a tool that writes into the client's tree and leaves it there | **closed 5.8.3** (`6fd8001`) — registration is about OWNERSHIP, not about which writer did the writing, and the `register_artifact` call sat inside the branch of `PhasedRun._write` that writes the file itself. The three commands that supply `_safe_write_file` therefore wrote a checkpoint nobody owned, so the failure path ran `cleanup_artifacts()` against an empty registry. Reproduced before and after on `spring-audit -o <existing dir>`: `cleaned_up: []` + residue → `cleaned_up: ["target-dir.partial"]` + a clean tree. Battery `tests/test_hygiene_rows_b15res_b18_ask16.py`, with the negative control that a delivered answer leaves no checkpoint and cleanup does not fire on a good run |
57
- | B16 / ASK-09 | *(see the `ASK-09 / B16-sv` row above — same defect, reopened there.)* The 5.8.2 saint-server audit files it as `B16` and the corpus audit as `ASK-09`; both describe the ceiling hint denying a `--compact` that exists and fits. Recorded here only so a reader searching `B16` lands on the reopened row rather than concluding it is untracked. | 5.8.2 re-audits | High | **closed 5.8.4 — tracked as `ASK-09 / B16-sv`**, which closed by measurement on `tutorials` with a battery that overflows for real |
58
- | B18 | **`--min-band` rejects the tier vocabulary that our own report teaches.** `audit-report` prints *"Risk tiers: R1 14, R2 7, R3 74, R4 3"*, the crosswalk *"R1 = band critical …"*, and the instruction *"Filter tiers with --min-band"* — and then `ask risk . --min-band R1` answers `INVALID_INPUT` and lists the severity spellings (`critical`, `high`, `medium`, `low`) as the only ones it takes. Two surfaces of one product, one teaching a vocabulary the other refuses. | 5.8.1 → 5.8.2 re-audits | Medium — **a regression introduced by the `B2` fix**: B2 gave the band a tier spelling precisely so it could not be read as a severity, and the filter was never taught the new vocabulary, so the product documents a filter expression it refuses | **closed 5.8.3** (`6fd8001`) — `risk_model.band_of` resolves either spelling from the crosswalk the report is already rendered from, with `BAND_OF_TIER` derived rather than retyped so the two cannot drift. `--min-band` and `--band` on `risk`, and `--min-band` on `enrich`, all read it; a rejection names both spellings. Measured on BroadleafCommerce: `--min-band R1` and `--min-band critical` both return 0 rows, `--min-band R2` returns 18. The second thread is closed too — `TIER_CROSSWALK_NOTE` names the command the flag lives on (`ask risk --min-band`) and what the other two carry instead. ⚠ **This row read `open` for its whole first day closed**: the verdict was written into the *defect* cell because the quoted error message carried escaped pipes (`\|`), and a reader — or a `grep` — counting open rows counted this one. The quoted message no longer carries a pipe; `tests/test_ledger_rows_are_wellformed.py` now asserts that every row has exactly five cells and that no cell but the last opens with a verdict. |
59
- | B19 | **The schema registry resolves 44 identifiers and one of them has a document.** Residual of `B6`, which the same audit calls excellent and better than what it asked for: every identifier resolves, `pending_renames` declares the 13 non-conforming ones with their canonical name, and `endpoints` emits `endpoints-v1`. But `with_published_document: 1` — only `envelope-v1` has a JSON Schema behind it, and the rest resolve to their *description*. | 5.8.2 re-audit (residual of `B6`) | Medium — resolution enables **discovery**; the use case that opened B6 was **validation in CI** (`--ci` / `--fail-on`), and that needs the document, not the description | **closed 5.8.4 for the five a gate consumes** — `verify-v1`, `verify-edit-v1`, `endpoints-v1`, `pr-impact-v1` and `core-analysis-v1`, the set the audit puts at 90 % of CI use, are published as JSON Schema documents and served by `ask schema <name>`; `with_published_document` goes **1 → 7** (six identifiers; `pr-impact` and the root analysis each carry two emitted values pointing at one document). **The documents are held to the payloads, not to prose**: every one is validated in the battery against output the suite produces, so a document that stops describing what the command emits fails — a schema written from a reading of the code drifts the first time the code moves. Cross-checking against a second repository is what made them true rather than fitted: `spring-petclinic` caught `verify-edit`'s verdict vocabulary, which is `pass` / **`break`** / `unverified` and not the `fail` the first draft assumed — `break` is a statement about the change, not about the repository. **The two canonically-named documents sit beside emitted values that are still bare numbers** (`1.3`/`1.0` for `pr-impact`, `1.0` for the root analysis), deliberately: renaming an emitted `schema_version` is the incompatible change `ASK-11` exists to stop us making in silence, so `ask schema 1.0` still resolves to all seven shapes and each offers its canonical name to pin. Asserted both ways — every claimed document exists, every document is claimed, and no emitted identifier was quietly renamed to match one. Battery `tests/test_published_schema_documents_b19.py`, 30 assertions. ⚠ **Still open**: the other ~37 identifiers, which continue to resolve to their description. Original note — the row is honest about this already (`documented_note` says exactly which of the two it gives), so this is a scope completion rather than a correction. Recommended order, from the audit: the five a gate would actually consume — `verify-v1`, `verify-edit-v1`, the root analysis (`1.0` → `core-analysis-v1`), `endpoints-v1`, `pr-impact` (`1.2` → `pr-impact-v1`) — which it estimates at 90 % of CI use. Generate from the code that already owns each shape rather than by hand, since `entries[].constant` already names the authority. Publish `with_published_document` as a progress metric. |
56
+ | B15-res | **The `.partial` is not removed on the error path.** B15 closed the atomic-temporary half in 5.8.1 and it holds — the null device answers `exit 0` with no residue, the OS locale is isolated in `os_error`, the hint names the parent-directory decision — but a fourth artefact was outside the `finally`: `ask spring-audit . -o ./saint-server` against an existing directory leaves `?? saint-server.partial` in the caller's repository. The auditor has cleaned the user's repository by hand in four consecutive sessions. | 5.8.0 → 5.8.2 re-audits (residual of `B15`) | Medium — small in code, large in claim: **the pitch is read-only deterministic analysis**, and a pre-CI security review blocks a tool that writes into the client's tree and leaves it there | **closed 5.8.3** (`6fd8001`) — registration is about OWNERSHIP, not about which writer did the writing, and the `register_artifact` call sat inside the branch of `PhasedRun._write` that writes the file itself. The three commands that supply `_safe_write_file` therefore wrote a checkpoint nobody owned, so the failure path ran `cleanup_artifacts()` against an empty registry. Reproduced before and after on `spring-audit -o <existing dir>`: `cleaned_up: []` + residue → `cleaned_up: ["target-dir.partial"]` + a clean tree. Battery `tests/test_hygiene_rows_b15res_b18_ask16.py`, with the negative control that a delivered answer leaves no checkpoint and cleanup does not fire on a good run. ⚠ **Field-confirmed 5.8.4**: both error paths (`-o ./<existing dir>` and `-o /dev/null`) leave `git status --porcelain` empty on the reporter's machine — the first session in nine with no ASK residue to remove by hand |
57
+ | B16 / ASK-09 | *(see the `ASK-09 / B16-sv` row above — same defect, reopened there.)* The 5.8.2 saint-server audit files it as `B16` and the corpus audit as `ASK-09`; both describe the ceiling hint denying a `--compact` that exists and fits. Recorded here only so a reader searching `B16` lands on the reopened row rather than concluding it is untracked. | 5.8.2 re-audits | High | **reopened 5.8.4 — tracked as `ASK-09 / B16-sv`**, where the fifth cycle and the second false closure are recorded. The 5.8.4 saint-server audit files it as `B16` (five rounds, byte-identical text, `cold-start` and `endpoints`) and the corpus audit as `ASK-09` (fourth cycle, on `tutorials` under Windows/pipx). One defect, two origin IDs |
58
+ | B18 | **`--min-band` rejects the tier vocabulary that our own report teaches.** `audit-report` prints *"Risk tiers: R1 14, R2 7, R3 74, R4 3"*, the crosswalk *"R1 = band critical …"*, and the instruction *"Filter tiers with --min-band"* — and then `ask risk . --min-band R1` answers `INVALID_INPUT` and lists the severity spellings (`critical`, `high`, `medium`, `low`) as the only ones it takes. Two surfaces of one product, one teaching a vocabulary the other refuses. | 5.8.1 → 5.8.2 re-audits | Medium — **a regression introduced by the `B2` fix**: B2 gave the band a tier spelling precisely so it could not be read as a severity, and the filter was never taught the new vocabulary, so the product documents a filter expression it refuses | **closed 5.8.3** (`6fd8001`) — `risk_model.band_of` resolves either spelling from the crosswalk the report is already rendered from, with `BAND_OF_TIER` derived rather than retyped so the two cannot drift. `--min-band` and `--band` on `risk`, and `--min-band` on `enrich`, all read it; a rejection names both spellings. Measured on BroadleafCommerce: `--min-band R1` and `--min-band critical` both return 0 rows, `--min-band R2` returns 18. The second thread is closed too — `TIER_CROSSWALK_NOTE` names the command the flag lives on (`ask risk --min-band`) and what the other two carry instead. ⚠ **This row read `open` for its whole first day closed**: the verdict was written into the *defect* cell because the quoted error message carried escaped pipes (`\|`), and a reader — or a `grep` — counting open rows counted this one. The quoted message no longer carries a pipe; `tests/test_ledger_rows_are_wellformed.py` now asserts that every row has exactly five cells and that no cell but the last opens with a verdict. ⚠ **Field-confirmed 5.8.4**: `ask risk . --min-band R1 -o out.json` exits 0 on the reporter's corpus |
59
+ | B19 | **The schema registry resolves 44 identifiers and one of them has a document.** Residual of `B6`, which the same audit calls excellent and better than what it asked for: every identifier resolves, `pending_renames` declares the 13 non-conforming ones with their canonical name, and `endpoints` emits `endpoints-v1`. But `with_published_document: 1` — only `envelope-v1` has a JSON Schema behind it, and the rest resolve to their *description*. | 5.8.2 re-audit (residual of `B6`) | Medium — resolution enables **discovery**; the use case that opened B6 was **validation in CI** (`--ci` / `--fail-on`), and that needs the document, not the description | **closed 5.8.4 for the five a gate consumes** — `verify-v1`, `verify-edit-v1`, `endpoints-v1`, `pr-impact-v1` and `core-analysis-v1`, the set the audit puts at 90 % of CI use, are published as JSON Schema documents and served by `ask schema <name>`; `with_published_document` goes **1 → 7** (six identifiers; `pr-impact` and the root analysis each carry two emitted values pointing at one document). **The documents are held to the payloads, not to prose**: every one is validated in the battery against output the suite produces, so a document that stops describing what the command emits fails — a schema written from a reading of the code drifts the first time the code moves. Cross-checking against a second repository is what made them true rather than fitted: `spring-petclinic` caught `verify-edit`'s verdict vocabulary, which is `pass` / **`break`** / `unverified` and not the `fail` the first draft assumed — `break` is a statement about the change, not about the repository. **The two canonically-named documents sit beside emitted values that are still bare numbers** (`1.3`/`1.0` for `pr-impact`, `1.0` for the root analysis), deliberately: renaming an emitted `schema_version` is the incompatible change `ASK-11` exists to stop us making in silence, so `ask schema 1.0` still resolves to all seven shapes and each offers its canonical name to pin. Asserted both ways — every claimed document exists, every document is claimed, and no emitted identifier was quietly renamed to match one. Battery `tests/test_published_schema_documents_b19.py`, 30 assertions. ⚠ **Field-verified in 5.8.4**: the audit reads the documents back as real JSON Schema draft 2020-12 with `$schema`, `$id` and `title`, confirms `with_published_document: 1 → 7` and the six identifiers, and records that contract validation in CI — the use case that opened `B6` eight rounds earlier — is executable at last. The 13 declared renames it leaves behind are tracked as `B20`. ⚠ **Still open**: the other ~37 identifiers, which continue to resolve to their description. Original note — the row is honest about this already (`documented_note` says exactly which of the two it gives), so this is a scope completion rather than a correction. Recommended order, from the audit: the five a gate would actually consume — `verify-v1`, `verify-edit-v1`, the root analysis (`1.0` → `core-analysis-v1`), `endpoints-v1`, `pr-impact` (`1.2` → `pr-impact-v1`) — which it estimates at 90 % of CI use. Generate from the code that already owns each shape rather than by hand, since `entries[].constant` already names the authority. Publish `with_published_document` as a progress metric. |
60
60
  | ASK-16 | **74 % of `spring-audit`'s wall clock is outside the instrumented phase.** Clean CPU, all layers warm, writing to a file to bypass ceiling estimation: wall 9,4 s against `analysis_time_ms: 2 454` — 26 % of the clock. `symbols_analyzed` identical across 5.8.0/5.8.1/5.8.2 (30 053), output grew 8–10 % while time doubled, and stdout-vs-file is only ~1,6 s, so serialisation, population, cache state and ceiling estimation are all **excluded as causes by measurement**. ~5,4 s unattributed. The command recovered 7 of the 10,4 s it lost in 5.8.1 and sits **+45 % over its 5.7.2 best** (7,6 s → 11,0 s). | 5.8.1 → 5.8.2 corpus re-audits | High | **closed 5.8.3 by measurement** (`6fd8001`) — `perf.PhaseTimings` is always on and always published: `metadata.timings` gives the per-phase split, the measured total and `unaccounted_ms`, published rather than distributed over the phases. **The missing 74 % is the CIR build.** BroadleafCommerce, 2 985 files: cold wall 9 690 ms — `cir_build` 8 679 (90 %), `security_rules` 732, `semantic_model` 63, `tx_rules` 53, unaccounted 163 (1,7 %); warm wall 2 133 ms — `cir_build` 1 118, `security_rules` 735, unaccounted 164 (7,7 %). So `analysis_time_ms: 2 454` was never the analysis, it was the security rule pass alone, and the optimisation target is the parse, not the rules. The auditor's ordering — instrument before tuning — was right, and it changed the answer |
61
- | R2 | **WebFlux functional routing is not modelled.** Sixth consecutive cycle. `ask endpoints halo` returns `total: 0` over a CMS with a full REST API: `functional_routing: {files: 58, route_registrations: 183, modeled: false}`, `gaps: ["webflux_functional: 58 files"]`. Independently counted in the repository: 3 `@RestController` (all three are tests) against 89 files using `RouterFunctions`/`coRouter`/`SpringdocRouteBuilder`. | 5.6.1 → 5.8.2, every cycle | Medium — **the signalling is exemplary and carries no misreading risk**: the zero is declared with its cause, its count and a prose warning naming the failure mode. What is missing is the analysis, not the honesty | **open (6 cycles)** — cascades: `posture`, `risk`, `data-exposure` and `spring-audit` all derive their population from `cir.endpoints`, so a repository at 0 endpoints makes four commands report an empty surface honestly and uselessly (`spring-audit halo` = 0 findings on a full REST CMS). Acceptance: `endpoints halo` returns a non-empty inventory agreeing with Halo's published API on a 20-route sample, `supported_styles.webflux_functional: true`, and path resolution composes the builder chain (`route().path("/apis/x").GET("/{name}", …)`). **The only open row that requires new analysis, and the one both audits name as the single item separating the product from a 9,5.** |
62
- | C3-127 | **`validation` is the only command with sustained monotonic degradation.** Steady-state, three consecutive versions worsening: 17 268 → 18 273 → 21 337 ms, **+23,6 % over the record**, while every other command returned to record level or better after the cache converged. `ASK_PROGRESS=1` attributes 100 % of it to one phase, `mapping validation surface`. | 5.8.2 re-audit | Medium | **open, premise corrected by measurement (5.8.3, `e57f3e7`).** `risk` publishes `timings` now, and on BroadleafCommerce the two substrates this row is about are **2,6 s of 13,5 s (19 %)** — `indexing the validation surface` 2 103 ms, `resolving the conditional bean graph` 525 ms — while **75 % of the clock is `reading HTTP-input query sinks` (10 051 ms)**, a phase the row does not mention. Two consequences: the ~27 s figure is a property of the audited corpus, not of the command; and within a single `risk` invocation each substrate already runs exactly once, so the saving this row imagines is a **cross-command** cache — a feature with its own contract, not a patch. What stays open is the real one: the query-sink walk |
63
- | P1-proc | **A performance regression battery still does not exist, and the sixth request now arrives with a validated protocol.** `R6` (the parse cache never converging, so timings were irreproducible) was a real defect, was fixed well in 5.8.2 — dispersion between consecutive warm runs fell from 2,81× to a median of 1,04×, and `cache status` no longer prints its `over by N MB` line — and **was found only because an auditor happened to be measuring**. The same auditor then reported a 43 % regression that did not exist, and retracted it: a 2-run protocol produces artefacts up to **2,8×**. | 5.5.6 → 5.8.2, sixth request | Medium — process, not code | **open** — 5.8.2 shipped the *assertions* (`P1 / F-BR`: absolute ceilings and `max(sample) < 8 × posture_warm`, whose validation table the audit independently reproduces and extends with 5.8.2 at 6,8× ✅). What is still missing is the **harness that runs them**, and the audit supplies the missing half — a measurement protocol this ledger should treat as binding: `wall_steady()` = one discarded warm-up, then **4 runs**, report the **minimum**, and fail the measurement itself when `max/min ≥ 1,25` because at that point the cache is still evicting and no number is comparable. **Two independent corpora now report the same confounder**: 5 of 7 false positives in one audit and 4 of 7 in the other came from cache state or CPU contention — a competing `ask` process took `spring-audit` from 11,0 s to 71,4 s (+549 %), and `analysis_time_ms` inherits the bias (2 454 → 8 109 ms), so it is not an independent metric either. Acceptance: the battery runs `wall_steady`, asserts the reproducibility gate first, and no performance figure enters this ledger without a clean-CPU check. ⚠ **The cheap half closed 5.8.4, and the cause was not `endpoints`**: it was instrumented all along — one `Progress()`, started and finished — but a phase was announced by the *heartbeat*, which only fires once the interval has elapsed, so a run that finished inside 5 s printed nothing and an instrumented fast command was indistinguishable from an uninstrumented one. The counts the audit reported (posture 1 · spring-audit 2 · validation 3 · risk 5) were measuring how slow each command was, not how well it reports. Entering a phase is now announced whether or not the interval has passed, at `start()` and at every `update()`; the `_last_emit` stamp is still taken there, so the timed loop waits a full interval behind it and the two cannot double-print, and a counted stage is still rate-limited (asserted). `endpoints` also names the snapshot write as its own phase rather than charging that time to the scan. Measured: `endpoints` **0 → 2** phase lines on a 3-file repository, and on `tutorials` the first line arrives at `elapsed=0.0s` instead of after five seconds of silence. Battery `tests/test_phase_boundaries_are_announced_p1proc.py`, 8 assertions. |
61
+ | R2 | **WebFlux functional routing is not modelled.** Sixth consecutive cycle. `ask endpoints halo` returns `total: 0` over a CMS with a full REST API: `functional_routing: {files: 58, route_registrations: 183, modeled: false}`, `gaps: ["webflux_functional: 58 files"]`. Independently counted in the repository: 3 `@RestController` (all three are tests) against 89 files using `RouterFunctions`/`coRouter`/`SpringdocRouteBuilder`. | 5.6.1 → 5.8.4, every cycle | Medium — **the signalling is exemplary and carries no misreading risk**: the zero is declared with its cause, its count and a prose warning naming the failure mode. What is missing is the analysis, not the honesty | **open (7 cycles)** — cascades: `posture`, `risk`, `data-exposure` and `spring-audit` all derive their population from `cir.endpoints`, so a repository at 0 endpoints makes four commands report an empty surface honestly and uselessly (`spring-audit halo` = 0 findings on a full REST CMS). Acceptance: `endpoints halo` returns a non-empty inventory agreeing with Halo's published API on a 20-route sample, `supported_styles.webflux_functional: true`, and path resolution composes the builder chain (`route().path("/apis/x").GET("/{name}", …)`). **The only open row that requires new analysis, and the one both audits name as the single item separating the product from a 9,5.** ⚠ **Re-measured unchanged in 5.8.4**: `functional_routing: {files: 58, route_registrations: 183, modeled: false}`, 3 `@RestController` (all three are tests) against 89 files using `RouterFunctions` / `coRouter` / `SpringdocRouteBuilder`, and the variants the resolution has to cover are named — `RouterFunctions.route()`, `coRouter` (Kotlin DSL), `SpringdocRouteBuilder`, and nested `nest()` — with the path composed along the builder chain (`route().path("/apis/content.halo.run/v1alpha1").nest(accept(JSON), b -> b.GET("/posts/{name}", handler::get))` resolving to `GET /apis/content.halo.run/v1alpha1/posts/{name}`) |
62
+ | C3-127 | **`validation` is the only command with sustained monotonic degradation.** Steady-state, three consecutive versions worsening: 17 268 → 18 273 → 21 337 ms, **+23,6 % over the record**, while every other command returned to record level or better after the cache converged. `ASK_PROGRESS=1` attributes 100 % of it to one phase, `mapping validation surface`. | 5.8.2 re-audit | Medium | **open, premise corrected by measurement (5.8.3, `e57f3e7`).** `risk` publishes `timings` now, and on BroadleafCommerce the two substrates this row is about are **2,6 s of 13,5 s (19 %)** — `indexing the validation surface` 2 103 ms, `resolving the conditional bean graph` 525 ms — while **75 % of the clock is `reading HTTP-input query sinks` (10 051 ms)**, a phase the row does not mention. Two consequences: the ~27 s figure is a property of the audited corpus, not of the command; and within a single `risk` invocation each substrate already runs exactly once, so the saving this row imagines is a **cross-command** cache — a feature with its own contract, not a patch. What stays open is the real one: the query-sink walk. ⚠ **The drift half resolved itself in 5.8.4, measured**: `validation` returns to **18 068 ms** on the audited corpus — −15,3 % against 5.8.2 and +4,6 % over its 5.7.2 record — so the monotonic series (17 268 → 18 273 → 21 337) is broken without the cross-command cache having been built, and the row is now only about the substrate. The corpus divergence is two-sided and stays: the same audit measures the two substrates at ~24 s inside `risk`'s 40 554 ms there (`posture` 5 842 ms and `validation` 18 068 ms as standalone commands, both announced as stages of `risk` by `ASK_PROGRESS=1`), against 19 % on BroadleafCommerce. A cross-command cache must therefore publish its measured saving per corpus rather than inherit the ~24 s figure |
63
+ | P1-proc | **A performance regression battery still does not exist, and the sixth request now arrives with a validated protocol.** `R6` (the parse cache never converging, so timings were irreproducible) was a real defect, was fixed well in 5.8.2 — dispersion between consecutive warm runs fell from 2,81× to a median of 1,04×, and `cache status` no longer prints its `over by N MB` line — and **was found only because an auditor happened to be measuring**. The same auditor then reported a 43 % regression that did not exist, and retracted it: a 2-run protocol produces artefacts up to **2,8×**. | 5.5.6 → 5.8.2, sixth request | Medium — process, not code | **open** — 5.8.2 shipped the *assertions* (`P1 / F-BR`: absolute ceilings and `max(sample) < 8 × posture_warm`, whose validation table the audit independently reproduces and extends with 5.8.2 at 6,8× ✅). What is still missing is the **harness that runs them**, and the audit supplies the missing half — a measurement protocol this ledger should treat as binding: `wall_steady()` = one discarded warm-up, then **4 runs**, report the **minimum**, and fail the measurement itself when `max/min ≥ 1,25` because at that point the cache is still evicting and no number is comparable. **Two independent corpora now report the same confounder**: 5 of 7 false positives in one audit and 4 of 7 in the other came from cache state or CPU contention — a competing `ask` process took `spring-audit` from 11,0 s to 71,4 s (+549 %), and `analysis_time_ms` inherits the bias (2 454 → 8 109 ms), so it is not an independent metric either. Acceptance: the battery runs `wall_steady`, asserts the reproducibility gate first, and no performance figure enters this ledger without a clean-CPU check. ⚠ **The cheap half closed 5.8.4, and the cause was not `endpoints`**: it was instrumented all along — one `Progress()`, started and finished — but a phase was announced by the *heartbeat*, which only fires once the interval has elapsed, so a run that finished inside 5 s printed nothing and an instrumented fast command was indistinguishable from an uninstrumented one. The counts the audit reported (posture 1 · spring-audit 2 · validation 3 · risk 5) were measuring how slow each command was, not how well it reports. Entering a phase is now announced whether or not the interval has passed, at `start()` and at every `update()`; the `_last_emit` stamp is still taken there, so the timed loop waits a full interval behind it and the two cannot double-print, and a counted stage is still rate-limited (asserted). `endpoints` also names the snapshot write as its own phase rather than charging that time to the scan. Measured: `endpoints` **0 → 2** phase lines on a 3-file repository, and on `tutorials` the first line arrives at `elapsed=0.0s` instead of after five seconds of silence. Battery `tests/test_phase_boundaries_are_announced_p1proc.py`, 8 assertions. ⚠ **Seventh request, 5.8.4, and the reproducibility gate now passes on everything measured**: `posture` 1,02× · `impact-chain` 1,05× · `impact` 1,08× (1,56× in 5.8.2) · `spring-audit` / `endpoints` / `validation` 1,09× · `risk` 1,23× — seven of seven under the 1,25× gate for the first time, with `cache status` at 293,65 MB of a 512 MB budget and no `over by` line. The absolute ceilings the harness should assert, from 5.8.4 steady state on the audited corpus (3 342 `.java`): `impact` < 5 000 ms (measured 4 192) · `endpoints -o f` < 6 000 (5 166) · `posture` < 6 500 (5 842) · `impact-chain` < 7 000 (6 460) · `spring-audit -o f` < 11 000 (9 716) · `validation -o f` < 19 000 (18 068) · `risk -o f` < 42 000 (40 554), plus the short-circuit `verify-edit` on a genuinely clean tree < 6 000 (4 848). The reuse assertion stays the **v3** formulation — `max(sample) < 8 × posture_warm` — whose validation table extends with 5.8.4 at 6,8 ✅; the two earlier formulations are recorded here as invalid so nobody re-derives them: extremes-only (`samples[0]/samples[-1] > 1.5`) passes 5.6.1 at 2,95 with reuse almost gone, and flatness (`median/min < 1.5`) passes 5.5.5 at 1,00 with reuse broken, because flat at 80 s and flat at 30 s score identically. Population and staleness assertions to carry with them: `symbols_analyzed == PREVIOUS or "symbols_excluded" in metadata`, and no `unchanged_for` line in the progress output. What is still missing is only the harness that runs them |
64
+ | ASK-17 | **The parse store's default budget cannot hold a multi-repository workspace warm, and the cold cost of the largest repository is 7,6 minutes.** Measured on the 8-repo corpus (43 986 `.java`): `cache status` reports 56 730 entries at **511,12 MB against a 512 MB budget** — i.e. permanently sweeping by LRU — so `tutorials` (24 073 `.java`, the largest contributor) is the first candidate for eviction. Cold `--compact` on it: **457,6 s**, independently reproduced at 449 s, against 2,0 s on the second pass. | 5.8.4 corpus re-audit | Medium — warm plus `--compact` is still the answer (2,0 s), but a workspace this size cannot keep every repository warm at the default budget, and nothing tells the caller which repository is cold before it pays for it | **closed 5.8.5 — the disclosure shipped and the owed measurement taken, under our own control.** ⚠ **The +66 % does not reproduce, and the mechanism the row suspected is worth ~3 %, not 66 %.** Protocol: `tutorials` (24 073 `.java`), root `--compact`, both cache bases isolated **and emptied between runs** (`SOURCECODE_CONTEXT_CACHE_DIR` *and* `SOURCECODE_CACHE_DIR` — the first attempt isolated only the first, and the second pass of each version answered from the L2 view its own first pass had written: 2,4 s with an empty parse store, which is how a measurement of a cold path becomes a measurement of a warm one), `ASK_PARSE_CACHE_MAX_MB` fixed at 4 096 MB so no sweep can confound the comparison, 2 passes per version. **5.7.2 (`e803c8b`): 112,0 s / 112,6 s. This tree: 113,8 s / 115,2 s — +2,3 %**, with per-version dispersion of 1,005× and 1,012× and a payload 2,2 % larger. Then the audit's own condition, isolated as the only variable — the store pre-filled to 489 MB against the **default** 512 MB budget, so every 32 MB written sweeps: **117,0 s, +2,7 %.** So the LRU sweep is not where 457,6 s comes from, and neither is the analysis path: the auditor was right to hold the regression, and the held figure is now retracted **with a number** rather than on suspicion. ⚠ Scope, stated because the difference is unexplained rather than explained away: 457,6 s on Windows 11 / pipx is **not reproduced here** (113 s on 20 cores), and that gap is not assertable in either direction from this measurement — what is assertable is that 5.7.2 → this tree did not get slower and that a saturated store costs ~3 %. ✅ **What the measurement does confirm is the row's own claim, with our number: one repository of 24 073 `.java` leaves 46 840 entries and 470 MB in the store — 92 % of the 512 MB default budget** — so a workspace with a second repository of any size is permanently sweeping by construction, exactly as reported. Sizing rule, measured rather than guessed: ~20 KB of store per Java file, so ~500 MB per 24 000-file repository. Collateral confirmation of `AS-18`: after the saturated run the store rests at 669 MB — 157 MB over the budget and **under** the 736 MB effective ceiling it publishes at 7 writers — so the ceiling holds under the condition that produced the complaint. **The disclosure half:** `cache status` now publishes **coverage per repository**, which is the half the row itself recommended and the half that changes a decision: *«tutorials: 3 100 of 24 073 files cached (13 %)»* replaces a blind guess about whether to raise the budget, and an LRU eviction becomes visible **before** somebody pays 457,6 s to discover it. Both halves of the attribution were already held and thrown away — the walk knows the repository and it computes the store key for every file — so the run records the pairs (`parse_cache.record_repository_index`, from **both** readers of the store: `build_repo_ir` and the route-surface extractor, so the figure does not depend on which command was typed) and `store_stats` intersects them with the keys it collects **in the entry walk it already performs**: coverage costs an intersection, never a second scan of the store and never a scan of the repository. Three rules keep it honest. The index is **merged, never replaced**, because a `--changed-only` or `--since` run would otherwise shrink a 24 000-file population to the twelve files it read and publish *«12 of 12 cached (100 %)»* about a repository that is cold. The number travels with its **basis** — a file deleted since its last analysis still counts as recorded and reads as uncached, which errs toward *colder than it is* and says so. And the index is **neither an entry nor evictable**: the entry walk, the byte accounting and the LRU sweep all glob `*.json`, so its bytes are published under their own name (`repository_index_bytes`) rather than folded into a total that means entries — an index swept away with the entries it describes cannot report the eviction, which is the one moment it exists for. It lives inside the generation root, so retiring a generation retires its indexes with it: the keys are only readable by the build that wrote them. Regression `tests/test_parse_store_repository_coverage_ask17.py`, 11 assertions, including the one the row is about — entries deleted underneath a recorded repository make coverage **fall** while the population holds. ⚠ **Still owed, and unchanged:** the controlled cold measurement (5.7.2 against this tree, `ASK_PARSE_CACHE_MAX_MB` fixed, store emptied between runs, on a >20 000-file repository). Until it exists neither the +66 % nor its absence is assertable, and the row stays open on that half alone. **History:** **open** — ⚠ **not filed as a regression, deliberately**: the same cold figure was 274,8 s in 5.7.2 (+66 %), and the auditor refuses to call it one because the conditions are not comparable — in 5.7.2 the store had been retired by a version change, here it was mid-LRU-sweep. Seven false positives of exactly this class have been retracted over seven cycles; this is the eighth candidate and it is being held. What is owed is a measurement **we** control: cold `--compact` on a >20 000-file repository, 5.7.2 against 5.8.4, with `ASK_PARSE_CACHE_MAX_MB` fixed and the store emptied between runs — until that exists, neither the +66 % nor its absence is assertable. Recommended beside it, and cheap because both halves already exist: `cache status` should publish **coverage per repository** — how many entries belong to each analysed repository and what fraction of its files are covered — so *tutorials: 3 100 of 24 073 files cached (13 %)* replaces a blind decision about whether to raise the budget. Entries are content-addressed and the walk knows the repository, so this is a projection of facts we hold, not new analysis. |
65
+ | C3-128 | **`timeline` is the only gate-shaped command above 120 s, and it has not come back to its record.** Steady state, 4 runs, audited corpus: `timeline --since HEAD~5` costs **198 469 ms**, +27,3 % over its 5.7.2 record of 155 950 ms. The two other commands over 120 s are there structurally — `delta` (234 s) and `contract-diff` (136 s) analyse two whole trees — while `timeline` analyses **five** and costs less than `delta` does with two, so its cost is not explained by the number of trees it walks. | 5.8.2 → 5.8.4 re-audits | Low-Medium — an investigation command rather than a gate, and the last performance figure of the round sitting outside its own best | **closed 5.8.5 by measurement — the clock is attributed, and there is nothing left unaccounted to tune against.** The row's own instruction was ASK-16's: instrument before tuning. `timeline` published a per-sample total over what is really four costs — materialising a tree, measuring each watched metric, releasing the tree, and the remainder — so *«198 469 ms»* named a command rather than a phase. `perf.PhaseTimings` (the ASK-16 authority, always on) now splits it, with the phase names taken from `--watch` so the split cannot drift from the population, and the tree materialisation kept as its own phase because git's work must not be attributed to an analysis. **Measured, BroadleafCommerce (2 985 `.java`), `--since HEAD~5 --watch posture`, 5 samples: wall 38 921 ms — `measure:posture` 32 273 (82,9 %), `materialise_tree` 5 376 (13,8 %, ~1 075 ms per tree), `release_tree` 1 272 (3,3 %), unaccounted 0,45 ms (0,0 %).** So the answer to the row's premise — *«it analyses five trees and costs less than `delta` does with two»* — is that five sixths of the cost **is** the analysis, re-run per tree by construction, and the git work is a sixth of it: `timeline` is N × one analysis and there is no timeline-specific overhead to remove. Any future gain belongs to the metric being sampled (`C3-84`, `C3-127`), which is where it would also help every other command, and the payload now says so per run instead of per audit. Regression `tests/test_timeline_timings_c3_128.py`, 9 assertions, including that the series itself is byte-identical across two runs once the clock readings are removed — instrumentation that moved an answer would be a worse defect than the row. **Original note:** **open** — ⚠ the cache-reuse half of `B7` must **not** be reopened on this evidence: the v3 assertion (`max(sample) < 8 × posture_warm`) passes at 6,8 in both 5.8.2 and 5.8.4, against 14,8 / 13,4 / 12,4 / 8,5 in the four versions that genuinely failed it. What has not returned is the absolute cost. Attribution comes before tuning and is now cheap: `metadata.timings` (`ASK-16`) exists, so the per-phase split across the five trees can be published before anything is changed. |
66
+ | B20 | **Thirteen declared renames with no cut-off date, and two distinct `1.0` identifiers meanwhile.** The registry publishes `pending_renames` for the 13 non-conforming `schema_version` values with their canonical name — exactly the policy `ASK-11` exists to enforce: the rename is an incompatible change, declared before it is made, never applied in silence. The consequence is published by the registry itself as `ambiguous_identifiers: 1` — `spring-audit` emits `1.0` for `core-analysis-v1` and `impact-chain` emits `1.0` for `impact-chain-v1`, so a consumer dispatching on the emitted version cannot tell them apart. | 5.8.4 re-audit (residual of `B19` / `B6`) | Low — declared debt rather than a defect, and the audit says so in as many words | **closed 5.8.5 — the window is declared where every other incompatible change is, and the reported ambiguity was understated by five shapes.** `BC-002` in `sourcecode.breaking_changes`: **announced 5.8.5, takes effect 6.0.0**, printed by `ask schema breaking-changes-v1`, carried in the CHANGELOG's `## Upgrading` section above the release history, and projected into `ask schema schemas-v1` as `pending_renames_window` — **one fact, two surfaces**, with a structural assertion that the registry source holds no second copy of the date. The registry's policy is generalised rather than loosened: a declared change is now `kind: exit_code` (must move an exit code) **or** `kind: contract` (must move a **published value** *and* name the version it takes effect in), because a rename breaks a consumer with every exit code still 0 and a registry that only knew about exit codes had nowhere to put it. A contract change publishes no `exit_code_before`/`after` at all — inviting a reader to check a field that cannot move is how a disclosure becomes noise. The 13 affected shapes are **read from `schema_registry.canonical_migrations()` at call time**, never copied: a shape that starts conforming leaves the declaration by itself (asserted by swapping the registry for a conforming one and watching the list empty). ⚠ **Correction to the reported cause, measured**: the audit named *two* shapes spelling their version `1.0`; there are **seven** — `core-analysis-v1`, `impact-chain-v1`, `pr-impact-v1`, `migration-blast-v1`, `spring-impact-v1`, `event-topology-v1`, `test-gap-ranking-v1` — so `ambiguous_identifiers: 1` was counting one *identifier* over seven shapes, not two. `ask schema 1.0` already resolved to all seven with each canonical name; the count is now asserted from the registry so it cannot be quoted from prose again. The rename itself is deliberately **not** made early: that is the incompatible change this policy exists to prevent. Regression `tests/test_schema_rename_window_b20.py`, 11 assertions, plus the ASK-11 battery generalised to both kinds. **Original note:** **open** — the missing half is a date, not a decision. Announce the cut-off window in `breaking-changes-v1` with the target version, the way every other incompatible change is announced, so a consumer can pin `core-analysis-v1` today and know when the bare `1.0` stops being emitted. Until then `ask schema 1.0` resolving to all seven shapes, each offering its canonical name, is the correct behaviour and must not be *fixed* by renaming an emitted value early — that is the incompatible change this policy exists to prevent. |
67
+ | E-37 | **Two environment variables move three cache stores, and no surface said which moves which.** `SOURCECODE_CACHE_DIR` relocates the per-repository snapshot store (core snapshots, the rendered L2 view, the RIS); `SOURCECODE_CONTEXT_CACHE_DIR` relocates the shared ones (the Canonical IR and the per-file parse store). Their defaults are siblings under `~/.sourcecode`, so the split is not derivable from the paths, and `cache status` published a path per store with no variable beside it while `docs/CACHE.md` named both variables in one sentence after the table. | found here 2026-08-18 while taking the controlled A/B `ASK-17` owed, not reported by the field | Medium — it does not make an answer wrong, it makes a **measurement** wrong, silently: an operator who redirects or empties *the cache* moves one store and is served answers out of the other | **closed 5.8.5** — measured first: with only `SOURCECODE_CONTEXT_CACHE_DIR` redirected, the second cold pass of each version answered from the L2 view its own first pass had written — **2,4 s against 113 s, with an empty parse store** — and the run reported `cache_source: L2_view` without anything naming the store it came out of. Now every store in `cache status` publishes `base_env`, from one authority (`cache._STORE_BASE_ENV`), the text answer prints *«<path> ($VAR moves it)»* beside each one, `stores.base_env_note` states the consequence rather than the layout, and `docs/CACHE.md` carries a **column** instead of a sentence covering both. Regression `tests/test_cache_store_base_env_e37.py`, 8 assertions, and the ones that matter are **behavioural**: setting the variable a store names moves *that* store and leaves the others where they were — a published mapping nobody exercises is the class of claim this repository refuses everywhere else. The CLI is forbidden a second copy of the mapping (structural assertion). |
68
+ | B21 | **Six of the thirteen shapes being renamed name no command that emits them.** `schemas-v1` publishes `emitted_by` per shape; for `migration-blast-v1`, `spring-impact-v1`, `event-topology-v1`, `test-gap-ranking-v1`, `canonical-ir-v1` and `hibernate-strategy-v2` it is `[]`, beside seven shapes where it is populated. Read the way a consumer reads a list, an empty `emitted_by` says *nothing emits this shape* — which tells the one consumer that **is** affected by `BC-002` that it is not. | found here while declaring `BC-002` (5.8.5), not reported by the field | Low — a disclosure gap in a declaration whose whole purpose is to let a consumer decide whether it is affected | **open, and named rather than shipped as a fact**: every `BC-002` row with an empty producer list carries `emitted_by_basis` — *«not declared in the registry: this shape's producing command is not named yet, so match on `subject`»* — so the gap is visible instead of being read as a negative claim. What remains is to fill the six, and the honest way is derivation rather than six more hand-written strings: the producer is discoverable from the command that constructs each payload, which is the same authority every other population in this repository comes from. Until then the assertion in `tests/test_schema_rename_window_b20.py` holds the weaker invariant a reader can rely on: a row either names its emitters or says why it cannot. |
64
69
 
65
70
  ---
66
71