sourcecode 5.8.24__py3-none-any.whl → 5.8.26__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. sourcecode/__init__.py +1 -1
  2. sourcecode/_build_commit.py +3 -3
  3. sourcecode/_docs/DEFECT-LEDGER.md +317 -1
  4. sourcecode/_docs/USER_GUIDE.md +16 -7
  5. sourcecode/analysis_budget.py +26 -0
  6. sourcecode/audit_report.py +43 -0
  7. sourcecode/baseline_autocapture.py +9 -4
  8. sourcecode/cache.py +67 -1
  9. sourcecode/cache_model.py +8 -5
  10. sourcecode/cache_observation.py +148 -0
  11. sourcecode/cli.py +230 -29
  12. sourcecode/context_cache.py +11 -0
  13. sourcecode/envelope.py +21 -4
  14. sourcecode/explain.py +43 -1
  15. sourcecode/format_contract.py +3 -3
  16. sourcecode/migrate_check.py +21 -2
  17. sourcecode/non_coverage.py +46 -1
  18. sourcecode/output_ceiling.py +59 -4
  19. sourcecode/packs.py +85 -1
  20. sourcecode/parse_cache.py +16 -0
  21. sourcecode/posture.py +17 -1
  22. sourcecode/product_info.py +25 -1
  23. sourcecode/regress.py +38 -0
  24. sourcecode/release_info.py +1 -1
  25. sourcecode/repository_ir.py +118 -50
  26. sourcecode/resource_budget.py +137 -9
  27. sourcecode/sarif_emit.py +156 -0
  28. sourcecode/schema_registry.py +5 -0
  29. sourcecode/security_posture.py +12 -0
  30. sourcecode/selftest.py +8 -2
  31. sourcecode/serializer.py +21 -2
  32. sourcecode/spring_impact.py +101 -0
  33. sourcecode/spring_tx_analyzer.py +8 -0
  34. sourcecode/timeline_cache.py +24 -4
  35. sourcecode/workspace.py +1 -1
  36. {sourcecode-5.8.24.dist-info → sourcecode-5.8.26.dist-info}/METADATA +4 -4
  37. {sourcecode-5.8.24.dist-info → sourcecode-5.8.26.dist-info}/RECORD +41 -39
  38. {sourcecode-5.8.24.dist-info → sourcecode-5.8.26.dist-info}/WHEEL +0 -0
  39. {sourcecode-5.8.24.dist-info → sourcecode-5.8.26.dist-info}/entry_points.txt +0 -0
  40. {sourcecode-5.8.24.dist-info → sourcecode-5.8.26.dist-info}/licenses/LICENSE +0 -0
  41. {sourcecode-5.8.24.dist-info → sourcecode-5.8.26.dist-info}/licenses/NOTICE +0 -0
sourcecode/__init__.py CHANGED
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "5.8.24"
7
+ __version__ = "5.8.26"
@@ -1,5 +1,5 @@
1
1
  """Generated at build time by hatch_build.py. Do not edit (AUD-592-D04)."""
2
2
 
