sourcecode 5.9.24__py3-none-any.whl → 5.9.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.

Potentially problematic release.


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

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.9.24"
7
+ __version__ = "5.9.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 = 'f13c91f463a7cd885eacc9befd93c8fe4009c29e'
4
- CLOSURE_PROVENANCE = {'AUD-611-B05': {'commit': '', 'status': 'uncited'}, 'AUD-611-B06': {'commit': '', 'status': 'uncited'}, 'AUD-611-B07': {'commit': '', 'status': 'uncited'}, 'AUD-611-B08': {'commit': '', 'status': 'uncited'}, 'AUD-611-B09': {'commit': '', 'status': 'uncited'}, 'AUD-611-B10': {'commit': '', 'status': 'uncited'}, 'AUD-611-B11': {'commit': '', 'status': 'uncited'}, 'AUD-612-07': {'commit': '', 'status': 'uncited'}, 'AUD-612-09': {'commit': '', 'status': 'uncited'}, 'AUD-612-14': {'commit': '', 'status': 'uncited'}, 'AUD-612-B03': {'commit': '', 'status': 'uncited'}, 'AUD-612-B07': {'commit': '', 'status': 'uncited'}, 'AUD-612-B13': {'commit': '', 'status': 'uncited'}, 'AUD-612-E': {'commit': '', 'status': 'uncited'}, 'AUD-612-G': {'commit': '', 'status': 'uncited'}, 'AUD-612-I': {'commit': '', 'status': 'uncited'}, 'AUD-612-K': {'commit': '', 'status': 'uncited'}, 'AUD-612-M': {'commit': '', 'status': 'uncited'}, 'AUD-612-R': {'commit': '', 'status': 'uncited'}, 'AUD-613-ROOT': {'commit': '', 'status': 'uncited'}, 'AUD-614-07': {'commit': '', 'status': 'uncited'}, 'AUD-614-E': {'commit': '', 'status': 'uncited'}, 'AUD-614-F': {'commit': '', 'status': 'uncited'}, 'AUD-614-G': {'commit': '', 'status': 'uncited'}, 'AUD-614-I': {'commit': '', 'status': 'uncited'}, 'AUD-614-INTENT-TIE': {'commit': '', 'status': 'uncited'}, 'AUD-614-JUNIT4': {'commit': '', 'status': 'uncited'}, 'AUD-614-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-614-SELFTEST-TIMEOUT': {'commit': '', 'status': 'uncited'}, 'AUD-615-07': {'commit': '', 'status': 'uncited'}, 'AUD-615-COLD-COMPACT': {'commit': '', 'status': 'uncited'}, 'AUD-615-DRIFT': {'commit': '', 'status': 'uncited'}, 'AUD-615-E': {'commit': '', 'status': 'uncited'}, 'AUD-615-F': {'commit': '', 'status': 'uncited'}, 'AUD-615-INTENT-TIE': {'commit': '', 'status': 'uncited'}, 'AUD-615-JUNIT4': {'commit': '', 'status': 'uncited'}, 'AUD-615-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-615-SELFTEST-TIMEOUT': {'commit': '', 'status': 'uncited'}, 'AUD-616-E-PROV': {'commit': '', 'status': 'uncited'}, 'AUD-616-F, -RISK-L0, -SELFTEST-TIMEOUT, -INTENT-TIE, -DRIFT, -COLD-COMPACT, -E, -07': {'commit': '', 'status': 'uncited'}, 'AUD-616-INTENT-TIE-COV': {'commit': '', 'status': 'uncited'}, 'AUD-616-JUNIT4-SELF': {'commit': '', 'status': 'uncited'}, 'AUD-616-KOTLIN': {'commit': '', 'status': 'uncited'}, 'AUD-616-O': {'commit': '', 'status': 'uncited'}, 'AUD-616-P': {'commit': '', 'status': 'uncited'}, 'Window': {'commit': '', 'status': 'uncited'}, '`ASK-FIELD-01`': {'commit': '829f9fa', 'status': 'present'}, '`ASK-FIELD-02`': {'commit': '6a659fa', 'status': 'present'}, '`ASK-FIELD-03`': {'commit': '55b0d51', 'status': 'present'}, '`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'}, '`AUD-619-WATCH`': {'commit': '', 'status': 'uncited'}, '`AUD-620-COMMAND-SURFACE`': {'commit': '', 'status': 'uncited'}, '`AUD-620-REVIEW-PR`': {'commit': '', 'status': 'uncited'}, '`AUD-621-REVIEW-PR`': {'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'}, '`REG-04`': {'commit': '', 'status': 'uncited'}}
5
- CLOSURE_COVERAGE = {'closed_rows': 166, 'present': 91, 'absent': 0, 'unresolved': 0, 'uncited': 75, 'unknown': 0}
3
+ BUILD_COMMIT = '2f283c4d305e2709dc154a4399650c2042b28d68'
4
+ CLOSURE_PROVENANCE = {'AUD-611-B05': {'commit': '', 'status': 'uncited'}, 'AUD-611-B06': {'commit': '', 'status': 'uncited'}, 'AUD-611-B07': {'commit': '', 'status': 'uncited'}, 'AUD-611-B08': {'commit': '', 'status': 'uncited'}, 'AUD-611-B09': {'commit': '', 'status': 'uncited'}, 'AUD-611-B10': {'commit': '', 'status': 'uncited'}, 'AUD-611-B11': {'commit': '', 'status': 'uncited'}, 'AUD-612-07': {'commit': '', 'status': 'uncited'}, 'AUD-612-09': {'commit': '', 'status': 'uncited'}, 'AUD-612-14': {'commit': '', 'status': 'uncited'}, 'AUD-612-B03': {'commit': '', 'status': 'uncited'}, 'AUD-612-B07': {'commit': '', 'status': 'uncited'}, 'AUD-612-B13': {'commit': '', 'status': 'uncited'}, 'AUD-612-E': {'commit': '', 'status': 'uncited'}, 'AUD-612-G': {'commit': '', 'status': 'uncited'}, 'AUD-612-I': {'commit': '', 'status': 'uncited'}, 'AUD-612-K': {'commit': '', 'status': 'uncited'}, 'AUD-612-M': {'commit': '', 'status': 'uncited'}, 'AUD-612-R': {'commit': '', 'status': 'uncited'}, 'AUD-613-ROOT': {'commit': '', 'status': 'uncited'}, 'AUD-614-07': {'commit': '', 'status': 'uncited'}, 'AUD-614-E': {'commit': '', 'status': 'uncited'}, 'AUD-614-F': {'commit': '', 'status': 'uncited'}, 'AUD-614-G': {'commit': '', 'status': 'uncited'}, 'AUD-614-I': {'commit': '', 'status': 'uncited'}, 'AUD-614-INTENT-TIE': {'commit': '', 'status': 'uncited'}, 'AUD-614-JUNIT4': {'commit': '', 'status': 'uncited'}, 'AUD-614-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-614-SELFTEST-TIMEOUT': {'commit': '', 'status': 'uncited'}, 'AUD-615-07': {'commit': '', 'status': 'uncited'}, 'AUD-615-COLD-COMPACT': {'commit': '', 'status': 'uncited'}, 'AUD-615-DRIFT': {'commit': '', 'status': 'uncited'}, 'AUD-615-E': {'commit': '', 'status': 'uncited'}, 'AUD-615-F': {'commit': '', 'status': 'uncited'}, 'AUD-615-INTENT-TIE': {'commit': '', 'status': 'uncited'}, 'AUD-615-JUNIT4': {'commit': '', 'status': 'uncited'}, 'AUD-615-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-615-SELFTEST-TIMEOUT': {'commit': '', 'status': 'uncited'}, 'AUD-616-E-PROV': {'commit': '', 'status': 'uncited'}, 'AUD-616-F, -RISK-L0, -SELFTEST-TIMEOUT, -INTENT-TIE, -DRIFT, -COLD-COMPACT, -E, -07': {'commit': '', 'status': 'uncited'}, 'AUD-616-INTENT-TIE-COV': {'commit': '', 'status': 'uncited'}, 'AUD-616-JUNIT4-SELF': {'commit': '', 'status': 'uncited'}, 'AUD-616-KOTLIN': {'commit': '', 'status': 'uncited'}, 'AUD-616-O': {'commit': '', 'status': 'uncited'}, 'AUD-616-P': {'commit': '', 'status': 'uncited'}, 'Window': {'commit': '', 'status': 'uncited'}, '`ASK-FIELD-01`': {'commit': '829f9fa', 'status': 'present'}, '`ASK-FIELD-02`': {'commit': '6a659fa', 'status': 'present'}, '`ASK-FIELD-03`': {'commit': '55b0d51', 'status': 'present'}, '`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'}, '`AUD-619-WATCH`': {'commit': '', 'status': 'uncited'}, '`AUD-620-COMMAND-SURFACE`': {'commit': '', 'status': 'uncited'}, '`AUD-620-REVIEW-PR`': {'commit': '', 'status': 'uncited'}, '`AUD-621-REVIEW-PR`': {'commit': '', 'status': 'uncited'}, '`B-3`': {'commit': '773268d', 'status': 'present'}, '`B-6`': {'commit': '17c6e06', 'status': 'present'}, '`B-8`': {'commit': '8329614', 'status': 'present'}, '`BUG-15`': {'commit': '', 'status': 'uncited'}, '`BUG-21`': {'commit': '', 'status': 'uncited'}, '`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'}, '`REG-01`': {'commit': '', 'status': 'uncited'}, '`REG-04`': {'commit': '', 'status': 'uncited'}}
5
+ CLOSURE_COVERAGE = {'closed_rows': 169, 'present': 91, 'absent': 0, 'unresolved': 0, 'uncited': 78, 'unknown': 0}
@@ -14,44 +14,82 @@ Status values: `open` · `in-progress` · `closed <version>` · `by-design` (wit
14
14
  The facts these rows are keyed to are published: `ask schema facts-v1` prints the registry
15
15
  (authority, definition, consumers, parity test) that the release battery enforces.
16
16
 
17
- ## Current Synchronization — External Audit `5.9.23`, carried into `5.9.24` (2026-09-01)
18
-
19
- **Release gate: do not use 5.9.24 as a CI decision gate for these surfaces.** The audit measured
20
- three `rc=0`, zero-byte answers across `risk` and Markdown `audit-report`.
21
- The trigger is not yet established; warm-cache and contention hypotheses were
22
- explicitly tested and did not reproduce it. A global non-empty-output invariant
23
- is therefore the P0 containment work, not a claim that the root cause is known.
24
-
25
- Priority order is: `NEW-01` empty successful output; `REG-02` default agent
26
- memory rejection and post-hoc RSS enforcement; `BUG-01` undeclared parse loss;
27
- `REG-03` warm-path slowdown; `NEW-02` overloaded endpoint anchors; then
28
- workspace-help/selftest/plan and output-contract regressions. Full evidence:
29
- [`AUDIT-2026-09-01-EXTERNAL-5.9.23.md`](AUDIT-2026-09-01-EXTERNAL-5.9.23.md).
17
+ ## Current Synchronization — Alternative External Audit `5.9.25`, and External Audit `5.9.25` (2026-09-02)
18
+
19
+ **Release `5.9.26` closes eight rows from this round with verified evidence**
20
+ (`ALT-09`, `ALT-11`, `BUG-01`, `BUG-03`, `REG-02`'s disclosure half, `ALT-06`'s
21
+ itemization half, plus the `remediation-report` path-admission coverage gap
22
+ and the `ALT-01`/`ALT-03` forensic sidecar) — see each row below for the
23
+ commit-level detail, and `CHANGELOG.md` for the release note. It does **not**
24
+ close the release gate: `ALT-01`/`ALT-03` were investigated and instrumented,
25
+ not fixed, because the failure did not reproduce outside native Windows.
26
+
27
+ **Release gate unchanged and now more urgent: do not use 5.9.25 or 5.9.26 to
28
+ gate unattended CI through the audit-command family.** Two independent re-audits of
29
+ 5.9.25 agree the empty-`exit 0` defect did not shrink. The multi-repository
30
+ audit measured it at 18.4% (9/49, byte-counted) and, for the first time, in
31
+ `spring-audit` itself — the command the tool's own help recommends first. The
32
+ native-Windows audit found the same failure has gone from a hang to something
33
+ **erratic**: the identical `migrate-apply .` invocation gave 8/8 empty answers
34
+ in one batch and 3/3 full answers minutes later with nothing else changed, and
35
+ the failure spread to `migrate-check` and `onboard`, both reliable in 5.9.23.
36
+ `ALT-01` is **reopened**: the commit cited as closing it does not hold under
37
+ native re-test — `risk . -o FILE` still returns `rc=0`, zero bytes, and no
38
+ file in 4/4 runs.
39
+
40
+ Three rows are genuinely closed with verified evidence this round: `ALT-04`
41
+ (content-id clock exclusion, 3/3 stable hashes), `ALT-05` (findings-population
42
+ reconciliation, exact arithmetic), and `REG-01` (root `ask --help` no longer
43
+ hangs — sub-linear cost curve up to 4,772 files; the original 31-repo extreme
44
+ case was not re-confirmed). `BUG-15` (Rich markup leak) and `BUG-21`
45
+ (`--jobs` range validation) also close cleanly.
46
+
47
+ Priority order is: `ALT-01` reopened silent success and unkept output
48
+ promises; `ALT-03` security/audit-phase bisection, now with an erratic rather
49
+ than hang-only signature; `ALT-09` (new) `cache clear` ignoring a piped `n`
50
+ answer; `REG-02` agent memory ceiling; `BUG-01` silent malformed-file
51
+ disappearance; then `ALT-07` remediate hang, `ALT-10`/`ALT-11` new latency and
52
+ ergonomics findings, and remaining CLI ergonomics. Full evidence:
53
+ [`AUDIT-2026-09-02-ALTERNATIVE-5.9.25.md`](AUDIT-2026-09-02-ALTERNATIVE-5.9.25.md)
54
+ and [`AUDIT-2026-09-02-EXTERNAL-5.9.25.md`](AUDIT-2026-09-02-EXTERNAL-5.9.25.md).
30
55
 
31
56
  | ID | Severity | Status | Evidence and required closure |
32
57
  |---|---:|---|---|
33
- | `NEW-01` | Critical maximum | **open, P0 / CI blocker** | `risk` and Markdown `audit-report` can exit `0` with zero bytes on stdout and stderr. The measured rate was 3/47 byte-counted runs; root cause is unknown. Enforce `EMPTY_ANSWER` globally and add byte-counted background-process coverage. |
34
- | `REG-02` | Critical | **open, regression** | The 1,536 MB default `--agent` ceiling rejects valid large repositories only after spending minutes; it is not published in successful default-agent metadata. Bound projection during construction, sample RSS periodically, and publish the effective ceiling. |
35
- | `REG-03` | High | **open, regression** | Warm `spring-audit` was measured 2.1–3.8x slower despite a fully reused CIR. Attribute the warm pass before claiming cache benefit. |
36
- | `REG-01` | High | **open, regression** | Root `ask --help` took 83–86 seconds in a workspace with nested repositories. Help must not perform unbounded repository/profile analysis. |
37
- | `REG-04` | Medium | **closed 5.9.24** | `_meta.answer_bounds.capped` now aggregates nested cap contracts, so compact answers disclose rows omitted below nested payloads. Regression coverage includes nested dictionaries and lists. |
38
- | `NEW-02` | High | **open** | Overloaded endpoint handlers can all inherit the final overload's `declaration_line`. Resolve by mapping annotation/signature or publish ambiguity. |
39
- | `BUG-01` | Critical maximum | **open, P0** | A malformed Java file can disappear without a file-specific declaration. Publish parse failure/recovery in the answer bounds and preserve any affected endpoint gap. |
58
+ | `ALT-01` | Critical maximum | **reopened, contradicted by native re-test** | `risk . -o FILE` returned `rc=0`, 0 bytes, no file in 4/4 native runs. Source review of the guard (`_fail_on_missing_required_answer`, `_fail_on_unkept_output_promise`, armed for `risk`/`spring-audit`/`verify`/`audit-report`/`migrate-check`/`migrate-apply` via `REQUIRED_ANSWER_COMMANDS`) finds it correctly wired end to end on this platform; the field failure is not reproducible outside native Windows. Both guards now write a forensic sidecar (`empty_answer_diagnostics.record`, referenced as `diagnostics_path` in the error envelope) capturing phase/RSS/argv/pid at the exact moment either fires, so the next occurrence carries evidence instead of a report written from memory. This does not close the row — it makes the next native reproduction diagnosable. |
59
+ | `ALT-02` | Critical maximum | **fixed, bounded-phase follow-up** | `aab1e8b` propagates deadline checks into security posture and publishes omitted stages and partial coverage. A small-repository budget can still finish its coarse earlier phases before the deadline is observed; `ALT-03` owns reducing that phase granularity. |
60
+ | `ALT-03` | High | **open, investigation, sharper signature** | Failures are now mostly near-instant (1-2s) empty success rather than multi-minute hangs on the same commands — consistent with a phase being *skipped*, not *stuck*. The audit could not isolate a trigger below three concurrent heavy ASK processes. Bisect the known working small subtree against the 3,342-file root; do not assert causality before reproducing it. The `ALT-01` diagnostic sidecar now records the last known phase and peak RSS at the moment of every occurrence, closing the evidence gap the audits both named. |
61
+ | `ALT-04` | High | **fixed, confirmed** | `36e3893` excludes every nested `*_time_ms` execution clock from `content_id`. Re-confirmed: `impact-chain` gave the same hash across 3 repeated native runs (was 3 different hashes in 5.9.23). |
62
+ | `ALT-05` | High | **fixed, confirmed** | `b12c8f1` publishes `population_total` and `excluded_before_cap` beside the compact findings cap. Re-confirmed with exact arithmetic: `1526 - 593 = 933 = by_rule.MIG-016`. |
63
+ | `ALT-06` | High | **fixed** | `complete` is correctly `false` when rows fail. The itemization half is also closed: `answer_bounds._summarise` picked a dict's `id` before its `reason`/`what`, so `selftest`'s per-category `limitations` entries rendered as the bare slug (`selftest_rows_failed`) with the actual failing row IDs dropped even though `reason` and the new `rows` field named them. `_summarise` now prefers the descriptive text; no other producer in this codebase pairs `id` with `what`/`reason` on the same dict, so nothing else changes. |
64
+ | `ALT-07` | Medium | **open** | `remediate` regressed from a 23.6-second answer to hangs beyond 400 seconds in `auditing for repairable findings`; it shares the `ALT-03` investigation until separated by evidence. |
65
+ | `ALT-08` | Medium | **fixed, confirmed** | `15d77d2` publishes the effective per-task stdout budget and the full-artifact path; confirmed functioning, with a minor arithmetic mismatch in `prepare-context review-pr`'s `_truncation_summary` noted as low-severity follow-up. |
66
+ | `ALT-09` | Critical | **open, new** | `ask cache clear .` deletes cache entries even when piped stdin answers `n`; only true EOF (`< /dev/null`) correctly refuses. The non-interactivity detector appears to treat "stdin has bytes" as "proceed" instead of reading and comparing them against `y`/`yes`. Low blast radius today (cache is rebuildable); fix the detection to use `isatty()` plus an actual answer comparison before any future destructive command reuses the same pattern. |
67
+ | `ALT-10` | Medium | **open, new** | `onboard .` took 164.9s cold in one native session versus a consistent 681-780ms in every prior round on the same repository (~240x). Delivered a full answer, so not release-blocking alone; profile for a shared warm-up path with the `ALT-01`/`ALT-03` commands. |
68
+ | `ALT-11` | Low | **fixed** | `watch` now emits a `{"tick": 0, "status": "watching", "elapsed_ms": …}` NDJSON line on stdout immediately after the index warms and before the tick loop begins — the channel `watch` already promises a consumer is reading, unlike stderr, whose silence on a non-TTY stream is a deliberate, tested invariant elsewhere in the CLI. Written to `--output` too. |
69
+ | `NEW-01` | Critical maximum | **escalated** | Now measured at 18.4% (9/49, byte-counted) and reaches `spring-audit --compact` in addition to `risk` and Markdown `audit-report`. Two identical back-to-back `spring-audit thingsboard --compact` runs (46,596 bytes/69.7s, then 0 bytes/57.2s) refute the earlier "only on abnormally fast runs" hypothesis. `BUG-17` (`remediation-report` silently empty on an unexpected positional, rather than `INVALID_USAGE`) does not reproduce as described here: the command returns its normal, non-empty bundled-ledger report regardless of the path given, by design (`del path` — "the bundled ledger is used"), so it is not the same failure class as `NEW-01`. What is real and now fixed is narrower: `path_admission`'s own coverage battery had a gap for exactly this command — `remediation-report`'s `path` parameter matched the battery's repo-path discovery heuristic with no invocation row, a pre-existing red independent of this audit. Closed by documenting the exclusion inline (`PATH_NOT_READ_BY_DESIGN` in `tests/test_path_admission.py`), the same "opt-out is a decision, not an omission" convention `cli.py` already uses for `--copy`/`Progress`. |
70
+ | `REG-02` | Critical | **partially fixed: disclosure half closed, enforcement-timing half open** | Reproduced and fixed the specific gap named in both audits: a successful `--agent` run published `_meta.memory.configured_max_rss_mb: null` even though `_root_memory_seam` was actively enforcing the 1,536 MB default at every phase boundary for that same run. `disclosure()` now takes the same `default_ceiling_mb` `exceeded()` already did, and the root command threads whether `--agent` is active through a per-invocation flag (`_AGENT_MODE_ACTIVE`) so a successful run says what protected it. The still-open half — the ceiling rejecting large repositories only after minutes of work, and a higher observed peak than 5.9.23 (2,029.92 MB vs 1,552.45 MB) — needs periodic RSS sampling with early abort inside the expensive phases themselves (`BUG-22`'s row), which cannot be validated without the field's own large repository. |
71
+ | `REG-03` | High | **open, regression, unchanged** | Warm `spring-audit` still shows no reliable time or reliability benefit over cold; the `NEW-01` back-to-back pair is itself the sharpest evidence, since the "warm" run was proportionally slower and the one that came back empty. |
72
+ | `REG-01` | High | **closed, resolved on measured range** | Root `ask --help` no longer hangs in a nested-repository workspace: cost now scales sub-linearly (49 files: 608ms; 4,772 files: 13.6s), against 82-86s trending to 120s+/`rc=124` in 5.9.23. The original 31-repo/8,322-file extreme case was not re-confirmed — the audit's own 110s harness cutoff interrupted it. Re-run that specific extreme case before treating the ceiling as proven. |
73
+ | `REG-04` | Medium | **closed 5.9.24, reconfirmed** | `_meta.answer_bounds.capped` aggregation re-verified in `migrate-check --compact`: all 5 nested cap contracts listed, 268 omitted values disclosed. |
74
+ | `NEW-02` | High | **open, unchanged** | Overloaded endpoint handlers can all inherit the final overload's `declaration_line`; the `anchor_ambiguity`/`overload_count` fields expected from this row are still absent from the payload. Resolve by mapping annotation/signature or publish ambiguity. |
75
+ | `BUG-01` | Critical maximum | **fixed, mechanism narrowed, not eliminated** | Reproduced directly: a missing `;` or a missing `)` on a method signature both extract a clean class+method symbol pair with balanced braces, so the E-10 brace-only check (`unbalanced_braces`) never fired and the file passed as clean. `repository_ir.py` now also masks and checks paren balance (`unbalanced_parens`), publishing the same `parse_structure` gap for that shape. This is a heuristic extractor without a real Java grammar, so a syntax error that disturbs neither braces nor parens (for example a missing operand with balanced punctuation on both sides) remains undetectable by construction — the row stays open at lower severity for that residual class, not closed. |
40
76
  | `BUG-02` | Critical maximum | **open, P0** | Large `risk` ended `rc=127` with no output or structured failure. Add fail-fast resource/process handling and durable partial checkpoints. |
41
77
  | `BUG-12` | Critical maximum | **open, P0** | Deep `--agent` output construction reaches multi-gigabyte RSS before a small answer. Bound projection while building. |
42
- | `BUG-03` | High | **open, P1** | Marginal Spring detection can overstate what a zero-finding result covers. Publish Spring footprint/coverage with the stack-fit claim. |
78
+ | `BUG-03` | High | **fixed** | `non_coverage.stack_fit`'s marginal-detection disclosure (`fit: "partial"`, below 10% Spring-bean density) has existed since AUD-596-D13 but was unreachable from `risk` and `posture`: neither call site passed `spring_footprint`, so `java_files` stayed 0 and the branch that reads it could never fire regardless of the real ratio. Both now compute it the same way `spring-audit`'s own call already did (`sum(bean.spring for bean in graph.beans.values())` over `len(cir.files)`); verified end to end on a 1-bean/16-file fixture. |
43
79
  | `BUG-04` | High | **open, P1** | `selftest` publishes `complete=true` with five error rows. Derive bounds from actual results. |
44
80
  | `BUG-05` | High | **open, P1** | Overloaded endpoint handlers can receive the wrong declaration line. Resolve by mapping annotation or publish ambiguity. |
45
81
  | `BUG-07` | Closed 5.9.23, verification gap | Compact JVM inventory is bounded, but the audit notes no selftest row protects the correction. Add release-battery coverage before treating it as durable closure. |
46
- | `BUG-10` | High | **open, P1** | Unsupported routing styles can produce empty coverage gaps. Publish detected-but-unmodeled routing gaps. |
47
- | `BUG-11` | High | **open, P1** | Default `pack assessment` can fail on a small Spring repository. Use a bounded default or explicit full mode. |
48
- | `BUG-13` | High | **open, P1** | Unscoped `selftest` starts over nested repositories after warning. Refuse and require an explicit path. |
49
- | `BUG-16` | High | **open, P1** | Context-cache growth has no published budget or eviction contract; about 1.5 GB was measured. |
82
+ | `BUG-10` | High | **open, P1, unchanged** | Unsupported routing styles can produce empty coverage gaps. Publish detected-but-unmodeled routing gaps. |
83
+ | `BUG-11` | High | **open, P1, now hangs instead of failing fast** | Default `pack assessment` and `pack gate --since` now hang to their external kill (300s/200s) instead of the legitimate `INVALID_INPUT`/`INVALID_USAGE` they gave in 5.9.23 — both compose the audit-family commands affected by `ALT-01`/`ALT-03` and inherit the failure. |
84
+ | `BUG-13` | High | **open, P1, unchanged** | Unscoped `selftest` starts over nested repositories after warning. Refuse and require an explicit path. |
85
+ | `BUG-15` | Low | **closed 5.9.25** | Non-TTY `ask --help` no longer emits Rich markup: 0 occurrences of `[bold]`/`[dim]`/`[bold green]`, down from 208 (5.9.22) and 202 (5.9.23). |
86
+ | `BUG-16` | High | **open, P1, unchanged** | Context-cache growth has no published budget or eviction contract; ~1.7 GB was measured (was ~1.6 GB in 5.9.23). |
87
+ | `BUG-21` | Low | **closed 5.9.25** | `--jobs` now rejects out-of-range values with `INVALID_USAGE` stating the `1<=x<=128` bound, replacing silent acceptance. |
50
88
  | `BUG-22` | High | **open, P1** | RSS ceilings are checked post-hoc. Add periodic sampling and orderly abort. |
51
89
 
52
- The remaining `BUG-04b`, `BUG-06`, `BUG-08`, `BUG-09`, `BUG-15`, `BUG-19`,
53
- `BUG-20`, `BUG-21` and `BUG-23` findings are open P2/coverage follow-ups in
54
- the audit document. None is closed by documentation alone.
90
+ The remaining `BUG-04b`, `BUG-06`, `BUG-08`, `BUG-09`, `BUG-19`, `BUG-20` and
91
+ `BUG-23` findings are open P2/coverage follow-ups in the audit document. None
92
+ is closed by documentation alone.
55
93
 
56
94
  ## Current Synchronization — External Audit `AUD-621`
57
95
 
@@ -279,7 +279,7 @@ pipx install sourcecode # isolated install, no venv needed
279
279
 
280
280
  # Verify
281
281
  ask version
282
- # ask 5.9.19
282
+ # ask 5.9.26
283
283
  ```
284
284
 
285
285
  Requires Python 3.9+.
@@ -77,7 +77,16 @@ def _summarise(value: Any) -> Any:
77
77
  payloads would see the same fact move twice.
78
78
  """
79
79
  if isinstance(value, dict):
80
- for key in ("id", "what", "reason", "not_covered", "message", "title"):
80
+ # R-05 (5.9.25 alternative audit): `reason`/`what` are the producer's
81
+ # own descriptive sentence about the gap; `id` is a stable category
82
+ # slug meant for a consumer that filters programmatically, not for a
83
+ # human reading `gaps`. `id` used to be checked first, so a producer
84
+ # that published both (selftest's `limitations`) had its actual
85
+ # itemized detail — which rows, not just how many — silently dropped
86
+ # in favor of the bare slug. No other producer in this codebase pairs
87
+ # `id` with `what`/`reason` on the same dict, so this reorder changes
88
+ # nothing for any of them.
89
+ for key in ("what", "reason", "not_covered", "message", "title", "id"):
81
90
  if key in value and isinstance(value[key], str):
82
91
  return value[key][:200]
83
92
  return {k: v for k, v in list(value.items())[:3] if not isinstance(v, (dict, list))}
@@ -88,6 +97,12 @@ def _summarise(value: Any) -> Any:
88
97
 
89
98
  def _gaps(payload: "dict[str, Any]") -> "list[dict[str, Any]]":
90
99
  found: "list[dict[str, Any]]" = []
100
+ # `prepare-context` and related task answers publish their unanswered
101
+ # analysis as a top-level list, rather than under the route-specific
102
+ # `coverage` block. It is still a declared gap in the answer.
103
+ for gap in _as_list(payload.get("gaps")):
104
+ if gap:
105
+ found.append({"field": "gaps", "what": _summarise(gap)})
91
106
  coverage = payload.get("coverage")
92
107
  if isinstance(coverage, dict):
93
108
  for gap in _as_list(coverage.get("gaps")):
sourcecode/cli.py CHANGED
@@ -1746,6 +1746,44 @@ def _stdin_is_interactive() -> bool:
1746
1746
  return False
1747
1747
 
1748
1748
 
1749
+ def _read_piped_answer(*, timeout: float = 0.2) -> Optional[str]:
1750
+ """Best-effort read of a non-interactive stdin's actual content.
1751
+
1752
+ ALT-09 (5.9.25 alternative audit): the non-interactive branch of a
1753
+ destructive prompt treated *any* redirected stdin as an implicit "yes" —
1754
+ `echo n | ask cache clear .` deleted cache files despite the piped answer
1755
+ being an explicit refusal, and only true immediate EOF (`< /dev/null`)
1756
+ happened to refuse, by accident of `_confirm`'s own EOF handling on a
1757
+ different path. A non-interactive caller can still mean to answer.
1758
+
1759
+ This reads one line on a background thread and gives it `timeout` seconds
1760
+ to arrive. A pipe with data (or a closed pipe with none) resolves almost
1761
+ immediately either way. A stdin that is open but never closed — an
1762
+ inherited terminal fd, an MCP session — resolves neither within the
1763
+ window, and this returns `None` rather than block: the caller falls back
1764
+ to its prior default-confirm behavior in that case, unchanged.
1765
+ """
1766
+ import threading
1767
+
1768
+ result: list = [None]
1769
+
1770
+ def _reader() -> None:
1771
+ try:
1772
+ result[0] = sys.stdin.readline()
1773
+ except Exception:
1774
+ result[0] = None
1775
+
1776
+ reader = threading.Thread(target=_reader, daemon=True)
1777
+ reader.start()
1778
+ reader.join(timeout)
1779
+ if reader.is_alive():
1780
+ return None
1781
+ line = result[0]
1782
+ if not line:
1783
+ return None
1784
+ return line.strip()
1785
+
1786
+
1749
1787
  def _confirm(question: str, *, command: str, default: bool = False, err: bool = False) -> bool:
1750
1788
  """Ask a yes/no question, and diagnose a prompt that cannot be answered.
1751
1789
 
@@ -2160,7 +2198,16 @@ def _stamp_envelope(content: str) -> str:
2160
2198
  try:
2161
2199
  from sourcecode import resource_budget
2162
2200
 
2163
- extra["memory"] = resource_budget.disclosure()
2201
+ # REG-02: `--agent` enforces `DEFAULT_AGENT_RSS_CEILING_MB` at every
2202
+ # phase boundary (`_root_memory_seam`) whether or not `ASK_MAX_RSS_MB`
2203
+ # was set, so a successful run under it must not disclose "no ceiling
2204
+ # configured" — that was the exact gap the field measured.
2205
+ extra["memory"] = resource_budget.disclosure(
2206
+ default_ceiling_mb=(
2207
+ resource_budget.DEFAULT_AGENT_RSS_CEILING_MB
2208
+ if _AGENT_MODE_ACTIVE else None
2209
+ ),
2210
+ )
2164
2211
  except Exception as _exc: # a footprint sample is not the envelope
2165
2212
  degraded = degraded or f"memory disclosure unavailable: {type(_exc).__name__}"
2166
2213
 
@@ -3000,6 +3047,25 @@ _OUTPUT_PROMISED: "Optional[str]" = None
3000
3047
  _OUTPUT_DELIVERED: bool = False
3001
3048
  _ANSWER_REQUIRED_BY: "Optional[str]" = None
3002
3049
 
3050
+ #: REG-02. Whether the running root invocation set `--agent`, so the envelope
3051
+ #: builder can disclose the mode-specific RSS default `_root_memory_seam`
3052
+ #: actually enforces — set at the same point that seam is armed, and reset at
3053
+ #: the top of `main()` like every other per-invocation global here.
3054
+ _AGENT_MODE_ACTIVE: bool = False
3055
+
3056
+ # ALT-01. These commands publish repository-analysis answers. A successful
3057
+ # process with neither stdout nor a verified output artifact is never valid.
3058
+ # Kept as one authority so the entry point can arm the guard before Click enters
3059
+ # a callback; a callback that returns early is the failure this protects.
3060
+ REQUIRED_ANSWER_COMMANDS: "frozenset[str]" = frozenset({
3061
+ "audit-report",
3062
+ "migrate-apply",
3063
+ "migrate-check",
3064
+ "risk",
3065
+ "spring-audit",
3066
+ "verify",
3067
+ })
3068
+
3003
3069
 
3004
3070
  def _record_output_promise(argv: "list[str]") -> None:
3005
3071
  """Note that this invocation asked for a file, before anything is parsed.
@@ -3047,6 +3113,16 @@ def _require_answer_delivery(command: str) -> None:
3047
3113
  _ANSWER_REQUIRED_BY = command
3048
3114
 
3049
3115
 
3116
+ def _require_answer_from_argv(argv: "list[str]") -> None:
3117
+ """Arm the process guard before an affected command callback can return."""
3118
+ if any(token in ("--help", "-h", "--version") for token in argv):
3119
+ return
3120
+ for token in argv:
3121
+ if token in REQUIRED_ANSWER_COMMANDS:
3122
+ _require_answer_delivery(token)
3123
+ return
3124
+
3125
+
3050
3126
  def _analysis_failed(command: str, phase: str, exc: Exception) -> NoReturn:
3051
3127
  """Fail a costly command with evidence instead of an empty successful exit.
3052
3128
 
@@ -3071,6 +3147,9 @@ def _fail_on_missing_required_answer(exit_code: object) -> None:
3071
3147
  """Turn a silent successful composed run into a typed failure (C3-122)."""
3072
3148
  if exit_code not in (0, None) or _OUTPUT_DELIVERED or not _ANSWER_REQUIRED_BY:
3073
3149
  return
3150
+ from sourcecode import empty_answer_diagnostics as _diag
3151
+
3152
+ _diag_path = _diag.record(_ANSWER_REQUIRED_BY, "missing_required_answer")
3074
3153
  _emit_error_json(
3075
3154
  EXECUTION_FAILED_CODE,
3076
3155
  f"{_ANSWER_REQUIRED_BY} finished without publishing an answer.",
@@ -3081,6 +3160,10 @@ def _fail_on_missing_required_answer(exit_code: object) -> None:
3081
3160
  ),
3082
3161
  command=_ANSWER_REQUIRED_BY,
3083
3162
  expected="parseable stdout or the requested output artifact",
3163
+ # ALT-03/NEW-01: two audits measured this outcome without pinning a
3164
+ # mechanism. This sidecar captures phase/RSS/argv at the moment of
3165
+ # failure so the next occurrence carries its own forensic record.
3166
+ diagnostics_path=_diag_path,
3084
3167
  )
3085
3168
  raise SystemExit(1)
3086
3169
 
@@ -3099,6 +3182,11 @@ def _fail_on_unkept_output_promise(exit_code: object) -> None:
3099
3182
  missing = _unkept_output_promise()
3100
3183
  if missing is None:
3101
3184
  return
3185
+ from sourcecode import empty_answer_diagnostics as _diag
3186
+
3187
+ _diag_path = _diag.record(
3188
+ _ANSWER_REQUIRED_BY, "unkept_output_promise", promised_path=str(missing),
3189
+ )
3102
3190
  _emit_error_json(
3103
3191
  EXECUTION_FAILED_CODE,
3104
3192
  f"The run finished without producing '{missing}'.",
@@ -3110,6 +3198,9 @@ def _fail_on_unkept_output_promise(exit_code: object) -> None:
3110
3198
  ),
3111
3199
  expected="The file named by --output.",
3112
3200
  path=str(missing),
3201
+ # ALT-01: the guard closing on paper did not close it in the field.
3202
+ # This sidecar captures phase/RSS/argv at the moment of failure.
3203
+ diagnostics_path=_diag_path,
3113
3204
  )
3114
3205
  raise SystemExit(1)
3115
3206
 
@@ -3134,7 +3225,7 @@ def _unkept_output_promise() -> "Optional[str]":
3134
3225
  the run wrote it by some route this module does not know about, and a
3135
3226
  discard sink was never a promise of a file.
3136
3227
  """
3137
- if _OUTPUT_DELIVERED or not _OUTPUT_PROMISED:
3228
+ if not _OUTPUT_PROMISED:
3138
3229
  return None
3139
3230
  try:
3140
3231
  from sourcecode.output_sink import is_null_device
@@ -3143,7 +3234,9 @@ def _unkept_output_promise() -> "Optional[str]":
3143
3234
  if is_null_device(path) or (path.is_file() and path.stat().st_size > 0):
3144
3235
  return None
3145
3236
  except Exception:
3146
- return None
3237
+ # A destination that cannot be inspected cannot prove delivery. This is
3238
+ # a release gate, so uncertainty fails closed with the promised path.
3239
+ return _OUTPUT_PROMISED
3147
3240
  return _OUTPUT_PROMISED
3148
3241
 
3149
3242
 
@@ -5522,6 +5615,8 @@ def main(
5522
5615
  cache_observation.reset()
5523
5616
  except Exception:
5524
5617
  pass
5618
+ global _AGENT_MODE_ACTIVE
5619
+ _AGENT_MODE_ACTIVE = False
5525
5620
  _apply_jobs(jobs)
5526
5621
  # A root option so the ceiling is one policy rather than sixty flags, and an
5527
5622
  # environment variable so it survives into a subcommand's own parse: Click
@@ -5833,6 +5928,7 @@ def main(
5833
5928
 
5834
5929
  # --agent: enable signal analyzers; output via agent_view (not compact)
5835
5930
  if agent:
5931
+ _AGENT_MODE_ACTIVE = True
5836
5932
  dependencies = True
5837
5933
  env_map = True
5838
5934
  code_notes = True
@@ -8377,6 +8473,11 @@ def prepare_context_cmd(
8377
8473
 
8378
8474
  # P0-1: Apply output budget per task — safety net for large repos.
8379
8475
  _pc_budget = _prepare_context_budget(task)
8476
+ out["output_budget"] = {
8477
+ "stdout_max_bytes": _pc_budget,
8478
+ "applies_to": "stdout only",
8479
+ "full_output": "Use --output <file> to retain the complete artifact.",
8480
+ }
8380
8481
 
8381
8482
  # Size-gated preview: on enterprise-scale monoliths free users get a capped
8382
8483
  # preview (top-5 / lightweight) + upgrade note. Small/mid repos get the full
@@ -12718,6 +12819,7 @@ def spring_audit_cmd(
12718
12819
  _phases_cut: list[str] = []
12719
12820
  # C3-79: families the budget stopped *inside*, with how far each got.
12720
12821
  _rules_partial: list[dict] = []
12822
+ _projection_stages_cut: list[str] = []
12721
12823
  if scope in ("all", "tx"):
12722
12824
  if _run.exhausted():
12723
12825
  _stopped_early = _WHY_BUDGET
@@ -12762,7 +12864,11 @@ def spring_audit_cmd(
12762
12864
  getattr(_sec_watch, "partially_run", []) or []
12763
12865
  )
12764
12866
  _rules_partial.extend(_partial_families)
12765
- if _cut or _partial_families:
12867
+ _stage_cut = list(
12868
+ getattr(_sec_watch, "stages_not_run", []) or []
12869
+ )
12870
+ _projection_stages_cut.extend(_stage_cut)
12871
+ if _cut or _partial_families or _stage_cut:
12766
12872
  _stopped_early = _WHY_BUDGET
12767
12873
  _phases_cut.append("security_audit")
12768
12874
  else:
@@ -12952,6 +13058,7 @@ def spring_audit_cmd(
12952
13058
  rules_partially_run=_rules_partial,
12953
13059
  rules_not_run=_rules_cut,
12954
13060
  phases_stopped_inside=_phases_cut,
13061
+ composition_walks_not_run=_projection_stages_cut,
12955
13062
  )
12956
13063
  # AUD-595-A03: the declaration said what did not run and never
12957
13064
  # what the process would do about it. Same rule as the exits
@@ -14729,6 +14836,7 @@ def migrate_apply_cmd(
14729
14836
  and worktree. The resulting worktree is retained for review; ASK never commits
14730
14837
  or merges the generated changes.
14731
14838
  """
14839
+ _require_answer_delivery("migrate-apply")
14732
14840
  from sourcecode.migrate_check import run_migrate_check
14733
14841
  from sourcecode.openrewrite_recipe import build_plan
14734
14842
  from sourcecode.repository_ir import find_java_files
@@ -17647,6 +17755,7 @@ def watch_cmd(
17647
17755
 
17648
17756
  _prog = Progress()
17649
17757
  _prog.start("warming the index")
17758
+ _warm_t0 = time.monotonic()
17650
17759
  try:
17651
17760
  from sourcecode import context_cache as _ctxcache
17652
17761
 
@@ -17663,6 +17772,24 @@ def watch_cmd(
17663
17772
  try:
17664
17773
  if output_path is not None:
17665
17774
  _sink = open(output_path, "w", encoding="utf-8")
17775
+ # ALT-11 (5.9.25 audit): a non-TTY `watch` gave no output at all until
17776
+ # its first change or its external timeout — indistinguishable from
17777
+ # hung. The `Progress` spinner above stays correctly silent there by
17778
+ # design (a non-interactive command's stderr is a byte-identical
17779
+ # contract), so the confirmation belongs in the one channel `watch`
17780
+ # already promises a consumer is reading: its own NDJSON stdout.
17781
+ from sourcecode.watch_loop import WatchEvent
17782
+
17783
+ _warming_event = WatchEvent(
17784
+ tick=0, status="watching",
17785
+ elapsed_ms=int((time.monotonic() - _warm_t0) * 1000),
17786
+ )
17787
+ _warming_line = _json.dumps(_warming_event.to_dict(), ensure_ascii=False)
17788
+ lines.append(_warming_line)
17789
+ typer.echo(_warming_line)
17790
+ if _sink is not None:
17791
+ _sink.write(_warming_line + "\n")
17792
+ _sink.flush()
17666
17793
  for event in watch(
17667
17794
  target,
17668
17795
  analyse=analyse_with_pr_impact(target, cir, model),
@@ -20658,6 +20785,12 @@ def cache_clear_cmd(
20658
20785
  typer.echo("Aborted.", err=True)
20659
20786
  raise typer.Exit(code=1)
20660
20787
  else:
20788
+ # ALT-09: a piped answer is read and honored before falling back
20789
+ # to the idempotent-cleanup default; see `_read_piped_answer`.
20790
+ _answer = _read_piped_answer()
20791
+ if _answer is not None and _answer.lower() in ("n", "no"):
20792
+ typer.echo("Aborted.", err=True)
20793
+ raise typer.Exit(code=1)
20661
20794
  typer.echo(
20662
20795
  f"Non-interactive: clearing cache for {target}{_ris_note}. "
20663
20796
  "Pass --yes to silence this notice.",
@@ -21327,14 +21460,21 @@ def cache_context_clear_cmd(
21327
21460
  """Delete the AI Context Cache for a repository (structured context + stats)."""
21328
21461
  from sourcecode import context_cache as _ctxcache
21329
21462
  target = _resolve_repo_root(Path(path))
21330
- if not yes and _stdin_is_interactive():
21331
- if not _confirm(
21332
- f"Delete AI context cache for {target}?",
21333
- command="ask cache context-clear",
21334
- err=True,
21335
- ):
21336
- typer.echo("Aborted.", err=True)
21337
- raise typer.Exit(code=1)
21463
+ if not yes:
21464
+ if _stdin_is_interactive():
21465
+ if not _confirm(
21466
+ f"Delete AI context cache for {target}?",
21467
+ command="ask cache context-clear",
21468
+ err=True,
21469
+ ):
21470
+ typer.echo("Aborted.", err=True)
21471
+ raise typer.Exit(code=1)
21472
+ else:
21473
+ # ALT-09: same piped-answer read as `cache clear`.
21474
+ _answer = _read_piped_answer()
21475
+ if _answer is not None and _answer.lower() in ("n", "no"):
21476
+ typer.echo("Aborted.", err=True)
21477
+ raise typer.Exit(code=1)
21338
21478
  removed = _ctxcache.clear_for_repo(target)
21339
21479
  typer.echo(f"Removed {removed} context file(s).")
21340
21480
 
@@ -21572,6 +21712,7 @@ def main_entry() -> None:
21572
21712
  # parser is about to read, so the guard at the end covers every command —
21573
21713
  # including one that returns before it reaches its emit seam.
21574
21714
  _record_output_promise(sys.argv[1:])
21715
+ _require_answer_from_argv(sys.argv[1:])
21575
21716
  # ASK-09: give the output ceiling a way to resolve the running command that
21576
21717
  # does not go through Click's thread-local context. Registered as a provider,
21577
21718
  # so the Typer→Click conversion is only paid by a run that is actually over