sourcecode 5.9.3__py3-none-any.whl → 5.9.6__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.3"
7
+ __version__ = "5.9.6"
@@ -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 = 'a0b0aba2d197881146491fccf60155b9d776bdb8'
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}
3
+ BUILD_COMMIT = '9bb90b227511dc71932bd66cf2ad103fd401314d'
4
+ CLOSURE_PROVENANCE = {'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'}, '`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': 116, 'present': 91, 'absent': 0, 'unresolved': 0, 'uncited': 25, 'unknown': 0}
@@ -16,6 +16,70 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
16
16
 
17
17
  ## Current Synchronization
18
18
 
19
+ **Field simulation on TeaVM, 2026-08-27 — `ASK-FIELD`.** A deep, non-destructive
20
+ run of ASK `5.9.5` against `/Users/user/Documents/workspace/testing/teavm`
21
+ (4,024 files; 3,539 Java files) exercised the main analysis commands, packs,
22
+ baseline lifecycle, contracts and cache lifecycle. The checkout stayed clean
23
+ and the prior release closure set showed no regression. Three behaviours were
24
+ reproduced and are now closed below: cache warming was not visible to subsequent
25
+ processes, root README summary extraction emitted a Markdown-link fragment, and
26
+ Spring presence disagreed between `spring-audit` and `migrate-check`. The run
27
+ also confirmed that endpoint zero-result disclosure, legal input refusals,
28
+ baseline collision protection, pack `UNVERIFIED` semantics and target
29
+ non-mutation remain intact. This is field evidence, not an external audit; it
30
+ must not be represented as a release certification.
31
+
32
+ | ID | Severity | Status | Evidence and required closure |
33
+ |---|---:|---|---|
34
+ | `ASK-FIELD-01` | P1 | **closed 5.9.6 (`829f9fa`)** | Explicit warm persists synchronously in its declared namespace; the fresh-process TeaVM lifecycle battery passes. |
35
+ | `ASK-FIELD-02` | P1 | **closed 5.9.6 (`6a659fa`)** | Member annotation imports resolve from the declaring class and confirmed Spring evidence drives presence; TeaVM and Spring control parity passes. |
36
+ | `ASK-FIELD-03` | P2 | **closed 5.9.6 (`55b0d51`)** | Split Markdown badge continuations are discarded without changing normal inline-link unwrapping; the real TeaVM summary regression passes. |
37
+
38
+ The simulation did not open the Kotlin/Java/Node classification as a defect:
39
+ the signals were explicitly confidence-scored and may represent a legitimate
40
+ polyglot repository. It also did not open zero endpoints, minimum token-budget
41
+ refusal, unsupported `explain --compact`, or contractless `verify`: each matched
42
+ the declared contract and non-coverage in the observed run.
43
+
44
+ **External audit intake `AUD-610`, release 5.9.3 — 2026-08-27.** The attached
45
+ report scored the build **7.2/10** after five parallel batches and approximately
46
+ 120 invocations. It verified a clean target repository, no Java-file changes,
47
+ no commits and no unauthorized persistence. The report praised structured errors,
48
+ output bounding for agents and dry-run safety, but identified 17 defects or
49
+ follow-ups. The full future closure criteria are maintained in
50
+ [`DEVELOPMENT-ROADMAP.md`](DEVELOPMENT-ROADMAP.md); the ledger records their
51
+ status boundary here so a later fix cannot silently close only one consumer.
52
+
53
+ The report's P1 reliability cluster was `AUD-610-B01` (root YAML compact/agent
54
+ zero-byte failure), `AUD-610-B02` (all `audit-report` formats zero-byte failure),
55
+ `AUD-610-B03` (`risk --limit 0` legal-boundary crash) and `AUD-610-B04`
56
+ (`timeline --since` pays for work then fails silently). None reproduced in the
57
+ TeaVM `5.9.5` simulation; they are held as release-artifact verification items,
58
+ not open deterministic defects.
59
+
60
+ The remaining rows are: `AUD-610-B05` consumer-path admission (**closed
61
+ 5.9.4**, `86ed0a3`); `AUD-610-B06` invalid-input exit parity (**closed**,
62
+ `ae2e8b1`, `f4b4f77`); `AUD-610-B07` command/path typo disambiguation
63
+ (**closed**, `178aa6d`); `AUD-610-B08` pack
64
+ gate ref admission (**closed 5.9.4**, `6690a7f`); `AUD-610-B09` inaccurate
65
+ root help for `--files -` (**closed**, `f66cd28`, `52bcc36`); `AUD-610-B10`
66
+ not-found envelope parity (**closed**, `c07581a`);
67
+ `AUD-610-B11` malformed CVE feed status (**closed 5.9.4**, `abc6319`);
68
+ `AUD-610-B12` unknown posture profiles (**closed 5.9.4**, `f7335b1`, `6e75646`);
69
+ `AUD-610-B13` baseline overwrite (**closed 5.9.4**, `cbe4da9`);
70
+ `AUD-610-B14` CLI/MCP population explanation (**closed**, `7c9b23e`);
71
+ `AUD-610-B15` no-history contract parity (**closed**, `a130637`);
72
+ `AUD-610-B16` UTF-8 replacement character (**platform verification hold**);
73
+ and `AUD-610-B17` one-off retrieve silent failure (**investigation hold, not
74
+ reproduced**).
75
+
76
+ Commercially, the report recommends conditional pilot/adoption and a provisional
77
+ `$35-50/developer/month` band, requiring closure of the flagship zero-byte paths
78
+ before an annual commitment. This is recorded as external market evidence, not
79
+ as a pricing approval or a product claim. The destructive commands excluded by
80
+ the auditor remain outside the evidence set because only their dry-run paths were
81
+ executed.
82
+
19
83
  **External `5.9.0` audit received, 2026-08-26 - `AUD-608`; corrections shipped in `5.9.1`.** The audited