3
- BUILD_COMMIT = '61b9c12b0e354cda157038bc8be6e19384a42368'
4
- CLOSURE_PROVENANCE = {'Window': {'commit': '', 'status': 'uncited'}, '`AUD-588-F05`': {'commit': '470e4a4', 'status': 'present'}, '`AUD-590-B01` / `B-01`, `F-01`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B02` / `B-02`, `F-02`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B03` / `B-03`, `F-03`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B04` / `B-04`, `F-04`': {'commit': '', 'status': 'uncited'}, '`AUD-590-R01` / `R-01`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A01` / `A-1`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A02` / `A-2`, `A-4`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A03` / `A-3`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A05` / `A-5`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A06` / `A-6`, `A-7`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A08` / `A-8`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A09` / `A-9`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A10` / `A-10`': {'commit': '', 'status': 'uncited'}, '`AUD-592-A02` / `A-1` second half, `§12.5`': {'commit': '63f3d61', 'status': 'present'}, '`AUD-592-B01` / `B-8`': {'commit': '8329614', 'status': 'present'}, '`AUD-592-D01` / `D-1`': {'commit': '0f8e03f', 'status': 'present'}, '`AUD-592-D02` / `D-2`': {'commit': '1d3db4d', 'status': 'present'}, '`AUD-592-D03` / `D-3`': {'commit': 'f28fe40', 'status': 'present'}, '`AUD-592-R01` / `A-1`': {'commit': '216025f', 'status': 'present'}, '`AUD-592-R02` / `N-4`, reopens `AUD-513-N04`': {'commit': 'ec3050d', 'status': 'present'}, '`AUD-593-N01` / `N-01`': {'commit': 'dd6a706', 'status': 'present'}, '`AUD-593-N02` / `N-02`': {'commit': 'f539787', 'status': 'present'}, '`AUD-593-N03` / `N-03`': {'commit': '1dd0b1c', 'status': 'present'}, '`AUD-593-N05` / `N-05`': {'commit': 'aa89388', 'status': 'present'}, '`AUD-593-N06` / `N-06`': {'commit': 'aa584a6', 'status': 'present'}, '`AUD-594-N04` / `D-5`, disclosure half of `B-3`': {'commit': 'a0060b7', 'status': 'present'}, '`AUD-594-X01` / `A-1`, residual of `AUD-593-N03`': {'commit': 'dd222b9', 'status': 'present'}, '`AUD-594-X02` / `A-1` second half': {'commit': '1193937', 'status': 'present'}, '`AUD-594-X03` / `X-03`, residual of `AUD-593-N01`': {'commit': 'e89e8f0', 'status': 'present'}, '`AUD-594-X04`': {'commit': '05b8cc9', 'status': 'present'}, '`AUD-595-A02` / `N-5`, advisory half of `AUD-588-B11`': {'commit': '3e15227', 'status': 'present'}, '`AUD-595-A03` / `B-6` exit-code half': {'commit': 'ab7285f', 'status': 'present'}, '`AUD-595-A05`': {'commit': 'e0b3d10', 'status': 'present'}, '`AUD-595-A07`, narrow half of `AUD-592-A02`': {'commit': 'cc15177', 'status': 'present'}, '`AUD-595-B01` / §B': {'commit': 'b77a1ad', 'status': 'present'}, '`AUD-595-Q01`': {'commit': '91a6566', 'status': 'present'}, '`AUD-596-A01` / `ASK-DET-001`': {'commit': 'fa508b4', 'status': 'present'}, '`AUD-596-A02` / `ASK-SELF-001`': {'commit': '4dfadc8', 'status': 'present'}, '`AUD-596-A03` / `ASK-C1-001`': {'commit': 'bffa3a6', 'status': 'present'}, '`AUD-596-A04` / `ASK-UX-003` + `ASK-UX-004`': {'commit': 'f0aac3c', 'status': 'present'}, '`AUD-596-B01` / `B-01`, harder witness for `BUG-6`': {'commit': '567d09d', 'status': 'present'}, '`AUD-596-B02` / `B-02`, class residual of `R2`': {'commit': '', 'status': 'uncited'}, '`AUD-596-B03` / `B-03`': {'commit': 'a8cb814', 'status': 'present'}, '`AUD-596-B06` / `B-06`': {'commit': '8107927', 'status': 'present'}, '`AUD-596-B07` / `B-07`, class residual of `AUD-592-R02`': {'commit': 'e060485', 'status': 'present'}, '`AUD-596-B09` / `B-09`': {'commit': '5b8146c', 'status': 'present'}, '`AUD-596-B10` / `B-10`': {'commit': '5cb994d', 'status': 'present'}, '`AUD-596-B16` / `B-16`': {'commit': '1507f69', 'status': 'present'}, '`AUD-596-D01` / `B-13` + `B-14`': {'commit': '0b1bdf0', 'status': 'present'}, '`AUD-596-D02` / `B-08`, residual of `AUD-594-N04`': {'commit': 'e7f73c3', 'status': 'present'}, '`AUD-596-D03` / `ASK-AGT-001`': {'commit': '8c46967', 'status': 'present'}, '`AUD-596-D04` / `B-18`': {'commit': '040f027', 'status': 'present'}, '`AUD-596-D05` / `B-19`': {'commit': 'fd2bf68', 'status': 'present'}, '`AUD-596-D06` / `B-20`': {'commit': '784cd83', 'status': 'present'}, '`AUD-596-D07` / `B-17`, half refuted': {'commit': 'c3ff822', 'status': 'present'}, '`AUD-596-D08` / `B-22`, class residual of `AUD-591-A06`': {'commit': 'b396c6a', 'status': 'present'}, '`AUD-596-D09` / `B-23`, surviving half of `B26`': {'commit': 'fd3d63f', 'status': 'present'}, '`AUD-596-D10` / `B-29`': {'commit': '', 'status': 'uncited'}, '`AUD-596-D11` / `ASK-UX-005`': {'commit': 'f4b4f77', 'status': 'present'}, '`AUD-596-D12` — six hints that send the reader back to the error': {'commit': '498df44', 'status': 'present'}, '`AUD-596-D13` / `B-28`': {'commit': '1014c72', 'status': 'present'}, '`AUD-596-D14` / `B-15`, narrow half of `B20`': {'commit': '10f2827', 'status': 'present'}, '`AUD-596-R01` / `B-05`': {'commit': '', 'status': 'uncited'}, '`AUD-596-X01` / `ASK-GATE-001`, gating half of `B24`': {'commit': 'e59c655', 'status': 'present'}, '`AUD-596-X02` / `ASK-UX-001` + `ASK-UX-002` + `B-21`': {'commit': 'f6ebf50', 'status': 'present'}, '`AUD-597-A01` / `ASK-UX-001` + `N-01`': {'commit': 'f8cf061', 'status': 'present'}, '`AUD-597-A02` / `ASK-UX-008` + `ASK-DOC-002`': {'commit': '9d47a4c', 'status': 'present'}, '`AUD-597-A03` / `ASK-DET-001`': {'commit': '4c7622a', 'status': 'present'}, '`AUD-597-A04` / `ASK-LEDGER-001`': {'commit': '46fb870', 'status': 'present'}, '`AUD-597-D01` / `B-04`': {'commit': '1094e6a', 'status': 'present'}, '`AUD-597-D02` / `ASK-DOC-001`': {'commit': 'ecb1bb7', 'status': 'present'}, '`AUD-597-D03` / `N-06`': {'commit': '9784333', 'status': 'present'}, '`AUD-597-D04` / `N-05`': {'commit': 'e51c087', 'status': 'present'}, '`AUD-597-D05` / `N-04`, fourth appearance of `C4-27`': {'commit': '9cccc15', 'status': 'present'}, '`AUD-597-D06` / `ASK-CLI-001`': {'commit': '178aa6d', 'status': 'present'}, '`AUD-597-D07` / `N-03`': {'commit': '9245e35', 'status': 'present'}, '`AUD-597-R01` / `ASK-PERF-001`': {'commit': '6910054', 'status': 'present'}, '`AUD-597-R02` / `ASK-ENC-001`': {'commit': 'ce6dac1', 'status': 'present'}, '`AUD-597-R03` / `ASK-UX-002`': {'commit': 'c57e50b', 'status': 'present'}, '`AUD-597-X01` / `ASK-BUILD-001` + `ASK-META-001` / `B-12`': {'commit': '354338b', 'status': 'present'}, '`AUD-598-B01` / `ASK-META-001`': {'commit': 'b2f7c65', 'status': 'present'}, '`AUD-598-B02` / `ASK-UX-002`': {'commit': '56a4bf7', 'status': 'present'}, '`AUD-598-B03` / `B-11`': {'commit': 'c63cb24', 'status': 'present'}, '`AUD-598-B04` / `N-02`': {'commit': '187937f', 'status': 'present'}, '`AUD-598-R01` / `ASK-PERF-002`': {'commit': '', 'status': 'uncited'}, '`AUD-598-X01` / `N-01` + `B-13` + `B-14` + `B-29` + `N-06`': {'commit': '6f2f5a4', 'status': 'present'}, '`AUD-600-B01` / `P-01`': {'commit': '366ffed', 'status': 'present'}, '`AUD-600-B02` / `P-02` + `ASK-PACK-002`': {'commit': '2a814c3', 'status': 'present'}, '`AUD-600-B03` / `ASK-PACK-001`': {'commit': '452f2a0', 'status': 'present'}, '`AUD-600-D01`': {'commit': '1224e1b', 'status': 'present'}, '`AUD-600-D02`': {'commit': 'eab8172', 'status': 'present'}, '`AUD-600-R01` / `R-01`': {'commit': '88add64', 'status': 'present'}, '`B-3`': {'commit': '773268d', 'status': 'present'}, '`B-6`': {'commit': '17c6e06', 'status': 'present'}, '`B-8`': {'commit': '8329614', 'status': 'present'}, '`BUG-3` / `443b345`': {'commit': '', 'status': 'uncited'}, '`BUG-4a` / `AUD-588-B11` residual': {'commit': '24b4185', 'status': 'present'}, '`BUG-4b` / `AUD-588-B11` residual': {'commit': '0376350', 'status': 'present'}, '`E-39`, class residual of `AUD-593-N06`': {'commit': '25fc11f', 'status': 'present'}, '`N-5`': {'commit': 'c68083f', 'status': 'present'}, '`N-7`': {'commit': '082e9de', 'status': 'present'}, '`R-1`': {'commit': '424fb1c', 'status': 'present'}, '`R-2`': {'commit': '5158b92', 'status': 'present'}, '`R-3`': {'commit': '88aec04', 'status': 'present'}, '`R-4`': {'commit': '04fd04f', 'status': 'present'}}
5
- CLOSURE_COVERAGE = {'closed_rows': 107, 'present': 88, 'absent': 0, 'unresolved': 0, 'uncited': 19, 'unknown': 0}
3
+ BUILD_COMMIT = 'e2da5d84a30bc2e8c60325e1e2d48a8d736b8ad7'
4
+ CLOSURE_PROVENANCE = {'Window': {'commit': '', 'status': 'uncited'}, '`AUD-588-F05`': {'commit': '470e4a4', 'status': 'present'}, '`AUD-590-B01` / `B-01`, `F-01`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B02` / `B-02`, `F-02`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B03` / `B-03`, `F-03`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B04` / `B-04`, `F-04`': {'commit': '', 'status': 'uncited'}, '`AUD-590-R01` / `R-01`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A01` / `A-1`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A02` / `A-2`, `A-4`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A03` / `A-3`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A05` / `A-5`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A06` / `A-6`, `A-7`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A08` / `A-8`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A09` / `A-9`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A10` / `A-10`': {'commit': '', 'status': 'uncited'}, '`AUD-592-A02` / `A-1` second half, `§12.5`': {'commit': '63f3d61', 'status': 'present'}, '`AUD-592-B01` / `B-8`': {'commit': '8329614', 'status': 'present'}, '`AUD-592-D01` / `D-1`': {'commit': '0f8e03f', 'status': 'present'}, '`AUD-592-D02` / `D-2`': {'commit': '1d3db4d', 'status': 'present'}, '`AUD-592-D03` / `D-3`': {'commit': 'f28fe40', 'status': 'present'}, '`AUD-592-R01` / `A-1`': {'commit': '216025f', 'status': 'present'}, '`AUD-592-R02` / `N-4`, reopens `AUD-513-N04`': {'commit': 'ec3050d', 'status': 'present'}, '`AUD-593-N01` / `N-01`': {'commit': 'dd6a706', 'status': 'present'}, '`AUD-593-N02` / `N-02`': {'commit': 'f539787', 'status': 'present'}, '`AUD-593-N03` / `N-03`': {'commit': '1dd0b1c', 'status': 'present'}, '`AUD-593-N05` / `N-05`': {'commit': 'aa89388', 'status': 'present'}, '`AUD-593-N06` / `N-06`': {'commit': 'aa584a6', 'status': 'present'}, '`AUD-594-N04` / `D-5`, disclosure half of `B-3`': {'commit': 'a0060b7', 'status': 'present'}, '`AUD-594-X01` / `A-1`, residual of `AUD-593-N03`': {'commit': 'dd222b9', 'status': 'present'}, '`AUD-594-X02` / `A-1` second half': {'commit': '1193937', 'status': 'present'}, '`AUD-594-X03` / `X-03`, residual of `AUD-593-N01`': {'commit': 'e89e8f0', 'status': 'present'}, '`AUD-594-X04`': {'commit': '05b8cc9', 'status': 'present'}, '`AUD-595-A02` / `N-5`, advisory half of `AUD-588-B11`': {'commit': '3e15227', 'status': 'present'}, '`AUD-595-A03` / `B-6` exit-code half': {'commit': 'ab7285f', 'status': 'present'}, '`AUD-595-A05`': {'commit': 'e0b3d10', 'status': 'present'}, '`AUD-595-A07`, narrow half of `AUD-592-A02`': {'commit': 'cc15177', 'status': 'present'}, '`AUD-595-B01` / §B': {'commit': 'b77a1ad', 'status': 'present'}, '`AUD-595-Q01`': {'commit': '91a6566', 'status': 'present'}, '`AUD-596-A01` / `ASK-DET-001`': {'commit': 'fa508b4', 'status': 'present'}, '`AUD-596-A02` / `ASK-SELF-001`': {'commit': '4dfadc8', 'status': 'present'}, '`AUD-596-A03` / `ASK-C1-001`': {'commit': 'bffa3a6', 'status': 'present'}, '`AUD-596-A04` / `ASK-UX-003` + `ASK-UX-004`': {'commit': 'f0aac3c', 'status': 'present'}, '`AUD-596-B01` / `B-01`, harder witness for `BUG-6`': {'commit': '567d09d', 'status': 'present'}, '`AUD-596-B02` / `B-02`, class residual of `R2`': {'commit': '', 'status': 'uncited'}, '`AUD-596-B03` / `B-03`': {'commit': 'a8cb814', 'status': 'present'}, '`AUD-596-B06` / `B-06`': {'commit': '8107927', 'status': 'present'}, '`AUD-596-B07` / `B-07`, class residual of `AUD-592-R02`': {'commit': 'e060485', 'status': 'present'}, '`AUD-596-B09` / `B-09`': {'commit': '5b8146c', 'status': 'present'}, '`AUD-596-B10` / `B-10`': {'commit': '5cb994d', 'status': 'present'}, '`AUD-596-B16` / `B-16`': {'commit': '1507f69', 'status': 'present'}, '`AUD-596-D01` / `B-13` + `B-14`': {'commit': '0b1bdf0', 'status': 'present'}, '`AUD-596-D02` / `B-08`, residual of `AUD-594-N04`': {'commit': 'e7f73c3', 'status': 'present'}, '`AUD-596-D03` / `ASK-AGT-001`': {'commit': '8c46967', 'status': 'present'}, '`AUD-596-D04` / `B-18`': {'commit': '040f027', 'status': 'present'}, '`AUD-596-D05` / `B-19`': {'commit': 'fd2bf68', 'status': 'present'}, '`AUD-596-D06` / `B-20`': {'commit': '784cd83', 'status': 'present'}, '`AUD-596-D07` / `B-17`, half refuted': {'commit': 'c3ff822', 'status': 'present'}, '`AUD-596-D08` / `B-22`, class residual of `AUD-591-A06`': {'commit': 'b396c6a', 'status': 'present'}, '`AUD-596-D09` / `B-23`, surviving half of `B26`': {'commit': 'fd3d63f', 'status': 'present'}, '`AUD-596-D10` / `B-29`': {'commit': '', 'status': 'uncited'}, '`AUD-596-D11` / `ASK-UX-005`': {'commit': 'f4b4f77', 'status': 'present'}, '`AUD-596-D12` — six hints that send the reader back to the error': {'commit': '498df44', 'status': 'present'}, '`AUD-596-D13` / `B-28`': {'commit': '1014c72', 'status': 'present'}, '`AUD-596-D14` / `B-15`, narrow half of `B20`': {'commit': '10f2827', 'status': 'present'}, '`AUD-596-R01` / `B-05`': {'commit': '', 'status': 'uncited'}, '`AUD-596-X01` / `ASK-GATE-001`, gating half of `B24`': {'commit': 'e59c655', 'status': 'present'}, '`AUD-596-X02` / `ASK-UX-001` + `ASK-UX-002` + `B-21`': {'commit': 'f6ebf50', 'status': 'present'}, '`AUD-597-A01` / `ASK-UX-001` + `N-01`': {'commit': 'f8cf061', 'status': 'present'}, '`AUD-597-A02` / `ASK-UX-008` + `ASK-DOC-002`': {'commit': '9d47a4c', 'status': 'present'}, '`AUD-597-A03` / `ASK-DET-001`': {'commit': '4c7622a', 'status': 'present'}, '`AUD-597-A04` / `ASK-LEDGER-001`': {'commit': '46fb870', 'status': 'present'}, '`AUD-597-D01` / `B-04`': {'commit': '1094e6a', 'status': 'present'}, '`AUD-597-D02` / `ASK-DOC-001`': {'commit': 'ecb1bb7', 'status': 'present'}, '`AUD-597-D03` / `N-06`': {'commit': '9784333', 'status': 'present'}, '`AUD-597-D04` / `N-05`': {'commit': 'e51c087', 'status': 'present'}, '`AUD-597-D05` / `N-04`, fourth appearance of `C4-27`': {'commit': '9cccc15', 'status': 'present'}, '`AUD-597-D06` / `ASK-CLI-001`': {'commit': '178aa6d', 'status': 'present'}, '`AUD-597-D07` / `N-03`': {'commit': '9245e35', 'status': 'present'}, '`AUD-597-R01` / `ASK-PERF-001`': {'commit': '6910054', 'status': 'present'}, '`AUD-597-R02` / `ASK-ENC-001`': {'commit': 'ce6dac1', 'status': 'present'}, '`AUD-597-R03` / `ASK-UX-002`': {'commit': 'c57e50b', 'status': 'present'}, '`AUD-597-X01` / `ASK-BUILD-001` + `ASK-META-001` / `B-12`': {'commit': '354338b', 'status': 'present'}, '`AUD-598-B01` / `ASK-META-001`': {'commit': 'b2f7c65', 'status': 'present'}, '`AUD-598-B02` / `ASK-UX-002`': {'commit': '56a4bf7', 'status': 'present'}, '`AUD-598-B03` / `B-11`': {'commit': 'c63cb24', 'status': 'present'}, '`AUD-598-B04` / `N-02`': {'commit': '187937f', 'status': 'present'}, '`AUD-598-R01` / `ASK-PERF-002`': {'commit': '', 'status': 'uncited'}, '`AUD-598-X01` / `N-01` + `B-13` + `B-14` + `B-29` + `N-06`': {'commit': '6f2f5a4', 'status': 'present'}, '`AUD-600-B01` / `P-01`': {'commit': '366ffed', 'status': 'present'}, '`AUD-600-B02` / `P-02` + `ASK-PACK-002`': {'commit': '2a814c3', 'status': 'present'}, '`AUD-600-B03` / `ASK-PACK-001`': {'commit': '452f2a0', 'status': 'present'}, '`AUD-600-D01`': {'commit': '1224e1b', 'status': 'present'}, '`AUD-600-D02`': {'commit': 'eab8172', 'status': 'present'}, '`AUD-600-R01` / `R-01`': {'commit': '88add64', 'status': 'present'}, '`AUD-601-B01` / `N-02`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-B02` / `ASK-PACK-004`': {'commit': '', 'status': 'uncited'}, '`AUD-601-D01` / `N-01`, `ASK-UX-001`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-D02` / `B-13`, `B-14`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-M01` / `B-26`, `B-27` retracted': {'commit': '', 'status': 'uncited'}, '`AUD-601-R01` / `R-02`': {'commit': '', 'status': 'uncited'}, '`B-3`': {'commit': '773268d', 'status': 'present'}, '`B-6`': {'commit': '17c6e06', 'status': 'present'}, '`B-8`': {'commit': '8329614', 'status': 'present'}, '`BUG-3` / `443b345`': {'commit': '', 'status': 'uncited'}, '`BUG-4a` / `AUD-588-B11` residual': {'commit': '24b4185', 'status': 'present'}, '`BUG-4b` / `AUD-588-B11` residual': {'commit': '0376350', 'status': 'present'}, '`E-39`, class residual of `AUD-593-N06`': {'commit': '25fc11f', 'status': 'present'}, '`N-5`': {'commit': 'c68083f', 'status': 'present'}, '`N-7`': {'commit': '082e9de', 'status': 'present'}, '`R-1`': {'commit': '424fb1c', 'status': 'present'}, '`R-2`': {'commit': '5158b92', 'status': 'present'}, '`R-3`': {'commit': '88aec04', 'status': 'present'}, '`R-4`': {'commit': '04fd04f', 'status': 'present'}}
5
+ CLOSURE_COVERAGE = {'closed_rows': 113, 'present': 88, 'absent': 0, 'unresolved': 0, 'uncited': 25, 'unknown': 0}
@@ -16,6 +16,105 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
16
16
 
17
17
  ## Current Synchronization
18
18
 
19
+ **Corpus ampliado recibido, 2026-08-23 — 17 hallazgos nuevos en intake.**
20
+ [`AUDIT-2026-08-23-CORPUS-AMPLIADO.md`](AUDIT-2026-08-23-CORPUS-AMPLIADO.md) registra cuatro
21
+ P0 (`AUD-CA-001`, `002`, `003`, `014`), ocho P1, cuatro P2 y un P3. La evidencia externa
22
+ aporta reproducción y contraste manual, pero estas filas permanecen **pendientes de
23
+ reproducción contra checkout/wheel**: no se declaran corregidas ni se atribuyen al código
24
+ auditado. La prioridad inmediata es seguridad no modelada, colisiones de FQN y superficie
25
+ HTTP Micronaut; después siguen contratos de inventario, cobertura y precisión de salida.
26
+
27
+ **Correcciones iniciadas, 2026-08-23.** `AUD-CA-001` (declaración segura de Shiro),
28
+ `AUD-CA-005` (autoridad de `src/main`), `AUD-CA-013` (sobrecargas y anclas de
29
+ `explain`) y `AUD-CA-017` (contador estructurado) están corregidos en commits atómicos
30
+ de `5.8.25`. `AUD-CA-014` tiene una corrección parcial: Micronaut ya aparece como
31
+ superficie detectada-no-modelada y cuantificada, pero el soporte HTTP sigue pendiente.
32
+ Los restantes hallazgos permanecen abiertos hasta reproducirlos y cubrirlos con pruebas.
33
+
34
+ **Estado autoritativo del repositorio, 2026-08-23 — `5.8.25`, `HEAD 3d56d11`.** Este
35
+ snapshot supersede cualquier estado de release indicado en las secciones históricas de
36
+ este ledger. La batería completa pasa en verde cuando se configuran ambos almacenes
37
+ temporales: `SOURCECODE_CONTEXT_CACHE_DIR` para CIR/parse y `SOURCECODE_CACHE_DIR` para
38
+ snapshots/RIS. Usar sólo el primer directorio produce falsos fallos de persistencia.
39
+
40
+ Los commits `125e486`, `735d766`, `434d0e2`, `cd3f8f1`, `e9f5861`, `49187f9` y `3d56d11` están incluidos
41
+ en el checkout actual. Cubren rutas
42
+ Windows, selftest con `ask.exe`, resolución bounded de comandos y flags, fallback de
43
+ contexto, rutas POSIX del workspace, salida estable del job Windows, fingerprint sin estado
44
+ runtime de caché, sweep final del parse cache, gate CI de migraciones, trazabilidad explícita
45
+ de benchmarks históricos y aislamiento de observabilidad por invocación. Por tanto,
46
+ `ASK-UX-001`, `ASK-UX-002` y `ASK-ENV-001` ya no deben describirse como fallos sin
47
+ corrección: quedan **pendientes de verificación contra el wheel publicado y de confirmación
48
+ en GitHub Windows**.
49
+
50
+ La cola abierta actual es: validación externa de `ASK-DET-002`, `B-26` y `ASK-GATE-001`,
51
+ regeneración reproducible de cifras `B-11`, `ASK-PERF-003` (medición aislada aún
52
+ necesaria), `B-13`/`B-14` (reconciliación contra artefacto exacto), y la deuda P3 de filas
53
+ sin cita y `B-30`. No quedan fallos locales reproducidos pendientes en esta batería.
54
+
55
+ Las menciones posteriores a `5.8.24`, “release/commit pending” o “no audit, no release”
56
+ son fotografías históricas de sus respectivas iteraciones. Se mantienen para trazabilidad,
57
+ pero no representan el estado vigente ni deben usarse para decidir el siguiente release.
58
+
59
+ **External `5.8.25` regression audit received, 2026-08-23 — triage recorded.**
60
+ [`AUDIT-2026-08-23-EXTERNAL-5.8.25.md`](AUDIT-2026-08-23-EXTERNAL-5.8.25.md) separates the
61
+ Windows/MSAS wheel evidence from this checkout. The only new confirmed product defect is
62
+ `ASK-DET-002`: root `content_id` changes between cold and warm runs because `_cache` runtime
63
+ state is included in the answer fingerprint. `ASK-UX-001`/`ASK-UX-002` and
64
+ `ASK-GATE-001` remain actionable contract residues in the audited artefact; `ASK-ENV-001`
65
+ is a Windows-specific witness still requiring reproduction. `B-26` is recorded as a fleet
66
+ capacity issue (559.48 MB against 512 MB), not as a finding-analysis error. The reported
67
+ performance deltas are not accepted as regressions until isolated measurements control cache
68
+ state and concurrency. `B-13`/`B-14` require reconciliation against the exact wheel and
69
+ repository pair because the source re-audit disagrees with the external field result.
70
+
71
+ The validated commercial queue is now: deterministic/provenance envelope, bounded UX and
72
+ hint correctness, coherent CI gates, Windows envelope verification, then PR semantic gate,
73
+ bounded agent context, Java/Spring migration inventory, HTTP exposure provenance and SARIF
74
+ enrichment. The complete CTO assessment and acceptance criteria are in the linked audit;
75
+ they are not a claim that every auditor suggestion is a next-iteration fix.
76
+
77
+ **ThingsBoard field audit, 2026-08-23 — five findings documented and corrected in the
78
+ working tree; included in `5.8.25` and commit `2c551db`.**
79
+ [`AUDIT-2026-08-23-THINGSBOARD-FINDINGS.md`](AUDIT-2026-08-23-THINGSBOARD-FINDINGS.md)
80
+ records the evidence and acceptance tests for the missing negative scope (`AUD-THB-001`),
81
+ TX-006 precision (`AUD-THB-002`), ambiguous `none_detected` exposure semantics
82
+ (`AUD-THB-003`), incomplete evidence bounds in the generated report (`AUD-THB-004`) and
83
+ contradictory migration summary wording (`AUD-THB-005`). The audit found no runtime
84
+ regression, crash, write leakage or mutation of the audited checkout. The five authorities
85
+ and focused regression tests were updated and released; these rows are product gaps/defects, not
86
+ claims that ThingsBoard itself is broken.
87
+
88
+ **`AUD-601` queue closed, 2026-08-23 — seven rows, one commit each, no release.** Six closed
89
+ and one (`AUD-601-X01`) held with the half it was owed. Suite **12 702 / 0 red**,
90
+ `__version__` unchanged at `5.8.24`. ⚠ **Three more reported mechanisms are corrected by the
91
+ reproduction, on top of the three corrected at triage.** `D01`: the AUD-597-A01 generator does
92
+ cover the two lines the field read as bare — the *unmeasured* rendering never called it, and
93
+ underneath was an `lru_cache` that pins *"this command declares nothing"* whenever the front
94
+ page is composed before the command is registered. `D02`: `data-exposure` is **not**
95
+ byte-identical across two repositories (measured `mall` against `nacos`); what is real is that
96
+ `build_meta` derived repository identity a second time and published no tree state. `D01`'s
97
+ second half does not reproduce either: `posture . --diff dev:prod --compact` is
98
+ 124 268 B → 10 955 B on BroadleafCommerce, **×11.3**, not the 71 483 tokens under every flag
99
+ the report reads.
100
+
101
+ **The head of the queue was the method row, and it is the one that changes how the next round
102
+ is measured.** `AUD-601-M01`: four retracted performance findings in six rounds were all
103
+ measured honestly against a cache state nobody could establish. Every timed answer now
104
+ publishes `_meta.cache_state` — `cold` / `seeded` / `warm`, from the lookups each layer
105
+ actually **served**, never from files on disk — and `cache clear <repo>` stops leaving
106
+ `timeline-samples-v1/` standing in silence, which is the layer that produced the retraction.
107
+ `cache_state` is a `regress` condition field, so two cache states can no longer be compared as
108
+ if they were two versions. ⚠ **`peak_rss_mb` was reaching the determinism batteries as an
109
+ answer**: it is monotonic within a process, so two runs of an unchanged tree disagree on it by
110
+ construction — the `duration_ms` shape again, now in `_CLOCK_KEYS`, with
111
+ `regress.without_run_conditions()` replacing three hand-maintained pop-lists.
112
+
113
+ **`AUD-601-X01` is held, not closed, and `S-10` shipped instead.** It still does not reproduce
114
+ here through any entry path. The platform that produces it now runs the whole suite on every
115
+ push (`.github/workflows/windows-battery.yml`) plus named cases for what the field says is
116
+ broken, so the sixth witness arrives as a red build rather than as a sixth round of a held row.
117
+
19
118
  **Backlog sweep after `5.8.24`, 2026-08-23 — no audit, no release: the rows left open across
20
119
  the older passes, re-read against HEAD one by one.** Twelve rows closed. Five needed a fix and