20
84
  revision is the released `5.9.0` commit. The three confirmed corrections are
21
85
  closed: `AUD-608-C01` (P1, `cve` now declares its static inventory scope,
@@ -46,15 +46,30 @@ 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.9.3, `validation` enters through that shared CIR and `data-exposure` reuses one semantic
49
+ 5.9.6, `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
 
53
53
  The current external pack audit intake is tracked in
54
54
  [`AUDIT-2026-08-26-EXTERNAL-5.8.28-PACK.md`](AUDIT-2026-08-26-EXTERNAL-5.8.28-PACK.md);
55
- the two pack fixes are included in release 5.9.3, with release-artifact verification
55
+ the two pack fixes are included in release 5.9.6, with release-artifact verification
56
56
  remaining for detached execution and pack evidence.
57
57
 
58
+ ### Reliability contracts in 5.9.6
59
+
60
+ Invalid analysis inputs fail before expensive work and use the structured
61
+ `INVALID_INPUT` envelope: a missing `--consumer` repository, a malformed CVE
62
+ feed, or an unresolved `pack gate --since` ref is never treated as an empty or
63
+ `UNVERIFIED` result. `posture --diff` reports unknown profiles when the project
64
+ declares profiles. `baseline capture` does not replace a snapshot already stored
65
+ for the same commit; use `--auto` when a pipeline should skip it.
66
+
67
+ With `endpoints --compact`, `endpoints: []` means the rows were deliberately
68
+ omitted. Read `endpoints_note`, `endpoints_omitted` and `total`; an empty list is
69
+ not evidence that the API has no routes. `impact` and `impact-chain` return exit
70
+ code 1 when the target is unresolved, including when the selected repository
71
+ contains no Java sources.
72
+
58
73
  ---
59
74
 
60
75
  ## The semantic engine
@@ -221,7 +236,7 @@ pipx install sourcecode # isolated install, no venv needed
221
236
 
222
237
  # Verify
223
238
  ask version
224
- # ask 5.9.3
239
+ # ask 5.9.6
225
240
  ```
226
241
 
227
242
  Requires Python 3.9+.
@@ -445,11 +445,15 @@ def _announce_ask_dir(ask_dir: Path) -> None:
445
445
 
446
446
 
447
447
  def write_baseline(baseline: dict, out_dir: Path) -> Path:
448
- """Write `baseline` as deterministic JSON under `out_dir`; return the file path."""
448
+ """Write a new baseline without replacing an existing commit snapshot."""
449
449
  _readonly.guard(out_dir, what="baseline history directory")
450
450
  out_dir.mkdir(parents=True, exist_ok=True)
451
451
  protect_ask_dir(out_dir)
452
452
  path = out_dir / _baseline_filename(baseline)
453
+ if path.exists():
454
+ raise FileExistsError(
455
+ f"Baseline already exists for commit {baseline.get('commit') or '-'}: {path}"
456
+ )
453
457
  path.write_text(
454
458
  json.dumps(baseline, sort_keys=True, indent=2) + "\n", encoding="utf-8"
455
459
  )
sourcecode/cli.py CHANGED
@@ -4552,6 +4552,25 @@ def pack_gate_cmd(
4552
4552
  )
4553
4553
  raise typer.Exit(code=2)
4554
4554
  comparison_ref = since
4555
+ if since is not None:
4556
+ from sourcecode.git_checkout import RefError, repo_root, resolve_ref
4557
+
4558
+ try:
4559
+ resolve_ref(repo_root(root), since)
4560
+ except RefError as exc:
4561
+ # Test doubles and callers that provide a non-Git analysis root are
4562
+ # still handled by the existing pack component contracts. Only a
4563
+ # ref failure in an actual repository is an invalid comparison.
4564
+ if exc.code == "not_a_git_repository":
4565
+ pass
4566
+ else:
4567
+ _emit_error_json(
4568
+ INVALID_INPUT_CODE,
4569
+ str(exc),
4570
+ ref=exc.ref or since,
4571
+ hint="Use a commit, tag, or local branch that exists in the repository.",
4572
+ )
4573
+ raise typer.Exit(code=2)
4555
4574
  if pr is not None:
4556
4575
  try:
4557
4576
  from sourcecode.github_diff import pull_request_base_ref
@@ -6968,7 +6987,6 @@ def main(
6968
6987
  return _core_dict_for_writers
6969
6988
 
6970
6989
  if not no_cache and _core_key and not _pipeline_error and not changed_only:
6971
- import atexit as _atexit
6972
6990
  import threading as _threading
6973
6991
 
6974
6992
  # Pre-compute core dict in the main thread — avoids the 5-second atexit
@@ -7009,9 +7027,18 @@ def main(
7009
7027
  except Exception:
7010
7028
  pass
7011
7029
 
7012
- _cache_write_thread = _threading.Thread(target=_write_cache_async, daemon=True)
7013
- _cache_write_thread.start()
7014
- _atexit.register(_cache_write_thread.join, 30.0)
7030
+ # `cache warm` is an explicit persistence command. A daemon writer
7031
+ # may still be serialising a large core when the child exits, so a
7032
+ # successful warm could leave no entry for the next process to read.
7033
+ # Keep the low-latency asynchronous path for ordinary analysis, but
7034
+ # make the explicit warm contract synchronous and fail closed.
7035
+ if os.environ.get("SOURCECODE_CACHE_SYNC") in ("1", "true", "yes"):
7036
+ _write_cache_async()
7037
+ else:
7038
+ import atexit as _atexit
7039
+ _cache_write_thread = _threading.Thread(target=_write_cache_async, daemon=True)
7040
+ _cache_write_thread.start()
7041
+ _atexit.register(_cache_write_thread.join, 30.0)
7015
7042
 
7016
7043
  # Update RIS with aggregated snapshot data (non-fatal side-effect).
7017
7044
  # Update RIS whenever git is available, even when L1/L2 cache is skipped
@@ -8578,6 +8605,9 @@ def impact_cmd(
8578
8605
  not_found — no symbol and no substring. `risk_score` is null, never 0:
8579
8606
  nothing was measured, which is not measuring zero.
8580
8607
 
8608
+ An unresolved target exits with code 1, including when the selected repository
8609
+ contains no Java sources; the structured answer still names `not_found`.
8610
+
8581
8611
  \b
8582
8612
  NOTE: Free on repos up to the size limit; Pro unlocks enterprise-scale
8583
8613
  monoliths. Run 'ask license' for details.
@@ -8652,7 +8682,7 @@ def impact_cmd(
8652
8682
  format,
8653
8683
  )
8654
8684
  _emit_command_output(_nf_output, output_path, copy)
8655
- return
8685
+ _exit_on_unresolved_target("not_found")
8656
8686
 
8657
8687
  _prog = Progress()
8658
8688
  _prog.start(f"building CIR ({len(file_list)} files) for impact analysis")
@@ -8902,7 +8932,8 @@ def endpoints_cmd(
8902
8932
  False, "--compact",
8903
8933
  help="Answer how exposed the repository is without listing every endpoint: "
8904
8934
  "counts by method, by security policy and by controller. Every count is "
8905
- "measured over the full population.",
8935
+ "measured over the full population; an empty `endpoints` list means the "
8936
+ "rows were intentionally omitted, not that no endpoints exist.",
8906
8937
  ),
8907
8938
  no_cache: bool = typer.Option(
8908
8939
  False, "--no-cache",
@@ -8980,6 +9011,13 @@ def endpoints_cmd(
8980
9011
  _enforce_format("endpoints", format)
8981
9012
 
8982
9013
  target = _admit_path(path)
9014
+ if consumer:
9015
+ # A missing consumer must fail before the server scan. Treating it as an
9016
+ # empty consumer silently turns a requested join into an unfiltered one.
9017
+ consumer = [
9018
+ _admit_path(item, usage="endpoints --consumer")
9019
+ for item in consumer
9020
+ ]
8983
9021
 
8984
9022
  # AUD-591-A10: a prefix that is not a route selects nothing and reports it
8985
9023
  # as no exposure. Rejected before the analysis, so the shell rewrite is
@@ -9116,6 +9154,12 @@ def endpoints_cmd(
9116
9154
 
9117
9155
  _listed = len(data.get("endpoints", []))
9118
9156
  data["endpoints"] = []
9157
+ data["endpoints_note"] = (
9158
+ f"The endpoints list is intentionally omitted by --compact; "
9159
+ f"{_listed} endpoint(s) were measured. An empty `endpoints` list "
9160
+ "does not mean that the repository has no endpoints. Re-run without "
9161
+ "--compact or narrow the request to retrieve rows."
9162
+ )
9119
9163
  data["endpoints_omitted"] = {
9120
9164
  "total": _listed,
9121
9165
  "shown": 0,
@@ -10599,7 +10643,9 @@ def baseline_capture_cmd(
10599
10643
  ref: Optional[str] = typer.Option(
10600
10644
  None, "--ref",
10601
10645
  help="Human label for this baseline (e.g. a version or milestone). "
10602
- "The commit is recorded automatically when the path is a git repo.",
10646
+ "The commit is recorded automatically when the path is a git repo. "
10647
+ "A capture never overwrites an existing snapshot for the same commit; "
10648
+ "use --auto to skip an already captured commit.",
10603
10649
  ),
10604
10650
  kind: str = typer.Option(
10605
10651
  "architecture", "--kind",
@@ -10750,7 +10796,16 @@ def baseline_capture_cmd(
10750
10796
  baseline = build_baseline(
10751
10797
  _cir, ref=ref, commit=git_commit(target_dir), record_env=record_env,
10752
10798
  )
10753
- written = write_baseline(baseline, dest)
10799
+ try:
10800
+ written = write_baseline(baseline, dest)
10801
+ except FileExistsError as exc:
10802
+ _prog.finish()
10803
+ _emit_error_json(
10804
+ INVALID_INPUT_CODE,
10805
+ str(exc),
10806
+ hint="Use `--auto` to skip an already captured commit, or capture a new commit.",
10807
+ )
10808
+ raise typer.Exit(code=1)
10754
10809
  _prog.finish()
10755
10810
 
10756
10811
  t = baseline["totals"]
@@ -10846,16 +10901,26 @@ def baseline_diff_cmd(
10846
10901
  _history = _resolve_history_dir((history_dir or target_dir).resolve())
10847
10902
  _series = load_series(_history)
10848
10903
  if not _series:
10849
- # No stored capture is not "nothing changed": there is no base to
10850
- # compare against, and answering with an empty diff would read as one.
10851
- _emit_error_json(
10852
- INVALID_INPUT_CODE,
10853
- f"No findings baseline is stored for '{target_dir}'.",
10854
- path=str(_history),
10855
- hint="Capture one first: ask baseline capture <path> --kind findings.",
10856
- expected="A stored findings-baseline-v1 JSON file.",
10904
+ # Empty history is an honest query result, not invalid input. Keep
10905
+ # diff/trend/attest interchangeable so automation can branch on one
10906
+ # status instead of treating the same state as success in one command
10907
+ # and a failure in another.
10908
+ _emit_command_output(
10909
+ _serialize_dict({
10910
+ "schema": "findings-delta-v1",
10911
+ "status": "no_history",
10912
+ "history_dir": str(_history),
10913
+ "message": "No findings baseline is stored; no delta was computed.",
10914
+ "non_coverage": [
10915
+ "Capture one first: ask baseline capture <path> --kind findings."
10916
+ ],
10917
+ "counts": {"new": 0, "resolved": 0, "unchanged": 0},
10918
+ }, format),
10919
+ output_path, copy,
10920
+ success_msg=f"Findings delta written to {output_path} (no history)",
10921
+ fmt=format,
10857
10922
  )
10858
- raise typer.Exit(code=1)
10923
+ return
10859
10924
 
10860
10925
  _prog = Progress()
10861
10926
  _prog.start("auditing the working tree")
@@ -10886,15 +10951,18 @@ def baseline_diff_cmd(
10886
10951
  history = _resolve_history_dir((history_dir or target_dir).resolve())
10887
10952
  latest = latest_baseline_path(history)
10888
10953
  if latest is None:
10889
- _emit_error_json(
10890
- INVALID_INPUT_CODE,
10891
- f"No captured baseline found for '{target_dir}'.",
10892
- path=str(history),
10893
- hint=_BASELINE_HISTORY_HINT,
10894
- expected="A stored architectural-baseline-v1 JSON file, or a "
10895
- "repository whose .ask/baselines holds one.",
10954
+ _emit_command_output(
10955
+ _serialize_dict({
10956
+ "schema": "architectural-delta-v1",
10957
+ "status": "no_history",
10958
+ "history_dir": str(history),
10959
+ "message": "No captured baseline was found; no delta was computed.",
10960
+ "non_coverage": [_BASELINE_HISTORY_HINT],
10961
+ }, format),
10962
+ output_path, copy,
10963
+ success_msg=f"Baseline diff written to {output_path} (no history)",
10896
10964
  )
10897
- raise typer.Exit(code=1)
10965
+ return
10898
10966
  baseline_file = latest
10899
10967
 
10900
10968
  if not baseline_file.exists():
@@ -11022,15 +11090,19 @@ def baseline_trend_cmd(
11022
11090
 
11023
11091
  baselines = load_baselines_dir(trend_dir)
11024
11092
  if not baselines:
11025
- _emit_error_json(
11026
- INVALID_INPUT_CODE,
11027
- f"No architectural baselines found in '{trend_dir}'.",
11028
- path=str(trend_dir),
11029
- hint=_BASELINE_HISTORY_HINT,
11030
- expected="A repository with a captured history, or a directory "
11031
- "containing architectural-baseline-v1 JSON files.",
11093
+ _emit_command_output(
11094
+ _serialize_dict({
11095
+ "schema": "architectural-trend-v1",
11096
+ "status": "no_history",
11097
+ "baseline_count": 0,
11098
+ "points": [],
11099
+ "hotspot_trends": [],
11100
+ "non_coverage": [_BASELINE_HISTORY_HINT],
11101
+ }, format),
11102
+ output_path, copy,
11103
+ success_msg=f"Trend written to {output_path} (no history)",
11032
11104
  )
11033
- raise typer.Exit(code=1)
11105
+ return
11034
11106
 
11035
11107
  data = build_trend(baselines)
11036
11108
  output = _serialize_dict(data, format)
@@ -14519,7 +14591,9 @@ def posture_cmd(
14519
14591
  Two axes, and they are different questions. `--diff` holds the tree fixed
14520
14592
  and moves the profile set. `--diff-ref` (and `--since`) hold the profile set
14521
14593
  fixed and move the tree: *does this change what a client can reach without
14522
- authenticating?* That one takes `--fail-on` and is the per-PR gate.
14594
+ authenticating?* That one takes `--fail-on` and is the per-PR gate. When the
14595
+ repository declares profiles, `--diff` rejects a profile name outside that
14596
+ population instead of comparing a configuration that does not exist.
14523
14597
 
14524
14598
  \b
14525
14599
  Examples:
@@ -14743,10 +14817,28 @@ def posture_cmd(
14743
14817
  )
14744
14818
  raise typer.Exit(code=1)
14745
14819
  left_raw, right_raw = diff.split(":", 1)
14820
+ left_profiles = _parse_set(left_raw, flag="--diff (left side)")
14821
+ right_profiles = _parse_set(right_raw, flag="--diff (right side)")
14822
+ from sourcecode.spring_profiles import profile_population
14823
+
14824
+ declared_profiles = set(profile_population(path).names)
14825
+ missing_profiles = sorted(
14826
+ (left_profiles | right_profiles) - declared_profiles
14827
+ )
14828
+ if missing_profiles and declared_profiles:
14829
+ _emit_error_json(
14830
+ INVALID_INPUT_CODE,
14831
+ "--diff names profile(s) not declared by the repository: "
14832
+ + ", ".join(missing_profiles),
14833
+ profiles=missing_profiles,
14834
+ hint="Use profiles reported by `ask posture <repo>`; use a single "
14835
+ "posture run to inspect an undeclared profile explicitly.",
14836
+ )
14837
+ raise typer.Exit(code=1)
14746
14838
  data = diff_posture(
14747
14839
  path,
14748
- _parse_set(left_raw, flag="--diff (left side)"),
14749
- _parse_set(right_raw, flag="--diff (right side)"),
14840
+ left_profiles,
14841
+ right_profiles,
14750
14842
  overrides,
14751
14843
  )
14752
14844
  else:
@@ -15571,7 +15663,7 @@ def impact_chain_cmd(
15571
15663
  output = _serialize_dict(data, format)
15572
15664
  _emit_command_output(output, output_path, copy,
15573
15665
  success_msg=f"Impact chain written to {output_path}")
15574
- return
15666
+ _exit_on_unresolved_target("not_found")
15575
15667
 
15576
15668
  _prog = Progress()
15577
15669
  _prog.start(f"analyzing impact ({len(file_list)} files)")
@@ -20306,6 +20398,11 @@ def cache_warm_cmd(
20306
20398
  # console script from PATH. A stale global script can otherwise warm one
20307
20399
  # package installation while the caller reads another one, leaving a view
20308
20400
  # apparently fresh even though its cache entry was never written.
20401
+ # The warm's child must finish its cache writes before reporting success.
20402
+ # This is scoped to the child so ordinary root analyses retain their async
20403
+ # write latency while `cache warm` has a durable cross-process contract.
20404
+ _sc_env = os.environ.copy()
20405
+ _sc_env["SOURCECODE_CACHE_SYNC"] = "1"
20309
20406
  _sc_bin = [sys.executable, "-m", "sourcecode"]
20310
20407
 
20311
20408
  # AUD-600-R01: which views a warm builds is `cache_model`'s fact, not a pair
@@ -20363,7 +20460,9 @@ def cache_warm_cmd(
20363
20460
  # none of it — only the exit code and, on failure, stderr. Capturing it
20364
20461
  # held a whole repository's answer in memory for nothing; on a 24 074-file
20365
20462
  # repository that is megabytes the parent buffers and drops.
20366
- result = _sub.run(cmd, stdout=_sub.DEVNULL, stderr=_sub.PIPE, text=True)
20463
+ result = _sub.run(
20464
+ cmd, stdout=_sub.DEVNULL, stderr=_sub.PIPE, text=True, env=_sc_env
20465
+ )
20367
20466
  finally:
20368
20467
  _prog.stop()
20369
20468
  if result.returncode != 0:
sourcecode/cve_feed.py CHANGED
@@ -258,9 +258,14 @@ def load_feed(feed_path: Path, *, now: Optional[datetime] = None) -> Feed:
258
258
  newest = stamp
259
259
 
260
260
  if unreadable:
261
+ feed.error = f"{unreadable} feed file(s) were not valid JSON"
261
262
  feed.limitations.append(
262
- f"{unreadable} feed file(s) were not valid JSON and were skipped"
263
+ f"{unreadable} feed file(s) were not valid JSON; the feed was rejected"
263
264
  )
265
+ # Partial advisory data is unsafe for a reachability gate: a skipped
266
+ # file can contain the one advisory that should have blocked the run.
267
+ feed.loaded = False
268
+ return feed
264
269
  feed.packages = len(feed.by_package)
265
270
  feed.loaded = True
266
271
 
sourcecode/self_audit.py CHANGED
@@ -246,6 +246,15 @@ def run_self_audit() -> dict:
246
246
  from sourcecode import __version__
247
247
 
248
248
  commands = _registered_commands()
249
+ # CLI leaves and MCP tools are related populations, not aliases of one
250
+ # another: MCP also serves native orchestration tools with no CLI command.
251
+ from sourcecode.mcp.registry import build_mcp_tool_specs
252
+ from sourcecode.mcp.server import _NATIVE_MCP_TOOLS
253
+
254
+ cli_command_count = len(commands)
255
+ mcp_registry_count = len(build_mcp_tool_specs())
256
+ native_mcp_count = len(_NATIVE_MCP_TOOLS)
257
+ mcp_tool_count = mcp_registry_count + native_mcp_count
249
258
  checks = [
250
259
  _check_cache_model(commands),
251
260
  _check_format_contract(commands),
@@ -268,7 +277,22 @@ def run_self_audit() -> dict:
268
277
  "schema": SCHEMA_ID,
269
278
  "verdict": verdict,
270
279
  "tool_version": __version__,
271
- "commands_registered": len(commands),
280
+ "commands_registered": cli_command_count,
281
+ "mcp_tools_served": mcp_tool_count,
282
+ "tool_populations": {
283
+ "cli_commands": {
284
+ "count": cli_command_count,
285
+ "definition": "registered CLI leaf commands, including commands hidden from MCP",
286
+ },
287
+ "mcp_tools": {
288
+ "count": mcp_tool_count,
289
+ "definition": "runtime-derived MCP tools plus native orchestration tools",
290
+ "runtime_derived": mcp_registry_count,
291
+ "native_orchestration": native_mcp_count,
292
+ },
293
+ "relationship": "mcp_tools = runtime_derived_mcp_tools + native_orchestration_tools; "
294
+ "CLI command count is not expected to equal MCP tool count",
295
+ },
272
296
  "counts": {
273
297
  "checks": len(checks),
274
298
  "passed": sum(1 for c in checks if c.status == STATUS_PASS),
@@ -130,6 +130,21 @@ def _resolve_bean_annotation(
130
130
  """
131
131
  simple = ann.lstrip("@")
132
132
  imports = imports_by_class.get(owner_fqn) or {}
133
+ if not imports:
134
+ # Field and method nodes carry a member suffix, while imports belong to
135
+ # their declaring class. Missing this join turns a foreign annotation
136
+ # such as Maven's `@Component` into an unresolved one and can falsely
137
+ # advertise a non-Spring repository as Spring. Member nodes have used
138
+ # both `Class#member` and `Class.member` identities over time.
139
+ owner_class = owner_fqn.split("#", 1)[0]
140
+ imports = imports_by_class.get(owner_class) or {}
141
+ if not imports:
142
+ declaring = max(
143
+ (name for name in imports_by_class if owner_fqn.startswith(name + ".")),
144
+ key=len,
145
+ default="",
146
+ )
147
+ imports = imports_by_class.get(declaring) or {}
133
148
 
134
149
  exact = imports.get(simple)
135
150
  if exact:
@@ -387,6 +402,22 @@ class BeanGraph:
387
402
  for b in self.beans.values()
388
403
  )
389
404
 
405
+ def has_confirmed_spring_beans(self) -> bool:
406
+ """True only when a bean annotation resolves to ``org.springframework``.
407
+
408
+ An unresolved simple annotation remains admissible for model consumers:
409
+ the missing import is not proof that the annotation is foreign. It is,
410
+ however, insufficient evidence for the cross-command product fact
411
+ ``spring_detected``. Otherwise a repository such as TeaVM, which uses
412
+ Maven's ``@Component`` spelling without Spring, is advertised as Spring.
413
+ """
414
+ return any(
415
+ b.spring
416
+ and b.annotation_fqn.startswith(_SPRING_PACKAGE_PREFIX)
417
+ and b.stereotype not in _JPA_ONLY_STEREOTYPES
418
+ for b in self.beans.values()
419
+ )
420
+
390
421
  def spring_beans(self) -> dict[str, BeanNode]:
391
422
  """The Spring IoC bean-definition population, keyed by symbol.
392
423
 
@@ -652,4 +683,13 @@ def spring_detected(
652
683
  can still ask.
653
684
  """
654
685
  graph = model.bean_graph if model is not None else BeanGraph.build(cir)
655
- return bool(graph.has_spring_beans()) or int(tx_total or 0) > 0
686
+ if graph.has_confirmed_spring_beans() or int(tx_total or 0) > 0:
687
+ return True
688
+ # Preserve the conservative model behaviour for a genuinely unresolved
689
+ # annotation, but never let a resolved foreign annotation trigger presence.
690
+ return any(
691
+ b.spring
692
+ and not b.annotation_fqn
693
+ and b.stereotype not in _JPA_ONLY_STEREOTYPES
694
+ for b in graph.beans.values()
695
+ )
sourcecode/summarizer.py CHANGED
@@ -458,6 +458,10 @@ class ProjectSummarizer:
458
458
  if (
459
459
  _BADGE_RE.match(line)
460
460
  or _LINK_ONLY_RE.match(line)
461
+ # A Markdown badge may wrap before its closing `](url)`. The
462
+ # first line is discarded above, but the continuation must not
463
+ # become a standalone project description.
464
+ or _re.match(r"^\s*\)?\]\([^)]*\)\s*$", line)
461
465
  or ("<" in line and ">" in line and not _html_stripped)
462
466
  ):
463
467
  # AUD-596-B07: a bar written on one line never becomes a
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 5.9.3
3
+ Version: 5.9.6
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  Project-URL: Homepage, https://github.com/HarounDominique/sourcecode
6
6
  Project-URL: Repository, https://github.com/HarounDominique/sourcecode
@@ -59,7 +59,7 @@ Description-Content-Type: text/markdown
59
59
  > question. All analysis is local and deterministic; see [Privacy](#configuration--privacy)
60
60
  > before inspecting a repository whose telemetry policy you need to control.
61
61
 
62
- ![Version](https://img.shields.io/badge/version-5.9.3-blue)
62
+ ![Version](https://img.shields.io/badge/version-5.9.6-blue)
63
63
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
64
64
 
65
65
  Copyright (c) 2026 Dominique Haroun. All rights reserved. ASK Engine 5.7.0 and
@@ -196,7 +196,7 @@ set `ASK_MAX_RSS_MB=<megabytes>`; if the process exceeds it, ASK refuses the
196
196
  answer with `MEMORY_TOO_LARGE`, including the observed peak and limit. Leave it
197
197
  unset, or set it to `0`, to disable the ceiling explicitly.
198
198
 
199
- **Current performance status.** Release 5.9.3 retains the shared Canonical IR
199
+ **Current performance status.** Release 5.9.6 retains the shared Canonical IR
200
200
  path used by `validation` and now passes that same CIR into labelled `data-exposure`
201
201
  runs; the no-label path remains parse-free. These are implementation properties,
202
202
  not duration promises. A controlled external warm-path audit has opened performance
@@ -218,6 +218,14 @@ in `overlapping_paths`; a missing or non-directory path is rejected as structure
218
218
  invalid input rather than treated as an empty repository. This prevents silent
219
219
  entitlement errors in CI and does not change per-repository gates or billing.
220
220
 
221
+ Release 5.9.6 also hardens the boundary contracts: missing consumer repositories,
222
+ malformed CVE feeds and unresolved `pack gate --since` refs fail as structured
223
+ input errors; duplicate same-commit baselines are never overwritten. `posture
224
+ --diff` validates unknown profiles when the repository declares profiles. In
225
+ `endpoints --compact`, an empty `endpoints` list is explicitly marked as an
226
+ intentional omission, not an empty API; unresolved `impact` and `impact-chain`
227
+ queries exit non-zero, including on repositories without Java sources.
228
+
221
229
  **What a warm actually covers.** `ask cache warm` rebuilds the declared root views and their
222
230
  shared structural layers (L1/L2 + the Repository Intelligence Snapshot + the shared Canonical
223
231
  IR). Pass `--views compact` or `--views compact,agent` to warm only selected views; without
@@ -243,7 +251,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
243
251
  # pip / pipx
244
252
  pipx install sourcecode # or: pip install sourcecode
245
253
 
246
- ask version # ask 5.9.3 — and, on a build that has aged,
254
+ ask version # ask 5.9.6 — and, on a build that has aged,
247
255
  # how many releases have probably shipped since
248
256
  ```
249
257
 
@@ -1,12 +1,12 @@
1
- sourcecode/__init__.py,sha256=UDhga2gkkWXn74Pt5-7wx8lD43QwN3SpJUzes_yiE-s,308
1
+ sourcecode/__init__.py,sha256=CbRd02cfZUR4FzB0YaXA5TDXpC4TVs6rxREE8Mh4QVo,308
2
2
  sourcecode/__main__.py,sha256=qDFVkobwsdZrX0B3stiRqsUNivVpybKO-egzAwCW4xw,165
3
- sourcecode/_build_commit.py,sha256=_5RY0YyRD9M5XcuDOokJ_Lx30t5eia8v9rvq3NDpzB0,8740
3
+ sourcecode/_build_commit.py,sha256=k-8VdhNwE-xC_kVXs_f5MKE4bT-D4Ow9l0KgBIHMjVM,8926
4
4
  sourcecode/access_drift.py,sha256=1R7_fiAPvKNOnxc9uxkH3JH-k8MiAuyE7FwWUX1r4Fg,8995
5
5
  sourcecode/adaptive_scanner.py,sha256=yJBKjNpkY6bpueYJ2YnRezen3sYZDecEt7WaaNWdqug,9466
6
6
  sourcecode/analysis_budget.py,sha256=ZuNEboPF8A6GK-_V7WNt92ezgriga7_Rbr-vvSN4UEE,4183
7
7
  sourcecode/answer_bounds.py,sha256=_51_DCuYEUmI_xwUFxDMiprSavkNakkLiIBbPjevQ3M,9793
8
8
  sourcecode/archetype.py,sha256=V_s5grgpbanrXb8qGEQb73W5Cg5iKW9FQHWKgg5KM8Y,41201
9
- sourcecode/architectural_baseline.py,sha256=4GiMVBLJVHvRKSWQGf2ZVurI1-R0qk5pmaevgvGSgVA,26325
9
+ sourcecode/architectural_baseline.py,sha256=9ds-ZYw4dEJUKWZSdq_ufHq7d2AXnMbEHteSQlT7iu8,26468
10
10
  sourcecode/architectural_delta.py,sha256=JhZVmA0gYUryt4x4ieVuxPbcvoBygHgADZaYJ_o4gdk,15142
11
11
  sourcecode/architecture_analyzer.py,sha256=GFc4ek-s1IHWM7pl-0L32WahZ93AmDgrAMcOuBKA5Dk,61463
12
12
  sourcecode/architecture_summary.py,sha256=BVVRHd952cjRhjHnR6CPrvKgaa-tdM16l-pBi1yDCPs,32395
@@ -35,7 +35,7 @@ sourcecode/change_plan.py,sha256=drAoDsqFplutCLc6Y0xue7sYkTls0SpYh7aOXvttv3I,129
35
35
  sourcecode/ci_output.py,sha256=X4NRokWWMRF9Ex8OQrEO2g04GzJ00GDP5zYIoqI9YCc,4625
36
36
  sourcecode/cir_graphs.py,sha256=RytqSv1WF8kdlfXj2fPQz16LY4akS2kyThbdWL8beCQ,14835
37
37
  sourcecode/classifier.py,sha256=JBzPwSSrDG-tUHAbcKB678HRbjLpD-ohzbzzO62mgpo,20114
38
- sourcecode/cli.py,sha256=quyqy_kgnVGoCue8UcibOX3EjDg1epoLMV7uwSeOHUg,933170
38
+ sourcecode/cli.py,sha256=uX7QYtJUYQJ8ci_ca4WI_evIjeVcsEZbef5iCDPptBg,938038
39
39
  sourcecode/client_calls.py,sha256=YOPnfRZzRyqmdHvXSVeiunW8cuiuCuWXacNIQRgKMCw,22640
40
40
  sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
41
41
  sourcecode/compare.py,sha256=Lnfmm9I3k0H1jP1tV6ZJ-R7znjQPLPLCe5TzV1Jb_PM,15323
@@ -52,7 +52,7 @@ sourcecode/contract_init.py,sha256=bXRCqWM9tvrDLeT8j7ea6IWf87jB4VJxnlSzGBsRRXo,1
52
52
  sourcecode/contract_model.py,sha256=nRxJKPMs1VHwFTa8AVXhGmaLjti3Lr2sjHDpWgv1bfE,3917
53
53
  sourcecode/contract_pipeline.py,sha256=4XE-xhab7ysdExXyUOBl2hAH4MABo7moKnT9V4XcEwQ,30243
54
54
  sourcecode/coverage_parser.py,sha256=X9scuUWxacahYTrTBGea2Ku-sf-WCVcnc9OEB35_3IU,19340
55
- sourcecode/cve_feed.py,sha256=t93Ofu0uxB3zyI8W9nUKmH4Ljbt4qZpGIg68IP_aTrY,10749
55
+ sourcecode/cve_feed.py,sha256=IBVpQaY04biXKEeNf1qKO_95GqEV23fwhj4oNk7s1-g,11028
56
56
  sourcecode/cve_reachability.py,sha256=aEHkG3IaeLxgekK79cz07QTcv9LZHEqEYqyLGjwjzSE,20409
57
57
  sourcecode/data_exposure.py,sha256=73EcuHsN3ESKdSvSj1B2FzD1myGJqzlNJpYO1NpI5WM,19963
58
58
  sourcecode/data_labels.py,sha256=9v0JaICRH7jIZ015T1_DuT5E0MFfeL-Wcq1WOPE0Fe0,10965
@@ -166,7 +166,7 @@ sourcecode/security_config.py,sha256=LUppRDYQK0oKM4qdEOLObaeU3frFTS-paEZo7dMgtY8
166
166
  sourcecode/security_config_scan.py,sha256=LU8gfNUBB6wjKR8jkK-qRKtUrbkHZ301_Xsk6hSp4XU,35916
167
167
  sourcecode/security_posture.py,sha256=ZWgWMsVdvkDHOkTtAy6T3Q9Kp8ufWsdqPsFQZVBNAFM,56885
168
168
  sourcecode/selection_contract.py,sha256=O3q_zHBgUsp1SDukqmH9nOUO6HxLFH_7fAqVL7JMV_A,1974
169
- sourcecode/self_audit.py,sha256=XlPZwLwX8k7B_iyLtyNpL9OkibLOu79yCbM9gkuOMnQ,10338
169
+ sourcecode/self_audit.py,sha256=ZK6VZwbkTL66f4Q4U_JD6sdl6KjQ4DvtPcn5XqGcZDY,11541
170
170
  sourcecode/selftest.py,sha256=WjD2xMa55k2svoBMpBLpHCPMip3rtSNUbskSqsuB3iY,40056
171
171
  sourcecode/semantic_analyzer.py,sha256=xGwQvhU9xeOlZ4N-TPXFZT8HBHx4Rg0nPwu8nDFl00Y,95621
172
172
  sourcecode/semantic_impact_engine.py,sha256=t09IirGC3JjQDy33JZd1_WKzQVKXkoNl3-XEUr5kjis,20563
@@ -178,14 +178,14 @@ sourcecode/source_text.py,sha256=R52gmSljz7L6YVPbixv5W-LOZ5eKUVvSUP3S-QH_IWc,904
178
178
  sourcecode/spring_event_topology.py,sha256=5_ON_21Le5zbG-1GRc5GLIi5HJfy_QjcXLVPC5WeUGQ,18055
179
179
  sourcecode/spring_findings.py,sha256=U5gMDdSdhpJLstt3ghG4y5CCS9nzIU9NuRMHpNu7FpY,27766
180
180
  sourcecode/spring_impact.py,sha256=nX-tuZOiWYc54tlCkBtel07_ksI5MCmFYNb-WONkCPM,98607
181
- sourcecode/spring_model.py,sha256=VHankGENA8vyVtJ5SfT1Of6SONCM-GX4eO78jjcJFYI,27086
181
+ sourcecode/spring_model.py,sha256=MBUKR1tes5UVGxtYiv4J2WrydgOp_zReek7P7hO50hA,28962
182
182
  sourcecode/spring_profiles.py,sha256=jDNBVhEZQAZXrpqt85FkP2URsMw4YWhQo3W3SLHuwpM,19617
183
183
  sourcecode/spring_properties.py,sha256=kPTk5qAJxdbHc1hclhHAP0O-hCIzBn8PTh3lzl-I4a4,8400
184
184
  sourcecode/spring_security_audit.py,sha256=mJxrB-uKvMh2aF3Q8cFVOC8yOYb2JW8P-yK-paMTQBQ,71218
185
185
  sourcecode/spring_semantic.py,sha256=zrUYWF7Gnz6hpP_3hRveHmGEntT95WpgXt6u7PzLSGg,16835
186
186
  sourcecode/spring_tx_analyzer.py,sha256=cFK9M3madb7dKbJ_d97cO-1fGMUPDOwB8KoJ-oocMeY,50951
187
187
  sourcecode/sql_taint.py,sha256=As1DbEdjXMBFjCTXU5ojksOzuKkP6LmnuMVnzzmckzc,26998
188
- sourcecode/summarizer.py,sha256=9LqLQWuGr6zmgpDAbgC7ZCS7mTI4Vnv4KzfHisdr9zY,41073
188
+ sourcecode/summarizer.py,sha256=ldvynjFaKQmQKLiu4_QJEpdciI6WybEodvN7TPQzfwA,41350
189
189
  sourcecode/target_admission.py,sha256=wFZ4pzlxhiF6Q6s2lEAZEzcCfj1Y6xNvujjt8MdO0Qo,7154
190
190
  sourcecode/test_gap_ranking.py,sha256=hl-tTyQUXZGJycG1b6npbLeE_DaVHaOsvPwG9qqC5y8,15711
191
191
  sourcecode/test_sources.py,sha256=MO6dYg5ogTN29n9G0-IbMqx8omCT23lkhQHbEze2p5M,9441
@@ -273,13 +273,13 @@ sourcecode/telemetry/consent.py,sha256=-Wb8BFGXEexOs8M0Eja7F-T6ZtLNPeRoZnHcHvlP7
273
273
  sourcecode/telemetry/events.py,sha256=l3Z2SQUO4_C2D2ItXOOdVNimAmHedUHnUxqYFTxnsAE,2900
274
274
  sourcecode/telemetry/filters.py,sha256=-lqT2oorECuqEIdeHb--7AZ6BCaUx0bya720a-Z5x6w,7335
275
275
  sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
276
- sourcecode/_docs/DEFECT-LEDGER.md,sha256=EkYSJieDrazfADCZ8tsCsybyW9U0nAzWDQ8rS4KmGTU,1115164
276
+ sourcecode/_docs/DEFECT-LEDGER.md,sha256=8VCou5sbCLeRew53_g4qzOwJT5gk3VT-9raFRYnzkY8,1119529
277
277
  sourcecode/_docs/LICENSE,sha256=23-fk61oSWGVmkWyka-FIM0rhAPM2hcYHnsaridL8Uo,1859
278
278
  sourcecode/_docs/NOTICE,sha256=Br-AA8Y3iJo1WXa5uP8QBpzqI5nDu3iGLbFyvDt3BOs,321
279
- sourcecode/_docs/USER_GUIDE.md,sha256=Yz49suorAuiyJLgdl3yPZAWH3aMzycejEhY8Aoeszcc,103883
280
- sourcecode-5.9.3.dist-info/METADATA,sha256=mxk_hAlo_FQe8kyHzN4d9ZXqBK3a6wt8xbeFw776CbI,64209
281
- sourcecode-5.9.3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
282
- sourcecode-5.9.3.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
283
- sourcecode-5.9.3.dist-info/licenses/LICENSE,sha256=23-fk61oSWGVmkWyka-FIM0rhAPM2hcYHnsaridL8Uo,1859
284
- sourcecode-5.9.3.dist-info/licenses/NOTICE,sha256=Br-AA8Y3iJo1WXa5uP8QBpzqI5nDu3iGLbFyvDt3BOs,321
285
- sourcecode-5.9.3.dist-info/RECORD,,
279
+ sourcecode/_docs/USER_GUIDE.md,sha256=uL8xhxZzt7YddqEIxClPky9n_9fgRUzV71EJJzg4nPI,104721
280
+ sourcecode-5.9.6.dist-info/METADATA,sha256=0lScNAiEEcjd-RM0Og37w0jO4ZMIt4UjI7oRlfZPGqE,64752
281
+ sourcecode-5.9.6.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
282
+ sourcecode-5.9.6.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
283
+ sourcecode-5.9.6.dist-info/licenses/LICENSE,sha256=23-fk61oSWGVmkWyka-FIM0rhAPM2hcYHnsaridL8Uo,1859
284
+ sourcecode-5.9.6.dist-info/licenses/NOTICE,sha256=Br-AA8Y3iJo1WXa5uP8QBpzqI5nDu3iGLbFyvDt3BOs,321
285
+ sourcecode-5.9.6.dist-info/RECORD,,