21
120
  got one: `B25` (a declaration that parsed to nothing said *"no labels declared"*), `B27` (the
@@ -40,6 +139,45 @@ unreachable by construction. The same sentinels ran the delegated tasks as if
40
139
  `--all --include-config` had been typed. Suite **12 606 / 0 red**, `__version__` unchanged at
41
140
  `5.8.24`.
42
141
 
142
+ **Fourteenth audit pass, 2026-08-23 — two independent rounds on `5.8.24`, and the first
143
+ pass in the series whose subject is verifiable.** Round A (16-repository bank, Windows 11 Pro,
144
+ ~145 invocations) scores **8.7/10**, verdict *adopt*; Round B (MSAS `3dde0376`, eighth round on
145
+ the same tree, ~55 invocations) scores **8.88/10**, verdict *buy and upgrade*, with its
146
+ performance axis marked provisional by its own author. Both read
147
+ `build_commit 61b9c12b0e35…` and both read the same `closure_coverage` (107 closed rows, 88
148
+ `present`, 19 `uncited`), which this checkout reproduces exactly — `AUD-597-X01` and
149
+ `AUD-598-X01` paying out together. `AUD-600-R01` is **closed without residue** (`struts`
150
+ 40 911 → 688 ms on the first `--agent` call after a warm, ×59), and `AUD-600-B01` and
151
+ `AUD-600-B02` are confirmed from outside. The queue is `AUD-601-M01` · `AUD-601-R01` ·
152
+ `AUD-601-B01` · `AUD-601-B02` · `AUD-601-X01` · `AUD-601-D01` · `AUD-601-D02`, detailed in
153
+ *Fourteenth Audit Pass* below. **Three of the reported mechanisms are corrected by the
154
+ reproduction**: the memory zero is a written-down decision and it also makes `ASK_MAX_RSS_MB`
155
+ unreachable on Windows (P1, not P2); the budget advisory *works* and shipped on the one command
156
+ where the budget cannot bind, while the three that enforce publish nothing; and the pack's
157
+ futility hint fails because `bounded_floor` models list truncation where `--compact` on a pack
158
+ is a substitution. **One row is retracted by its own reporter after four reports** — `C3-128`
159
+ (`timeline` above 120 s) is a per-commit sampling layer, not a command cost — and the method
160
+ row it produced (`AUD-601-M01`) is the head of the queue. No code changed and no release:
161
+ `__version__` stays `5.8.24`.
162
+
163
+ **Third strategy memo, 2026-08-23 — commercial, not a defect pass, and it produced two
164
+ ledger rows anyway.** A third external memo audited positioning, unit of sale and price
165
+ ceiling from six rounds against the binary. Its verdict row by row is
166
+ [`docs/AUDIT-2026-08-23-COMMERCIAL.md`](AUDIT-2026-08-23-COMMERCIAL.md); the strategy it
167
+ changes is in `EXECUTION-PLAN-12MO.md` and the four rows it opened are `S-27`…`S-30`.
168
+ **Six of its claims changed state when measured against HEAD**, and three of those changed
169
+ the priority of the proposal they supported: the parse store is not the fleet blocker
170
+ (`ASK-17` measured the LRU sweep at ~3 % and retracted the 66 % with a number), `pack` runs
171
+ its components in **subprocesses** and not in memory (`packs.py:555`) so the intersection
172
+ block is a join over published artefacts rather than a shared-process recomputation — which
173
+ is the cheaper *and* the architecturally correct half — and `verify --init` derives all
174
+ three of `verify_rules._KINDS`, so the observed 20/20 is its thresholds degenerating in
175
+ silence rather than a missing rule kind. `cache clear --global` already does what the memo
176
+ said no flag did. **What the reproduction found that no memo did is `P-13`**: `is_large_repo()`
177
+ counts Java files per repository, so the 40-microservice organisation the product route
178
+ names as its buyer pays nothing. `P-14` records why the ledger cannot be published raw.
179
+ No code changed and no release: `__version__` stays `5.8.24`.
180
+
43
181
  **Latest attached audits — audit of release `5.8.23`, 2026-08-22, two independent rounds, and
44
182
  the first pass whose queue is led by a regression that four previous protocols could not have
45
183
  seen.** Round A (MSAS, `banyan-v2` @ `3dde0376`, the same subject bit for bit for the seventh
@@ -439,6 +577,182 @@ with neither parseable stdout nor its requested artifact) or `BUG-6`
439
577
  queue position. `BUG-1` and `BUG-5` gain witnesses on public OSS repositories, recorded
440
578
  under their own rows rather than as new ones.
441
579
 
580
+ ### Fourteenth Audit Pass: `5.8.24` findings / 16-repository bank (round A) + MSAS (round B) / 2026-08-23
581
+
582
+ **Both rounds audited the same artefact and it is verifiable for the first time in the
583
+ series.** `build_commit` reads `61b9c12b0e354cda157038bc8be6e19384a42368` in both reports and
584
+ in this checkout, and `version.provenance.closure_coverage` agrees to the row: **107 closed
585
+ rows, 88 `present`, 19 `uncited`, 0 `absent`, 0 `unresolved`**. That is `AUD-597-X01` and
586
+ `AUD-598-X01` paying out together — the seven-report question *"does the binary carry the
587
+ fix"* is now answered by the binary, and the third state `uncited` distinguishes *not tracked*
588
+ from *tracked and missing* rather than flattering the total. Round B says the consequence
589
+ plainly: two previous rounds concluded *"the fix did not reach the artefact"* about
590
+ `ASK-UX-001`; the artefact now declares it present, the help block demonstrably changed, and
591
+ the two broken lines are still there — so the correct reading became **the fix is in and does
592
+ not cover these two cases**, which is a different row.
593
+
594
+ Round A (the 16-repository bank, ~145 invocations, Windows 11 Pro) scores **8.7/10** against
595
+ 8.3, verdict **adopt**. Round B (MSAS, `3dde0376`, eighth consecutive round on the same tree
596
+ bit for bit, ~55 invocations, `ASK_READONLY=1` and an explicit ceiling on every one, 0
597
+ tracebacks, 0 writes) scores **8.88/10** against 8.84, verdict **buy and upgrade with one
598
+ operating reservation**, and marks its own performance axis **provisional**.
599
+
600
+ **`AUD-600-R01` is closed without residue and the closure is the most consequential
601
+ measurement of the cycle.** Round A re-ran its own protocol (`cache clear -y` → `cache warm`
602
+ → 1st → 2nd): `struts` (~3 000 Java files) goes **40 911 ms → 688 ms** on the first `--agent`
603
+ invocation after the warm, a **×59 improvement**, with the second at 674 ms — ratio 1.0 where
604
+ it was ×49.2. `mall` 12 487 → 665 ms (was ×16.6), `spring-petclinic` 1 615 → 683 ms (was
605
+ ×1.9). `AUD-600-B01` and `AUD-600-B02` are confirmed closed from outside as well: the gate
606
+ composes `verify-edit` and answers `UNVERIFIED` / exit 2 on a working tree that gained
607
+ `GET /internal/dump-config`, and `pack assessment --compact` goes 1 901 108 B → 35 619 B
608
+ (**×53**) where it previously refused with `OUTPUT_TOO_LARGE` after ~52 s.
609
+
610
+ **Four rows come out of this pass, and three of them correct the report's own mechanism.**
611
+ The queue is `AUD-601-R01` · `AUD-601-B01` · `AUD-601-B02` · `AUD-601-X01`, with `AUD-601-M01`
612
+ recording the method finding that outranks all of them.
613
+
614
+ **`AUD-601-R01` — the memory disclosure publishes a confident zero, and the ceiling above it
615
+ cannot fire.** Round A found `_meta.memory.peak_rss_mb: 0.0` on four loads spanning 49 → 3 000
616
+ Java files including the agent view, against its own independent `tasklist` sampling of ~1.4 GB
617
+ on `thingsboard`, and filed it P2 as *"the product's doctrine inverted where it applies it
618
+ best"*. Reproduced in source and it is **worse than reported, in two ways**. First, the zero is
619
+ not an oversight: `resource_budget.peak_rss_mb()` returns `0.0` when the `resource` module is
620
+ absent with the comment *"the zero value explicitly means the platform did not expose a
621
+ sample"* — a confident falsehood written down as a decision, in the axis this product exists
622
+ to refuse. Second, and unreported: `resource_budget.exceeded()` computes the refusal as
623
+ `peak <= ceiling → None`, so with `peak` pinned at 0.0 **`ASK_MAX_RSS_MB` can never fire and
624
+ `MEMORY_TOO_LARGE` is unreachable on Windows** — the declared platform. That is a gate that
625
+ cannot fail, which is the exact shape `AUD-597` refused once already when it declined `--ci`
626
+ on `migrate-check`, and the root help promises the opposite in writing (`cli.py:805`).
627
+ Verified on this host for contrast: `peak_rss_mb: 54.69` on `posture ./spring-petclinic`,
628
+ so the fault is the platform branch and nothing else. **P1, not P2.**
629
+
630
+ **`AUD-601-B01` — the budget disclosure shipped on the one command where the budget cannot
631
+ bind, and is absent on the three where it does.** Round A reproduces `N-02` for the fifth time
632
+ (`ASK_MAX_ANALYSIS_SECONDS=1e9` and `=1_000` → rc 0, 0 bytes of stderr, no advisory) and reads
633
+ it as the row `AUD-598-B04` refused. Measured here, the report is looking at the wrong surface
634
+ and the real defect is underneath it: `budget_binds` **works exactly as `AUD-598-B04` closed
635
+ it** — `migrate-check` publishes *"1e+09s is more than 100x the 11.3s this product has measured
636
+ for `migrate-check`, so no run of it reaches that deadline"* with `measured_anchor_seconds:
637
+ 11.3` — and `_budget_disclosure()` has **exactly one call site** (`cli.py:14437`,
638
+ `migrate-check`, whose scope is `advisory_only`). The three members of
639
+ `phased_run.BUDGET_RUNNER_COMMANDS` — `spring-audit`, `risk`, `audit-report`, the only commands
640
+ where a deadline can actually stop anything — publish **no `analysis_budget` block at all**
641
+ (measured: `spring-audit ./spring-petclinic` with the budget set carries no key containing
642
+ *budget* anywhere in its payload). A caller who sets a deadline on a command that enforces one
643
+ learns nothing; a caller who sets it on the command that ignores it gets the full advisory.
644
+ This is this ledger's most repeated class — a correct fix that stopped at one call site — and
645
+ the population it owes is `BUDGET_RUNNER_COMMANDS ∪ {migrate-check}`, read off the registry.
646
+
647
+ **`AUD-601-B02` — the ceiling hint's futility model does not know what `--compact` does to a
648
+ pack.** Round B files `ASK-PACK-004` (minor): `pack assessment .` refuses with *"No flag this
649
+ command declares can bring this answer under the ceiling"* while `--compact` brings the same
650
+ answer from 2 094 023 to ~8 800 estimated tokens, so *"an agent that reads the hint will never
651
+ try `--compact`"*. Reproduced here with the ceiling forced
652
+ (`ASK_MAX_OUTPUT_TOKENS=2000 ask pack assessment ./spring-petclinic`) and the mechanism is
653
+ precise and published in the refusal itself: `bounded_floor_tokens: 18201` with
654
+ `bounded_floor_basis` = *"this payload with every list emptied — the smallest answer any
655
+ list-bounding flag can produce"*. **The floor models list truncation, and `--compact` on a pack
656
+ is not a truncation — it is a substitution**: it replaces each component's body with that
657
+ component's own decision summary (`S-21` / `AUD-600-B02`, as shipped). So the hint is honest
658
+ against a model that does not describe the flag it is deciding about, and `hint_basis` even
659
+ prints `declared_flags: ["--compact"]` beside the sentence saying no declared flag helps. The
660
+ row is the model, not the sentence: `bounded_floor` must be measured for a pack the way the
661
+ pack bounds itself, or the futility claim must be withheld for surfaces whose bounding flag is
662
+ a substitution. Note for whoever takes it: `bounding_is_futile` has **no production call site**
663
+ (only `output_ceiling.py` and one test), so this hint arrives through the `nothing_smaller`
664
+ branch, not through the measured one `AUD-598-B02` built.
665
+
666
+ **`AUD-601-X01` — the envelope declares itself degraded in the field and does not reproduce
667
+ here; the platform is the only variable, and it is held rather than closed.** Round B measures
668
+ `_meta.envelope_status: "degraded"`, `envelope_status_reason: "the running command could not be
669
+ named"` and `command: "unknown"` on **13 of 13 payloads**, filed `ASK-ENV-001` (moderate,
670
+ precisely because the field self-declares instead of failing silently, which is the
671
+ observability the same auditor asked for). Measured on this checkout at the **same
672
+ `build_commit`**, through four entry paths — `run_cli.py`, `python -c "from sourcecode.cli
673
+ import main_entry"` (the path `packs.py:555` uses), the installed `ask` console script, and
674
+ each of those under `ASK_READONLY=1` and an explicit budget — `command` resolves to `posture`
675
+ every time, `envelope_status` is absent, and `peak_rss_mb` is real. So the degradation is
676
+ environmental, `_active_command_context()` returns `""` only where Click has no current context
677
+ at emit time, and **nothing here can name which environment does that**. Held with its
678
+ measurement in both directions rather than closed, the way `ASK-17`'s 457.6 s Windows figure
679
+ was held: it binds to `S-10` (a Windows runner in CI), which now has **five** defect witnesses
680
+ and zero reproductions, and that is what the row is for.
681
+
682
+ **`AUD-601-M01` — the method row, and it outranks the defects.** Round A **retracts `B-27` /
683
+ `C3-128`** after four consecutive reports: *"`timeline` is the only gate-shaped command above
684
+ 120 s with no possible ceiling"*, published at 198 s, 163.9 s, 75.2 s and 161.6 s, is a cache
685
+ artefact. Its own control, same command, same session: **161 641 ms** on the first pass over an
686
+ unsampled commit range, then **547 / 509 / 505 ms** on repeats, and **512 ms** after
687
+ `cache clear ./spring-security -y` — because the layer is per sampled commit and a repository
688
+ clear does not invalidate it, which `ask cache model` declares in writing
689
+ (*"timeline · nothing (repeat cached) · [timeline-samples]"*). The cost belongs to seeding a
690
+ layer the product documents as seeded once. That is the **fourth** false finding cache state
691
+ has produced on that bank in six rounds (×10.4 in `5.8.16`, ×2.42 and ×1.39 in `5.8.18`, ×1.93
692
+ in `5.8.20`, and this), so Round A raises `B-26` / `ASK-17` to the **head of its queue as a
693
+ P1 of method**: at 559.95 MB against a 512 MB budget, with `cache clear --all` leaving the
694
+ shared store at 25 095 entries before and after, *neither the maintainer nor an auditor can
695
+ attribute a cost figure to a version*. Two more figures go unattributed for the same reason
696
+ this pass — `B-21` (`posture ./thingsboard` at 59.7 s interleaved against 4.9 / 4.3 s isolated)
697
+ and `migrate-check ./spring-security` (×1.59, published as **not attributable** rather than as
698
+ a regression, with the six-round series 6.02 → 11.60 → 11.30 → 10.66 → 6.81 → 10.82 s showing
699
+ the 6.81 as the outlier and three controlled passes at 10 365 / 10 400 / 10 825 ms). ⚠ **One
700
+ correction this ledger owes the row**: `cache clear --global` exists and empties the shared
701
+ parse and CIR stores (`cli.py:18213`); `--all` preserving them is declared behaviour, not the
702
+ defect. What survives is the budget, and it is real.
703
+
704
+ **The two rounds disagree on performance, and the disagreement is the finding.** Round A, on
705
+ its own protocol (explicit `cache warm` of five repositories, settled host, interleaved ×4,
706
+ median, first pass discarded), measures **eight of nine anchors improving 4–17 %** and every
707
+ one of the six that rose in `5.8.23` back inside its band or below — `ask --help` at 1.74 s,
708
+ under the ×1.25 watch this ledger set. Round B, on MSAS, measures `posture` +55 %,
709
+ `spring-audit` ×2.1, `migrate-check` ×2.2, `selftest` +45 %, `compare` +39 % and `export --c4`
710
+ worse, while `ask . --compact` improves 1.3× (94.6 → 74.9 s), `validation` 1.5× and
711
+ `verify --init` 1.3×. **Round B marks its own axis provisional and names the confound**: its
712
+ first invocation of the session cost 94.9 s against 8.9 s on the second, so the session started
713
+ cold, and its parse store fell 28 106 → 22 491 entries. Neither round is discarded and no
714
+ regression is filed: the two protocols differ in exactly the variable `AUD-601-M01` is about.
715
+ Round B's own reservation is the publishable operating advice, because it comes from the
716
+ product's own numbers rather than from a stopwatch — `pack gate --compact` went 45.6 → 127.9 s
717
+ and `manifest.timing` attributes it: `verify` 566 ms, `verify-edit` **2 854 ms**, `posture`
718
+ **99 539 ms (78 %)**, `pr-impact` 24 175 ms. The new component is not the cost; comparative
719
+ `posture` is. That is `S-22` paying out in the first round that could attribute a composed cost
720
+ without an external clock.
721
+
722
+ **Two design suggestions of the previous round shipped and both rounds verify them.**
723
+ `non_coverage` per pack with its own ids (`NC-PACK-001`, `NC-PACK-003`) and the `packs-v1`
724
+ registry with the aggregation rule published rather than inferable (*"BLOCK wins over
725
+ UNVERIFIED, and UNVERIFIED over PASS"*, `verdict_vocabulary`, `exit_codes`, `formats`,
726
+ `artifacts_written`), whose `basis` reads *"Generated from the pack declarations in `packs.py`
727
+ and the format registry. Nothing here is maintained by hand beside the code it describes."* —
728
+ which is the doctrine whose breach produced `AUD-598-X01`. Not taken, and both rounds say so
729
+ without prompting: the `intersections` block (`S-23`) and `--format sarif` (`S-28`), the latter
730
+ refusing with `INVALID_USAGE` and listing the four valid formats, so there is no ambiguity.
731
+
732
+ **Movement on three long rows, recorded so nobody re-derives them.** `B-11` / `AUD-598-B03`:
733
+ 19 anchors, **16 still reading `5.1.0` and one regenerated on `5.8.23`** — the first movement in
734
+ ten reports, consistent with that row closing on the falsifiability half and owing the 16 field
735
+ figures. `B-13`: `validation` gains `schema_version: validation-v1`; repository identity is
736
+ still absent from `posture`, `validation` and `data-exposure`, and `data-exposure` is
737
+ **byte-identical between `mall` and `struts`**, which is `B-14`'s identical-refusal shape and
738
+ the reason the two rows travel together. `N-06` / `AUD-597-D03`: the confident empty string is
739
+ gone and the absence is `null`, exactly as closed; the file itself is still unpopulated, which
740
+ is the half that row published as owed.
741
+
742
+ **Open queue of the fourteenth pass.** Ordered by what it costs to leave open: the method row
743
+ first, because while it stands no cost figure from that bank is attributable to a version;
744
+ then the confident zero, which is the doctrine broken in the axis that sells it.
745
+
746
+ | ID | Severity | Current status | Required direction |
747
+ | --- | --- | --- | --- |
748
+ | `AUD-601-M01` / `B-26`, `B-27` retracted | **P1 of method — it is not an analysis defect and it outranks them** | **closed on `fix/aud600-queue`, unreleased — `__version__` stays `5.8.24`.** The fix is not a faster command: a timed answer now declares the state it was measured in. `cache_observation` collects what each layer **served** this run — never what exists on disk, which `AUD-595-B01` already established costs like a cold store — and the envelope publishes `_meta.cache_state`: `cold` when every lookup missed, `warm` when every one hit, `seeded` when both, and **absent** where the command consulted no layer (`cold` for a layer nobody reads is `AUD-591-Q01` read the other way). `cir`, `snapshot` and `timeline-samples` report their lookups; `parse` keeps its own tally and is read at disclosure. Measured end to end: `posture ./spring-petclinic` cold declares `cir 0/1, parse 0/30`, the immediate repeat declares `warm`. ⚠ **The second half is the one that produced the retractions**: `cache.clear()` removed `core-*`, `snapshot-*`, `view-*` and the CAS directory and **never touched `timeline-samples-v1/`** — the key carries no tree state — and said nothing about it. `--all` now empties it and a plain clear names the samples it left standing, so `cache clear --all -y` is what makes one repository genuinely cold. `cache_state` joins `regress._CONDITION_FIELDS` (a comparison across two cache states is disqualified, not reported as drift) and `peak_rss_mb` joins `_CLOCK_KEYS`. `regress.without_run_conditions()` is now the single authority the three determinism batteries read, replacing three hand-maintained pop-lists that each learned about `cache_layers`, then `timings`, then this, one red run at a time. Commit `17915f0`. *Previous verdict:* **open.** Round A retracts `B-27` / `C3-128` after four reports with its own control (161 641 ms first pass over an unsampled commit range; 547 / 509 / 505 ms on repeats; 512 ms after `cache clear ./spring-security -y`, because the layer is per sampled commit and the product declares it: *"timeline · nothing (repeat cached) · [timeline-samples]"*). Fourth false finding cache state has produced on that bank in six rounds (×10.4 in `5.8.16`, ×2.42 and ×1.39 in `5.8.18`, ×1.93 in `5.8.20`, this). Two more figures unattributed this pass: `B-21` (`posture ./thingsboard` 59.7 s interleaved against 4.9 / 4.3 s isolated) and `migrate-check ./spring-security` (×1.59, published as **not attributable** with its six-round series and three controlled passes at 10 365 / 10 400 / 10 825 ms). Store at **559.95 MB against 512 MB**, `cache clear --all` leaving 25 095 entries either side. ⚠ **One half of the row is answered already and the report did not see it**: `cache clear --global` empties the shared parse and CIR stores (`cli.py:18213`); `--all` preserving them is declared, not the defect | The budget, and a way for an auditor to establish cache state as an input rather than infer it. `cache status` already publishes per-repository coverage (`ASK-17`, `5.8.5`); what is missing is a stated protocol the product itself can assert — a documented *cold / seeded / warm* declaration in the payload of a timed command, so a figure carries the state it was taken in. Until then this ledger publishes bank figures as **not attributable**, which is what Round A did with `migrate-check` and is the correct behaviour |
749
+ | `AUD-601-R01` / `R-02` | **P1 — a confident zero, and a ceiling that cannot fire on the declared platform** (the report filed P2) | **closed on `fix/aud600-queue`, unreleased — `__version__` stays `5.8.24`.** Reproduced exactly as triaged, and both halves are fixed. `peak_rss_sample()` is the single authority and returns `(value, basis)`: `resource` where it exists, then `psutil`, then `GetProcessMemoryInfo`, and `(None, why)` where none answer. The disclosure publishes `peak_rss_mb: null` with `peak_rss_basis` and `ceiling_enforceable`, and **no `*_mb` key is ever a bare `0.0`**. The unreported half — the P1 — is closed too: a ceiling configured on a platform that cannot be sampled ends in a structured `MEMORY_CEILING_UNAVAILABLE` instead of passing in silence, so `ASK_MAX_RSS_MB` can no longer be a gate that cannot fail. Where no ceiling was asked for, nothing changed. `cli.py:805`, the user guide and the manual say what the payload does. Commit `9c763f3`. *Previous verdict:* **open.** `_meta.memory.peak_rss_mb: 0.0` on four loads spanning 49 → 3 000 Java files including `--agent`, against the reporter's own `tasklist` control of ~1.4 GB on `thingsboard`. Reproduced in source and **wider than reported**: `resource_budget.peak_rss_mb()` returns `0.0` when the `resource` module is absent, with the comment *"the zero value explicitly means the platform did not expose a sample"* — the confident falsehood written down as a decision, in the one axis this product exists to refuse. Unreported half: `resource_budget.exceeded()` refuses only when `peak > ceiling`, so with `peak` pinned at 0.0 **`ASK_MAX_RSS_MB` can never fire and `MEMORY_TOO_LARGE` is unreachable on Windows**, while the root help promises the opposite (`cli.py:805`). A gate that cannot fail is the shape `AUD-597` already refused once. Contrast measured here: `peak_rss_mb: 54.69` on `posture ./spring-petclinic`, so the branch is the whole fault | `peak_rss_mb: null` with `peak_rss_basis: "not measurable on this platform (<reason>)"` where the platform does not expose it, and a real sample via `psutil` or `GetProcessMemoryInfo` where it does — the reporter's own acceptance criterion, and it is the right one. Second half, which the report did not ask for and which is the P1: where the sample is `null`, `exceeded()` must **refuse to answer** rather than return `None`, and `MEMORY_TOO_LARGE` must be declared unavailable instead of silently unreachable. Assertion: no `*_mb` key may be `0.0` in an invocation that completed an analysis, and no ceiling may be published as enforced on a platform where its input is `null` |
750
+ | `AUD-601-B01` / `N-02`, 5th round | **P2 — the fix landed on the one command where the budget cannot bind** | **closed on `fix/aud600-queue`, unreleased — `__version__` stays `5.8.24`.** The triage was right and the fix is the population. The envelope now publishes `analysis_budget` for **every command that reads `ASK_MAX_ANALYSIS_SECONDS`**, resolved through `analysis_budget.BUDGETED_ANALYSIS_COMMANDS` — a superset of `BUDGET_RUNNER_COMMANDS ∪ {migrate-check}`, because a caller who set a deadline on `verify` was equally uninformed. A command that never consults the variable carries **no block**, never one saying the budget does not apply. Measured: `spring-audit ./spring-petclinic` with the budget at `1e9` answers `budget_binds: false` against its 31 s anchor where it published nothing at all; `audit-report` has no measured anchor and answers `null` with the reason said out loud rather than a bound it cannot demonstrate. The battery is parametric over the registry, which is what this class keeps returning for. Commit `eb6e013`. *Previous verdict:* **open, and the report is looking at the wrong surface.** `AUD-598-B04`'s `budget_binds` **works**: `migrate-check` with `ASK_MAX_ANALYSIS_SECONDS=1e9` publishes *"1e+09s is more than 100x the 11.3s this product has measured for `migrate-check`, so no run of it reaches that deadline"* with `measured_anchor_seconds: 11.3`. But `_budget_disclosure()` has **exactly one call site** (`cli.py:14437`), and it is that command — whose scope is `advisory_only`. The three members of `phased_run.BUDGET_RUNNER_COMMANDS` (`spring-audit`, `risk`, `audit-report`), the only ones where a deadline can stop anything, publish **no `analysis_budget` block at all**: measured on `spring-audit ./spring-petclinic` with the budget set, no key containing *budget* appears anywhere in the payload. So a caller who sets a deadline on a command that enforces one learns nothing, and a caller who sets it on the command that ignores it gets the full advisory. Round A reads *"rc 0, 0 bytes of stderr, no advisory"* and is right about stderr and wrong about the payload | Publish `_budget_disclosure()` over the population, not over the command that happened to get it: `BUDGET_RUNNER_COMMANDS ∪ {migrate-check}`, read off `phased_run` rather than from a list. The assertion is parametric over that registry — every member publishes `analysis_budget` with `applies`, `budget_binds` and its basis — which is the same sweep shape `S-01` established and the reason this class keeps returning when it is closed by instance |
751
+ | `AUD-601-B02` / `ASK-PACK-004` | **P3 — the futility model does not describe the flag it is deciding about** | **closed on `fix/aud600-queue`, unreleased — `__version__` stays `5.8.24`.** The mechanism identified at triage is the one that was fixed, and the contradiction is gone. The vocabulary now declares which options `bounded_floor` actually models — the ones that only shorten lists (`--limit`, `--top-n`, `--min-severity`, `--max-nodes`, `--max-edges`) — and a command still holding an unmodelled option keeps its futility claim **withheld**, with the flag still offered and the payload naming what its floor is silent about (`bounded_floor_does_not_model`). Measured: `ASK_MAX_OUTPUT_TOKENS=2000 pack assessment ./spring-petclinic` now opens *"Use --compact (bounded decision summary, same report)"* where it opened with *"No flag this command declares…"* beside `declared_flags: ["--compact"]`. Where the model covers everything a command can do to its own answer, `AUD-598-B02`'s measurement is untouched. Nothing branches on a command name. Commit `a5fdc4a`. *Previous verdict:* **open, mechanism identified here and different from the report's.** `pack assessment .` refuses with *"No flag this command declares can bring this answer under the ceiling"* while `--compact` takes the same answer to ~8 800 estimated tokens. Reproduced with the ceiling forced (`ASK_MAX_OUTPUT_TOKENS=2000 ask pack assessment ./spring-petclinic`): the refusal publishes `bounded_floor_tokens: 18201` and `bounded_floor_basis` = *"this payload with every list emptied — the smallest answer any list-bounding flag can produce"*, and prints `hint_basis.declared_flags: ["--compact"]` beside the sentence saying no declared flag helps. **The floor models list truncation; `--compact` on a pack is a substitution** — it replaces each component's body with that component's own decision summary (`S-21` / `AUD-600-B02`, as shipped). The hint is honest against a model that does not fit the surface. Note for whoever takes it: `bounding_is_futile` has **no production call site** (only `output_ceiling.py` and one test), so this arrives through the `nothing_smaller` branch and not through the measured one `AUD-598-B02` built | Measure the floor the way the surface bounds itself, or withhold the futility claim where the bounding flag is a substitution rather than a truncation. A refusal that names a flag as declared and in the same breath says no declared flag helps is the contradiction to remove first — cheapest correct fix is to compute `bounded_floor` by applying the surface's own bounding transform, which for a pack is the `--compact` renderer it already has |
752
+ | `AUD-601-X01` / `ASK-ENV-001` | **P3 — held, not closed: it does not reproduce here and the platform is the only variable** | **held, and the half that was missing has shipped — `__version__` stays `5.8.24`.** Still not reproduced: through `run_cli.py` and the `from sourcecode.cli import main_entry` path `packs.py:555` uses, each plain, under `ASK_READONLY=1` and under an explicit budget, `command` resolves every time and `envelope_status` is absent. Asserting it away would be the confident answer the row is about. What ships is `S-10`: the platform lived in a report and not in the battery, which is why it has **five defect witnesses and zero reproductions**. `tests/test_the_envelope_names_its_command_on_every_entry_path_aud601_x01.py` asserts what the field says is broken — the envelope names its running command on each entry path, the footprint sample is a number or a stated null and never `0.0`, and a ceiling that cannot be evaluated refuses instead of passing — and `.github/workflows/windows-battery.yml` runs the whole suite on `windows-latest` on every push. The next witness arrives as a red build, not as a sixth round. Commit `07560b2`. *Previous verdict:* **open.** Round B measures `_meta.envelope_status: "degraded"`, `envelope_status_reason: "the running command could not be named"` and `command: "unknown"` on **13 of 13 payloads**, and grades it moderate rather than severe precisely because the field now self-declares instead of failing silently — the observability the same reporter asked for. Measured on this checkout at the **same `build_commit`** through four entry paths (`run_cli.py`; `python -c "from sourcecode.cli import main_entry"`, the path `packs.py:555` uses; the installed `ask` console script; each under `ASK_READONLY=1` and an explicit budget): `command` resolves to `posture` every time, `envelope_status` is absent, `peak_rss_mb` is real. `_active_command_context()` returns `""` only where Click has no current context at emit time, and nothing here can name the environment that produces it | Held with its measurement in both directions, the way `ASK-17`'s 457.6 s Windows figure was held — asserting it away would be the confident answer this row is about. It binds to `S-10` (a Windows runner in CI), which now has **five** defect witnesses and zero reproductions, and that is the fix: the platform that produces these needs to be in the battery, not in a report |
753
+ | `AUD-601-D01` / `N-01`, `ASK-UX-001`, 5th round | **P2 — declaration, and the closure reading changed** | **closed on `fix/aud600-queue`, unreleased — `__version__` stays `5.8.24`.** ⚠ **the reported mechanism is corrected: the generator covers those two lines, and the rendering the field read never called it.** With no measured scope, `_start_here_block` printed `_START_HERE` verbatim — so `posture . --diff dev:prod` and `endpoints .` appeared bare and `migrate-check . --compact` showed a flag only because that line carries one in the tuple, which is the reported symptom word for word. The output ceiling is a property of the **command**, not of the repository, so both renderings now bound the whole block; the static path still declines to claim an order it could not measure, which is what `AUD-596-X02` actually asked of it. ⚠ **Underneath was a memo that pins a wrong answer**: `_bounded_invocation` was an `lru_cache` and the front page is composed while `cli` is still importing, so a caller arriving before a command is registered gets *"declares nothing"* and every later render in that process is served it. Only resolutions that found the command are stored now. ⚠ **The second half does not reproduce**: the report reads `posture . --diff dev:prod` at 71 483 tokens with no flag, with `--compact` and with `--limit 20` alike; measured on BroadleafCommerce (2 985 Java files), `--compact` takes it 124 268 B → 10 955 B, **×11.3**. Commit `a6c93eb`. *Previous verdict:* **open, and for the first time the diagnosis is not *"the fix did not ship"*.** `CLOSURE_PROVENANCE` reports `AUD-597-A01` (commit `f8cf061`) as `present`, the help block demonstrably changed — `risk .` moved into a new category *"Too big for this session — `--output <file> --detach`, or nightly"* — and the two broken lines are still there: *"Runs now"* still carries `posture . --diff dev:prod` and `endpoints .` with no bounding flag, and only `migrate-check . --compact` shows one. So the generator does consult cost now and does not cover those two lines. Same correction for `AUD-598-B02` (`ASK-UX-002`, commit `56a4bf7`, `present`) with the payload not moving a byte: no flag / `--compact` / `--limit 20` all return exactly 71 483 tokens / 285 935 B | Sweep the five lines of the block, not the one that was fixed — the population is the block, which is `S-01`'s rule applied to a generator. And the sentence that closes `ASK-UX-002` already exists in the product: the honest-ceiling hint *"the weight is not in the lists they bound"* shipped for packs and was not applied to `posture --diff`, where the hint still offers `--compact` over an answer `--compact` does not move |
754
+ | `AUD-601-D02` / `B-13`, `B-14`, 5th round | **P3 — identity, and an identical refusal that proves it matters** | **closed on `fix/aud600-queue`, unreleased — `__version__` stays `5.8.24`.** ⚠ **the reported symptom does not reproduce**: measured on `mall` against `nacos`, the two `data-exposure` payloads differ in `repo_id`, `git_head`, `scope.path`, `scope.repo_root` and `inputs.tree` — the envelope has carried identity on all three surfaces since `B14`. What the reproduction found is the mechanism the row is really about: `build_meta` assembled that identity **a second time**, reading the tree once for `run_id` and then asking git for HEAD again, and it published neither the tree state nor whether the target is a checkout at all — so an answer about a dirty tree could not be told from an answer about the clean one. `build_meta` now derives the whole block from `envelope.repository_identity()`, the authority `pack_manifest` is already bound to: one git read instead of two, and `scope` publishes `is_git_repository` and `tree_state` (`clean` / `dirty` / `not-a-git-repository` — a state, never a silence that reads as clean). The battery asserts the two agree field by field, that a dirty tree says so, and — `B-14`'s shape — that one refusal about two repositories is two payloads. Commit `fbb259d`. *Previous verdict:* **open, with movement.** `validation` gains `schema_version: validation-v1`. Repository identity is still absent from `posture`, `validation` and `data-exposure`, and `data-exposure` is **byte-identical between `mall` and `struts`** — two different repositories, one answer, nothing in it naming either. That is `B-14`'s identical-refusal shape and the reason the two rows travel together: without identity in the envelope, a refusal cannot be told from a refusal about something else | The envelope block on the three payloads, from `envelope.repository_identity()`, which exists and is already the authority for `pack_manifest`. Assertion over the population that publishes answers about a repository, not over the three named here — closing this by instance is how it reached a fifth round |
755
+
442
756
  ### Thirteenth Audit Pass: `5.8.23` findings / MSAS (round A) + 16-repository bank (round B) / 2026-08-22
443
757
 
444
758
  **Verdicts first. Round A: buy and upgrade — 8.84/10 against 8.58, the maximum of the
@@ -1435,7 +1749,7 @@ retained only as evidence of what the auditor observed before the fix.
1435
1749
  | 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 | **closed 5.8.6 — by refuting the premise the row carried, with the measurement inside the phase.** Instrumented on BroadleafCommerce: of the 10 126 ms the phase costs, **14,0 s is method extraction over 2 766 files** and the walk it feeds runs in **0,01 s** and finds nothing. So the cost was never the analysis: it was parsing a universe the walk cannot reach. The walk starts at parameters annotated as HTTP inputs (18 of 2 766 files on that repository) and resolves callees **by name**, and a file whose text does not contain that name followed by `(` cannot declare it — the same regex that finds methods says so, which makes the pruning an equivalence and not a heuristic, the shape of the C3-88 guard one level up. `_DemandParsedMethods` parses the seeds first and then only what the walk asks for, transitively; in the worst case that is every file, which is exactly the old cost and never more. Measured end to end through the CLI: **`risk` 13 396 → 5 722 ms (−57 %)**, the phase 10 126 → 526 ms, with findings, defects and every row byte-identical (only clocks and run ids move). Also identical on keycloak-config-cli, spring-petclinic and jobrunr — whose 12,7 s of parse for zero seeds the C3-88 comment already recorded. One extractor (`_methods_in_text`) serves both universes, the demand-driven one never publishes itself onto the CIR (a partial universe cached as the complete one is the failure the eager path's own comment warns about), and the C3-121 budget check still runs per file inside the parse. Regression `tests/test_http_input_walk_demand_parse_c3_127.py`, 9 assertions, including a positive chain asserted equal to the eager reference. **History:** 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. ⚠ **Re-profiled by the field on 5.8.5, and the reporter retires half of their own premise**: in `risk`'s trace at 43,7 s on the audited corpus, `resolving the conditional bean graph` **no longer appears as a stage at all** (*"o lo habéis cacheado ya, o cae por debajo del muestreo de 5 s. Retiro esa mitad de mi propuesta"*), which agrees with our own 525 ms measurement. What is left is one substrate and one hot spot: `indexing the validation surface` ~10 s of the 43,7 s (against `validation` standalone at 19,6 s), so a cross-command cache is worth ~−22 % of `risk` here and makes `validation` free once `risk` has run; and **`composing risk factors (reading HTTP-input query sinks)` at ~15 s of 43,7 s (34 %)**, unchanged since round 4 and the same phase our BroadleafCommerce run puts at 75 % — **two corpora now name the same walk as the largest single cost in the CLI**, which makes it the profiling target with the best return and outranks the cache. The parallel lever above both stays `C3-84`: the rule pass is serial on a 20-core host |
1436
1750
  | 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 | **closed 5.8.6 — the harness ships, and the protocol with it.** `perf.steady_verdict` (six runs, not four; an unconverged sample publishes **no** figure), `perf.contention_verdict` (the control command interleaved: `clean` / `host` / `command`, and only the last is about the product), `perf.host_verdict` (another `ask` process disqualifies the host before anything is measured) and `perf.measurement_protocol()`, with `scripts/perf_harness.py --steady` running them — warm-up discarded, control interleaved between every target pass, both cache bases isolated (E-37) and `ASK_PARSE_CACHE_MAX_MB` pinned. It exits non-zero when a cell did not converge **or** when the session is not attributable to the build. The field's three sessions are replayed as data the way `ANCHOR_MULTIPLE_VALIDATION` replays the six measured releases — the 5.8.5 round-10 session (control 1,29× beside target 1,25×) must classify as `host`, round 9 as `clean`, and a held control beside a moving target as `command` — so a later change to either threshold that stops classifying them correctly fails in the battery instead of in a round. `docs/perf/REGRESSION-GATE.md` §5.1 publishes the protocol. Regression `tests/test_measurement_protocol_p1proc.py`, 15 assertions. **History:** 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. ⚠ **Seventh request, and the protocol is amended by its own author after a ninth false positive — caught before it was reported, which is the point.** Four amendments, all binding here: **(1) four runs are not enough.** A transient survived four passes and died on six: `endpoints` measured 21–28 s (a reported +141 %/+250 %) against a steady state of 7,7–8,0 s, and `spring-audit` 22,9 s against 9,2 s. The minimum is now **6 runs to convergence**, minimum reported. **(2) A control command is mandatory, and it is the piece that was missing for six rounds.** Interleave a cheap, stable command with the expensive one in the same session — `impact <Class> .` (~4,2 s, the most stable in the CLI) against `risk . -o f` — and fail the measurement if the *cheap* one disperses: `assert dispersion(control) < 1.15`. This is what stopped round 10 from reporting a 5.8.5 regression: `spring-audit` at 1,39× and `risk` at 1,49× (both over the 1,25× gate, where all seven were under it in 5.8.4) sat beside a control that dispersed 1,29× when it had measured 1,08× in the same round — proportional movement is the signature of host contention, not of a degraded command. **(3) Host hygiene is asserted, not assumed**: `ask` processes = 0 before measuring (cross-session contention has produced +300 % to +549 % in this ledger), and the round-10 host was carrying 404 active processes. **(4) Isolate *both* cache bases (`E-37`) and pin `ASK_PARSE_CACHE_MAX_MB`; read `metadata.timings`, never `analysis_time_ms`**, which inherits the contention bias it is being used to detect. The one figure round 10 leaves unresolved — that dispersion — is explicitly **not attributed to 5.8.5** and is exactly what this harness, run on a verified-idle host, settles in a single run instead of a round of argument. Of the reporter's nine retired false positives, **eight are of one family (cache state or contention)**, which is the strongest argument this row has ever carried |
1437
1751
  | 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. **Refutation reproduced independently in cycle 8, under the reporter's own protocol**: both stores isolated and emptied, `ASK_PARSE_CACHE_MAX_MB=4096`, 2 passes per version — 5.7.2 at 112,0 / 112,6 s against 5.8.5 at 113,8 / 115,2 s (~3 %), and an A/B of the budget itself (512 MB sweeping by LRU against 4 096 MB that cannot sweep) at 9,1–10,1 s against 9,0–9,7 s: **the LRU sweep costs nothing measurable**. The +66 % is retired as the reporter's eighth false positive, with `E-37` named as its confounder. |
1438
- | 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. |
1752
+ | 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 | **RETRACTED 2026-08-23 by its own reporter, after four reports — it is a cache artefact, not a command cost.** Control, same command, same session: **161 641 ms** on the first pass over an unsampled commit range, then **547 / 509 / 505 ms** on repeats, and **512 ms** after `cache clear ./spring-security -y` — the repository clear does not invalidate it because the layer is **per sampled commit**, which `ask cache model` declares in writing (*"timeline · nothing (repeat cached) · [timeline-samples]"*). The 198 s, 163.9 s, 75.2 s and 161.6 s of four rounds are the price of seeding a layer the product documents as seeded once, paid by a protocol that did not establish cache state as an input. What survives is **not** a `timeline` row: it is `AUD-601-M01`, the method row this retraction opened — the fourth false finding cache state has produced on that bank in six rounds. *Previous verdict:* **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. |
1439
1753
  | 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. ⚠ **Round 10 ran on the 5.8.5 build and still reports the renames as *«deuda bien declarada, pero sin fecha»*, asking for precisely what `BC-002` already ships.** The row stays closed — the window exists, is announced 5.8.5 / effective 6.0.0, and is printed by `ask schema breaking-changes-v1`, carried in the CHANGELOG's `## Upgrading` and projected into `schemas-v1` as `pending_renames_window`. What it leaves behind is a **discoverability check, not a defect**: the reporter quoted `pending_renames` and `counts` out of the registry payload and did not see the window beside them, so verify that `pending_renames_window` travels in the same payload those two keys do — and if it does not, that is where it belongs. A declaration a ten-round auditor cannot find is not yet declared to a consumer. |
1440
1754
  | E-40 | **Three commands delegate to a fourth and hand it `typer.Option` objects instead of values, so the cache they are sold on can never hit.** `onboard`, `review-pr` and `fix-bug` are `prepare-context` under three names and delegated with `ctx.invoke(prepare_context_cmd, ...)`. Click fills declared defaults only for a `click.Command`; handed the *function* Typer decorated it calls it directly, so every parameter the caller did not name kept its `typer.Option(...)` sentinel — an object that is **truthy** and whose `repr` carries its own address. Measured on `ask fix-bug <repo> -o out.json`, three identical invocations: the task cache key is built from `sym=;all=;cfg=;timeout=` and read `all=<typer.models.OptionInfo object at 0x10b5a82d0>`, so each run wrote a **new** entry (`…-fb09db5a-json`, `…-6d3c8370-json`, `…-52f29d3c-json`) and never read one — the warm path was unreachable by construction on the three commands whose sub-second re-run is the claim. Two more from the same sentinel: `include_config` and `all_gaps` reached `builder.build(...)` truthy, so the delegated tasks ran as if `--all --include-config` had been typed, and `_apply_jobs` raised inside its own `except`, leaving `--jobs` unapplied | found here while closing `C4-28`'s residual (the silent warm write is only reachable once the cache can hit), not reported by the field | Medium-High — a published performance claim that cannot be true, and two flag defaults inverted on the product's primary agent surface | **closed after `5.8.24` (`2317af7`).** `_invoke_command` is the one delegation seam: a parameter the caller names wins, every other one gets the value the callee declares, read from the callee's own signature. After it the key is stable (`pctx-fix-bug-d5b9a77-21a8847c-json`) and the second run is a hit. Regression `tests/test_delegated_command_defaults_e40.py`, 15 assertions parametrised over the three delegating commands, including an AST assertion that no `ctx.invoke(<command function>)` returns to `cli.py` and an end-to-end witness that two identical runs share one cache key. The affordance battery follows the new seam (`3f5b0e7`), so the spinner check still sees through the delegation. |
1441
1755
  | E-38 | **A class-level route prefix carried by a meta-annotation is dropped, and the route is published without it.** `_build_route_surface` reads the class prefix only when the class symbol carries `@RequestMapping` or `@Path` **literally** (`repository_ir.py:5882-5887`); a repository that declares its own composed annotation gets `prefixes = [""]`, and the published `path` is the method suffix alone. **Measured on shenyu (5.8.17): 35 of 365 published routes — 9,6 %, over 21 controllers — carry `path: "/"`.** `AiProxyApiKeyController` is annotated `@RestApi("/selector/{selectorId}/ai-proxy-apikey")`, a Shenyu annotation meta-annotated with `@RestController` + `@RequestMapping`; its five handlers are published at `/`, `/`, `/batchDelete`, `/{id}`, `/page`. The served URLs do not exist as published. This is a **confident falsehood**, not a gap: no `path_resolution: "unresolved"`, no `path_expression`, no warning, and `route_census.distinct_routes` (316 of 365) reads the collisions as if two controllers genuinely shared a path. It also propagates to every consumer keyed on route text — `explain-endpoint`, `data-exposure --path-prefix`, `validation --path-prefix`, `pr-impact` route matching — where a correct query returns nothing. **The machinery already exists on the other axis**: `E-12`/`5.7.1` closed exactly this class for *beans* by resolving the spelling against the graph's `imports` edges, and `BeanGraph.build` already builds a meta-annotation map in its first pass. The route axis never asked. Vendor-agnostic by construction: the rule is "an annotation this repository declares that itself carries `@RequestMapping`/`@Path`", never a proprietary name. | found 2026-08-21 during the `AUD-590-R01` census triage, on the corrected 5.8.17 build; **not part of the seventh-pass queue and deliberately not fixed in it** — it moves route counts on any repository using composed annotations and needs its own measured pass with the golden set re-baselined | **High** — it is the rule this repository enforces most loudly, inverted on the route axis: a published route that is not served, with no affordance saying so. Blast radius is every repository with a framework-level or in-house composed controller annotation; shenyu is the witness, dubbo/sa-token/halo are unmeasured | **closed `5.8.18`** (`096c7c3`) — direction taken as written: resolve the class-level prefix through the meta-annotation map `BeanGraph.build` already computes, in the order `E-12` established (explicit single-type import decides; wildcard leaves it admissible; a repository-declared annotation in the owner's package is the owner's own; **nothing resolved stays unresolved rather than acquiring a verdict from silence**). Where it cannot be resolved, publish `path_resolution: "unresolved"` with the annotation as `path_expression` — the contract the method-level path already honours — never a bare `/`. Regression on a fixture declaring its own composed annotation, plus a shenyu assertion that no published route is `/` |
@@ -1835,6 +2149,8 @@ class's subject, not its provenance.
1835
2149
  | P-10 | **A sixth pass, and the first to move a band on remediation rather than on capability.** Eval #12 raises the fair band to **€1 400–1 800** (from €1 200–1 500) for one stated reason: *"ahora con la evidencia de que los fallos que reportes se corrigen en días"* — 5 of 9 complaints closed in hours, verified against the source from outside. Floor **€400/seat/year** and ceiling **€2 500** are unchanged, and so is the ceiling's condition: performance resolved **and** `posture`/`risk` promoted out of `experimental`. *"Hoy no lo pagaría."* The new datum is that this pass argues **back toward the seat** — *"si el modelo fuese por repo/CI, hoy no pagaría por el pipeline: a 35–45 min por comando no hay pipeline. Pagaría por asiento de auditor"* — which is the first time the per-repository axis (P-5 → P-9) has been talked out of, and it was talked out of by C3-42, not by the model. The free gate (500 Java files against 3 342) is judged correctly placed | 4.6.0 (eval #12) | Pricing decision. **Remediation speed is now a priced property** — the ledger's original thesis, arriving from outside — and the performance row is now what stands between the fair band and the ceiling, **and** between the seat model and the pipeline model that prices higher |
1836
2150
  | P-11 | **A seventh pass at the price, and the first where a band is set by reliability rather than by capability.** Eval #13 prices **€9/dev/month** (*"lo que yo pagaría hoy por 4.10.3 en un repo de este tamaño: 16 de 43 comandos inalcanzables, incluidos los 4 Start here"*), **€22 recommended** (*"refleja lo que hoy funciona de verdad: el fast path"* — ~€2 600/year for a team of 10, *"se amortiza con un solo `impact-chain` que evite un despliegue roto"*), and a **€45 maximum conditional on C3-53/C3-54/C3-55/C3-57 being closed and `spring-audit`/`posture` running under 2 minutes with checkpointing** — above which *"compites de frente con Semgrep+CodeScene juntos y pierdes en detección"*. Then, for the fifth time, it argues off the seat axis: **per repository analysed, €150–300/repo/year, or per CI execution** — *"la herramienta es un servicio de análisis, no un IDE; cobrar por asiento castiga justo al equipo grande con el repo grande, que es donde aporta más"*. Its recommendation is explicit: *"cómpralo por el fast path, a precio de fast path, y no montes ningún gate de CI sobre `spring-audit`, `posture` ni `pr-impact` en Windows"* | 4.10.3 (eval #13) | — | **open** — pricing, not engineering, but it carries the sharpest engineering signal in the ledger: this is the first evaluation whose **floor** is set by what will not run rather than by what is missing, and its market references are named (DeepSource ~€8, Codacy ~€15–21, CodeScene ~€25–40, Snyk Code ~€25–57, Semgrep Code ~€40, NDepend ~€450 perpetual/seat). Read against P-9/P-10: the fair band did not move, the **floor collapsed** — €400/seat/year became €9/dev/month — and the whole difference is C3-53's class. Fifth independent arrival at the per-repository unit (P-5 → P-6 → P-8 → P-9 → here) |
1837
2151
  | P-12 | **An eighth and a ninth pass at the price, four days apart, on two consecutive builds — and the first pair to agree on both the floor and the condition attached to the ceiling.** Eval #14 (4.10.4, 7/10) prices **€20/seat/month** minimum (*"solo por `endpoints` + `endpoints --servlets` + `validation`; tres comandos, ~20 s, dan un inventario de superficie que no tiene sustituto barato"*), **€45 fair today** (*"el fast path completo… y valoro también la honestidad epistémica: puedo citar sus cifras en un informe sin cubrirme las espaldas, y eso tiene valor material"*), **€90 maximum** — explicitly conditional: *"solo con los repo-wide operables y BUG-1 corregido… hoy no lo pagaría"*. Eval #15 (4.10.5, 6,5/10) lands on the same shape from a different repo state: **€15–20** floor, **€40–50** *"con B3 y B4 resueltos"*, **€90–110** *"además, B5 arreglado y `pr-impact` fiable como gate bloqueante"*, with a ceiling argument that is new: above ~€110 *"compite con SAST enterprise que tiene 10 años más de madurez operativa"*. Both add a **CI licence on a separate axis: €150–300 per repo per month** for nightly runs — eval #14 conditions it precisely: *"depende de que la escritura de `-o` deje de perderse"*. Eval #14 also rules out one model outright: *"lo que no pagaría en ningún caso: precio por LOC o por endpoint analizado. En un repo de 3 574 endpoints donde la mitad del catálogo no ejecuta, un modelo por volumen cobra por capacidad que no se entrega"* | 4.10.4 (eval #14), 4.10.5 (eval #15) | — | **open** — pricing, not engineering, but read against P-11 it is the signal that matters: the floor **recovered from €9 to €15–20** in one release, and the whole difference is remediation of the execution rows, not new capability. Both ceilings are gated by the same two things: C1-36 (the gate must stop lying green) and C3-53/C3-54 (repo-wide runs must survive and keep their output). Sixth and seventh independent arrival at a per-repository unit |
2152
+ | P-13 | **The licence axis cannot charge the buyer the product route is built for.** `is_large_repo()` counts Java source files **per repository path** against `_FREE_REPO_JAVA_FILE_LIMIT = 500` (`license.py:121`, `:521`); the only other axis is the `delta` automation quota (`:267`). An organisation of 40 microservices at ~300 Java files each crosses no threshold on any repository and pays nothing, while `PRODUCT-ROUTE.md` and the third strategy memo both name that organisation as the buyer. The gate in `license.py` and the route contradict each other, and the contradiction favours not charging | third strategy memo, 2026-08-23 — found in the reproduction, **not reported by any of the three memos** | **High (product)** — it is not a pricing preference: the product's own gate excludes the segment its route targets, so `fleet` (`S-18`) has no axis to be sold on and the per-developer price is the ceiling by construction | **open** — `S-27`. Remedy: an axis that can charge an organisation (repository count, a fleet manifest, or seats over a workspace) declared in `license.entitlement()` with `when_it_changes` stating what it costs, exactly as `P-2` established for the existing axis. **Not** by lowering the per-repository threshold — that would start charging the users the current rule deliberately leaves free, and `is_large_repo()`'s docstring says so. Acceptance: a fixture workspace of N small repositories reaches the same entitlement decision as one repository of the same total size, or the difference ships as a stated product decision rather than as an artefact of the counter |
2153
+ | P-14 | **The remediation record is the strongest sales asset in the product and cannot be published as it stands.** `docs/DEFECT-LEDGER.md` carries absolute maintainer paths (`/Users/user/Documents/workspace/testing`), third-party repositories named as evaluation subjects, and self-criticism at a density a buyer can read as instability rather than as rigour. It ships inside the wheel and nowhere else | third strategy memo, 2026-08-23 — the memo proposed publishing it and did not evaluate the publication risk | **Medium (procurement)** — publishing the raw file leaks local paths and implies those third-party projects were audited by this product; not publishing it leaves the answer to *"what if the maintainer disappears"* as a promise instead of a number | **open** — `S-29`. Remedy: publish a **generated projection**, never the file — rows open and closed per version, median days to closure, declined rows with their reason — derived from the ledger plus `build_commit` so the page cannot drift from what shipped. Redaction is part of the acceptance criterion and not a follow-up. The assertion goes on the generator, not on its output, which is the same rule the doc batteries already apply |
1838
2154
 
1839
2155
  ## Claims to correct (not defects, but they cost trust)
1840
2156
 
@@ -46,7 +46,7 @@ CLI commands — impact, endpoints, spring-audit, explain, … each a pro
46
46
  The key idea: the extraction is **content-addressed**. Commands reuse the parse cache and,
47
47
  where their analysed scope matches, the shared Canonical IR; `ask cache model` names what a
48
48
  warm buys for each command rather than implying that every projection costs the same. In
49
- 5.8.24, `validation` enters through that shared CIR and `data-exposure` reuses one semantic
49
+ 5.8.25, `validation` enters through that shared CIR and `data-exposure` reuses one semantic
50
50
  model across all declared label seeds. (The extraction and consumption contract is fixed in
51
51
  the architecture ADRs 0001–0004 under `docs/architecture/`.)
52
52
 
@@ -178,7 +178,7 @@ pipx install sourcecode # isolated install, no venv needed
178
178
 
179
179
  # Verify
180
180
  ask version
181
- # ask 5.8.24
181
+ # ask 5.8.25
182
182
  ```
183
183
 
184
184
  Requires Python 3.9+.
@@ -1592,11 +1592,20 @@ inventories `rules_run` / `rules_not_run` with `rules_run_count` and
1592
1592
  stops at 1 408 files inside the `SEC-004` group and ends at 5.1s.
1593
1593
 
1594
1594
  **Memory budget.** Repository-scoped JSON answers include `_meta.memory` with
1595
- `unit: "MB"`, the observed `peak_rss_mb`, and `configured_max_rss_mb`. To bound
1596
- long analyses on a CI or supervised runner, set `ASK_MAX_RSS_MB=<megabytes>`.
1597
- Exceeding the ceiling produces exit code `1` and a structured
1598
- `MEMORY_TOO_LARGE` error with the peak and configured limit; no partial answer is
1599
- published. Unset the variable, or set it to `0`, to disable this ceiling.
1595
+ `unit: "MB"`, the observed `peak_rss_mb`, the `peak_rss_basis` it was sampled
1596
+ from, `configured_max_rss_mb`, and `ceiling_enforceable`. To bound long analyses
1597
+ on a CI or supervised runner, set `ASK_MAX_RSS_MB=<megabytes>`. Exceeding the
1598
+ ceiling produces exit code `1` and a structured `MEMORY_TOO_LARGE` error with the
1599
+ peak and configured limit; no partial answer is published. Unset the variable, or
1600
+ set it to `0`, to disable this ceiling.
1601
+
1602
+ Where the platform exposes no footprint sample, `peak_rss_mb` is `null` and
1603
+ `peak_rss_basis` says why — never `0`, which would read as a measured floor. A
1604
+ ceiling set on such a platform is **refused, not passed**:
1605
+ `ceiling_enforceable: false` and the run ends in a structured
1606
+ `MEMORY_CEILING_UNAVAILABLE` rather than reporting itself under a limit nothing
1607
+ measured. Installing `psutil` gives the sample where the standard library has
1608
+ none.
1600
1609
 
1601
1610
  Every count in such an answer is a floor over what ran — a family or phase that
1602
1611
  never ran is named, never reported as an absence of findings. A command outside