sourcecode 5.9.7__py3-none-any.whl → 5.9.9__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 +1 -1
- sourcecode/_build_commit.py +3 -3
- sourcecode/_docs/DEFECT-LEDGER.md +62 -0
- sourcecode/_docs/USER_GUIDE.md +8 -4
- sourcecode/cli.py +174 -32
- sourcecode/error_schema.py +14 -0
- sourcecode/license.py +8 -6
- sourcecode/packs.py +25 -4
- sourcecode/progress.py +12 -10
- sourcecode/risk.py +61 -0
- sourcecode/selftest.py +45 -2
- {sourcecode-5.9.7.dist-info → sourcecode-5.9.9.dist-info}/METADATA +9 -5
- {sourcecode-5.9.7.dist-info → sourcecode-5.9.9.dist-info}/RECORD +17 -17
- {sourcecode-5.9.7.dist-info → sourcecode-5.9.9.dist-info}/WHEEL +0 -0
- {sourcecode-5.9.7.dist-info → sourcecode-5.9.9.dist-info}/entry_points.txt +0 -0
- {sourcecode-5.9.7.dist-info → sourcecode-5.9.9.dist-info}/licenses/LICENSE +0 -0
- {sourcecode-5.9.7.dist-info → sourcecode-5.9.9.dist-info}/licenses/NOTICE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/_build_commit.py
CHANGED
|
@@ -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 = '
|
|
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'}, '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':
|
|
3
|
+
BUILD_COMMIT = '9e8ac3fb54c7d82f42f5240ff518ec41276d9bc2'
|
|
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-F': {'commit': '', 'status': 'uncited'}, 'AUD-614-G': {'commit': '', 'status': 'uncited'}, 'AUD-614-I': {'commit': '', 'status': 'uncited'}, 'AUD-614-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-614-SELFTEST-TIMEOUT': {'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'}, '`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': 142, 'present': 91, 'absent': 0, 'unresolved': 0, 'uncited': 51, 'unknown': 0}
|
|
@@ -71,6 +71,68 @@ Commercially, the audit supports normal repositories up to several thousand
|
|
|
71
71
|
files and keeps a provisional $35-50/developer/month range, but B01/B02 block
|
|
72
72
|
annual-grade reliability and enterprise-scale positioning until resolved.
|
|
73
73
|
|
|
74
|
+
**External audit intake `AUD-612`, re-audit of release 5.9.7 — 2026-08-27.**
|
|
75
|
+
The external scope covered roughly 130 new invocations across three forks and direct
|
|
76
|
+
retests of all AUD-611 items. It reports five clean forks, no target writes, commits
|
|
77
|
+
or task-kills, and a score of **7.8/10**. It confirms B05, B06, B08, B09, B10 and B11,
|
|
78
|
+
and reports B01 fixed in its own matrix; B01 remains release-artifact verification
|
|
79
|
+
because the prior external round reproduced it. The new queue is:
|
|
80
|
+
|
|
81
|
+
| ID | Severity | Status | Evidence / closure boundary |
|
|
82
|
+
|---|---:|---|---|
|
|
83
|
+
| AUD-612-B02 | Critical | open | `tutorials` root compact still timed out after 300-311s with zero stdout; warning proves activity, not completion. Define scale, deadline and partial-diagnostic contracts. |
|
|
84
|
+
| AUD-612-B03 | High | closed | YAML cache-hit delivery now publishes `_cache.cache_source` and `timing_ms.served_from_cache` consistently; TeaVM YAML smoke plus the direct cache-hit seam regression pass. A real L2 benchmark remains follow-up because this environment rebuilt both runs. |
|
|
85
|
+
| AUD-612-B04 | High | partial | Each `selftest` row now has configurable 120 s default timeout and structured timeout diagnostics/code 75; valid-row scale latency remains open for profiling. Dedicated TeaVM timeout contract regression passes. |
|
|
86
|
+
| AUD-612-B07 | Medium | closed | `pr-impact --since` now resolves refs through the shared authority and publishes `GIT_REF_NOT_FOUND`; dedicated TeaVM and existing Git-ref regressions pass. |
|
|
87
|
+
| AUD-612-B13 | Medium | closed/no-repro | Controlled TeaVM campaign: 30/30 `retrieve endpoint-security --endpoint /` executions completed with exit 0, valid JSON, no errors and stable content identity after cache warm-up. Reopen on a new correlated failure. |
|
|
88
|
+
| AUD-612-L | High | open | `audit-report --format markdown` timed out at 420s/500s, 2/2, after ~228s in 5.9.6. Performance regression remains open; the one-shot heartbeat sub-issue is mitigated by repeated warnings. |
|
|
89
|
+
| AUD-612-K | Medium | closed | Pack orchestration now streams child stderr, forwarding inherited `ASK_PROGRESS=1` progress while preserving aggregate stdout JSON. Dedicated TeaVM gate regression passes. |
|
|
90
|
+
| AUD-612-M | Medium | closed | `explain` missing-class failures now use structured `INVALID_INPUT` stderr and `exit 1`; dedicated TeaVM regression passes. |
|
|
91
|
+
| AUD-612-E | Medium | closed | The events branch now applies `_exit_on_unresolved_target`; missing event symbols return `not_found` with `exit 1`. TeaVM regression passes. |
|
|
92
|
+
| AUD-612-G | Medium | closed | `review-pr` runtime failures and `activate` failures now publish the shared `error.code` envelope on stderr; stdout remains reserved for valid results. `no_diff` remains the intentional successful `no_changes` result. Dedicated TeaVM and contract tests pass. |
|
|
93
|
+
| AUD-612-I | Medium | closed | Removed the residual `~10K` wording from the large-output warning; help, runtime warning and compact payload consistently describe a snapshot-dependent subset with explicit `full_available` metadata. Dedicated TeaVM regression passes. |
|
|
94
|
+
| AUD-612-07 | Low | closed | Missing single tokens close to a registered command now use command admission with a nearest-command suggestion; existing paths retain implicit root-scan behavior. Dedicated TeaVM regression passes. |
|
|
95
|
+
| AUD-612-09 | Medium | closed | `--files -` is documented and enforced as a newline-delimited changed-path list; raw unified diffs fail with an `--name-only` conversion hint. Dedicated TeaVM regressions pass. |
|
|
96
|
+
| AUD-612-14 | Medium | closed | The current population is 104 CLI leaves versus 110 MCP tools, explicitly decomposed as 101 runtime-derived plus 9 native orchestration tools; `mcp list-tools --json` matches and the relationship is published. Existing TeaVM-backed population regression passes. |
|
|
97
|
+
| AUD-612-R | Medium | closed | README, English guide and Spanish manual document that `rename-class` requires `--from/--to`, the old positional syntax is unsupported, and `--dry-run` is the migration safety step. TeaVM dry-run verification passes. |
|
|
98
|
+
| AUD-613-ROOT | High | closed 5.9.8 | On a TTY, explicit `ask .` was misclassified as bare `ask` after path preprocessing and rendered only the welcome card. An explicit-path marker now distinguishes both flows; TeaVM pseudo-TTY and focused CLI regressions pass. |
|
|
99
|
+
|
|
100
|
+
**External audit intake `AUD-614`, re-audit of release 5.9.8 — 2026-08-27.**
|
|
101
|
+
Five clean forks exercised the `testing/` corpus without target-repository writes,
|
|
102
|
+
commits or Java-file changes. The initial **8.0/10** score was superseded by the
|
|
103
|
+
addendum confirming a second severe availability regression; the governing score is
|
|
104
|
+
**7.5/10**. Previously closed semantic fixes remained stable, but the following rows
|
|
105
|
+
are the current release blockers and contract gaps:
|
|
106
|
+
|
|
107
|
+
| ID | Severity | Status | Evidence / closure boundary |
|
|
108
|
+
|---|---:|---|---|
|
|
109
|
+
| AUD-614-F | Critical | closed 5.9.8 (reverified) | The external `/dev/null` false-success is not reproducible in the current checkout: invalid TeaVM input returns `exit 1` and identical structured stderr whether stdout is captured or discarded. A dedicated redirection regression protects the boundary. Native Windows/MSYS release-artifact verification remains required because the external evidence conflicts. |
|
|
110
|
+
| AUD-614-L | Critical / regression | open | `audit-report` is contention-sensitive on `thingsboard` (3,714 files): one clean run completed in 136 s, while concurrent/isolated runs exceeded 5 minutes; JSON, YAML and Markdown all exhibited the problem. `tutorials` reached 5m39.6s with zero stdout. Requires profiling, stage visibility, deadline and structured partial output. |
|
|
111
|
+
| AUD-614-RISK-L0 | High / regression | closed 5.9.8 | `risk --limit 0` now returns a fast explicit zero-row view without constructing the repository-wide composer. It publishes `risks: []`, `shown: 0`, null totals and `analysis_skipped: true`; the TeaVM flow and dedicated regression pass. |
|
|
112
|
+
| AUD-614-SELFTEST-TIMEOUT | High / contract regression | closed 5.9.8 | `selftest --timeout` is now an overall deadline; each child receives the remaining budget, interrupted/unstarted rows are structured as `error`, and the command returns code 75. The real TeaVM flow and dedicated regression pass. The separate fourth-run hang remains mixed evidence. |
|
|
113
|
+
| AUD-614-WIN-PROC | Medium | open | Windows/MSYS timeout or SIGTERM can leave `ask.exe` descendants alive, with MSYS PIDs unusable by native `taskkill`. Validate signal handling and Windows Job Objects from native Windows and Git Bash. |
|
|
114
|
+
| AUD-614-G | Medium | closed 5.9.8 | The shared error authority normalizes legacy producer spellings to canonical uppercase codes (`NO_DIFF_SOURCE`, `INVALID_LICENSE`). TeaVM `review-pr` and activation contract tests pass. |
|
|
115
|
+
| AUD-614-E | Low | open | Impact commands now agree on unresolved exit status, but their `risk_level` values still diverge (`unknown`/`low`/`unknown`). Define one not-found payload semantics and enforce parity. |
|
|
116
|
+
| AUD-614-I | Medium | closed 5.9.8 | The user manual and internal compacting comment no longer promise `~10K`; they state that `cold-start --compact` is snapshot-dependent, expose `estimated_tokens`, and require size validation before direct AI injection. The CLI help and payload already used this contract. A hard ceiling remains optional future scope. |
|
|
117
|
+
| AUD-614-INTENT-TIE | Medium | open | Exact lexical ties at score `2.8284` still resolve differently for `endpoint security` and `security surface`. Define deterministic tie/margin semantics and add a real-repository regression. |
|
|
118
|
+
| AUD-614-07 | Low | closed 5.9.8 (reverified) | The installed `ask spring-audi` entry point returns `Did you mean: spring-audit?`; the TeaVM path/typo regression battery passes. The external wording report is not reproducible in the current checkout. |
|
|
119
|
+
| AUD-614-JUNIT4 | Medium | open | JUnit4 projects remain `project_type: unknown`; add evidence-backed classification without broad heuristic false positives. |
|
|
120
|
+
|
|
121
|
+
The audit also confirms root cache reuse, stdin contracts, pack progress, explain and
|
|
122
|
+
impact error routing, self-audit population disclosure, endpoint compact disclosure,
|
|
123
|
+
Spring detection controls, baseline safety and the prior `/dev/null` fix matrix. The
|
|
124
|
+
intermittent endpoint-security retrieval issue had 0/18 failures in this pass and stays
|
|
125
|
+
under observation because its historical rate is about 2% over 51 runs.
|
|
126
|
+
|
|
127
|
+
**Commercial boundary.** The corrected assessment is `$25-40/developer/month`, not the
|
|
128
|
+
initial `$45-58` headline. Do not market enterprise-scale support or production CI
|
|
129
|
+
gating for repositories above roughly 3,000 files until the four critical/high
|
|
130
|
+
availability and error-transport rows are independently closed with bounded evidence.
|
|
131
|
+
|
|
132
|
+
Commercially, normal repositories up to about 4,000 files remain viable at a provisional
|
|
133
|
+
$40-55/developer/month. B02, B04 and L block annual CI-gating and extreme-monorepo claims;
|
|
134
|
+
B03, K, M and G weaken machine-consumable evidence.
|
|
135
|
+
|
|
74
136
|
**External audit intake `AUD-610`, release 5.9.3 — 2026-08-27.** The attached
|
|
75
137
|
report scored the build **7.2/10** after five parallel batches and approximately
|
|
76
138
|
120 invocations. It verified a clean target repository, no Java-file changes,
|
sourcecode/_docs/USER_GUIDE.md
CHANGED
|
@@ -46,16 +46,16 @@ 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.
|
|
49
|
+
5.9.9, `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.
|
|
55
|
+
the two pack fixes are included in release 5.9.9, with release-artifact verification
|
|
56
56
|
remaining for detached execution and pack evidence.
|
|
57
57
|
|
|
58
|
-
### Reliability contracts in 5.9.
|
|
58
|
+
### Reliability contracts in 5.9.9
|
|
59
59
|
|
|
60
60
|
Invalid analysis inputs fail before expensive work and use the structured
|
|
61
61
|
`INVALID_INPUT` envelope: a missing `--consumer` repository, a malformed CVE
|
|
@@ -236,7 +236,7 @@ pipx install sourcecode # isolated install, no venv needed
|
|
|
236
236
|
|
|
237
237
|
# Verify
|
|
238
238
|
ask version
|
|
239
|
-
# ask 5.9.
|
|
239
|
+
# ask 5.9.9
|
|
240
240
|
```
|
|
241
241
|
|
|
242
242
|
Requires Python 3.9+.
|
|
@@ -1381,6 +1381,10 @@ explaining. The rest do one thing; `ask <command> --help` is the whole story.
|
|
|
1381
1381
|
| `ask chunk-file <file>` | supported | Split a large Java file into semantic chunks an agent can consume |
|
|
1382
1382
|
| `ask intent "<question>"` | supported | Resolve a plain-language question to the typed `retrieve` query that answers it (scored lexical match against a declared vocabulary — no model, no generation). Prints the query; `--run` executes it. A question fitting two intents too closely is refused with both named. |
|
|
1383
1383
|
| `ask rename-class` | supported | Word-boundary Java rename across the repository |
|
|
1384
|
+
|
|
1385
|
+
**Migration note:** `rename-class` requires the explicit `--from OldName --to NewName`
|
|
1386
|
+
options. Older positional name syntax is no longer accepted. Add `--dry-run` when
|
|
1387
|
+
updating an existing script so the planned file changes can be reviewed first.
|
|
1384
1388
|
| `ask schema <name>` | supported | Print a published JSON Schema for this tool's output |
|
|
1385
1389
|
| `ask self-audit` | supported | Check the SHIPPED build's own coherence — cache model, format contract, schema registry and output affordances. Declares explicitly that the semantic engine does not apply to this product's own source (ASK analyses Java; this build is Python), rather than reporting zeros. `ask selftest` is the different question: the defect ledger run against a real repository. |
|
|
1386
1390
|
| `ask auth workspace <repo>...` | supported | Aggregate workspace entitlement preview. `--enforce` opts into the named-buyer pilot that reuses the existing Pro gate when the organization-sized threshold is crossed; advisory mode remains the default and the CLI does not bill. |
|
sourcecode/cli.py
CHANGED
|
@@ -900,8 +900,8 @@ scope matches; `ask cache model` says exactly what a warm buys per command.
|
|
|
900
900
|
[bold]Change and risk:[/bold]
|
|
901
901
|
impact-chain <Class> . [dim]# blast radius; TX/SEC findings opt-in[/dim]
|
|
902
902
|
impact <Class> . [dim]# reverse deps → endpoints reached[/dim]
|
|
903
|
-
pr-impact . --files - [dim]#
|
|
904
|
-
pr-risk . --files - [dim]#
|
|
903
|
+
pr-impact . --files - [dim]# changed-file list on stdin; gating codes[/dim]
|
|
904
|
+
pr-risk . --files - [dim]# changed-file list on stdin; UNKNOWN fails the gate[/dim]
|
|
905
905
|
posture . --since main [dim]# did this branch open an endpoint? (exp.)[/dim]
|
|
906
906
|
drift . --profile prod [dim]# what opened up since the last capture[/dim]
|
|
907
907
|
audit-report . --format markdown [dim]# signed human evidence bundle (exp.)[/dim]
|
|
@@ -1029,6 +1029,11 @@ def _set_detected_path(value: str) -> None:
|
|
|
1029
1029
|
_tls.detected_path = value
|
|
1030
1030
|
|
|
1031
1031
|
|
|
1032
|
+
def _explicit_path_was_supplied() -> bool:
|
|
1033
|
+
"""Whether argv contained a repository path later removed by preprocessing."""
|
|
1034
|
+
return bool(_tls.__dict__.get("explicit_path", False))
|
|
1035
|
+
|
|
1036
|
+
|
|
1032
1037
|
# Options that take a value token — their next arg must not be treated as a path.
|
|
1033
1038
|
_OPTIONS_WITH_VALUE: frozenset[str] = frozenset({
|
|
1034
1039
|
"--format", "-f",
|
|
@@ -1271,6 +1276,7 @@ def _preprocess_args(args: list[str]) -> list[str]:
|
|
|
1271
1276
|
``_reject_path_before_subcommand``.
|
|
1272
1277
|
"""
|
|
1273
1278
|
result = _normalise_repository_option(list(args))
|
|
1279
|
+
_tls.explicit_path = False
|
|
1274
1280
|
skip_next = False
|
|
1275
1281
|
_path_index: int = -1
|
|
1276
1282
|
for i, arg in enumerate(result):
|
|
@@ -1299,7 +1305,17 @@ def _preprocess_args(args: list[str]) -> list[str]:
|
|
|
1299
1305
|
# got wrong instead of swallowing it and erroring on the next one.
|
|
1300
1306
|
if _has_trailing_nonsubcommand_positional(result, i):
|
|
1301
1307
|
_reject_unknown_command(arg)
|
|
1308
|
+
# A single missing token that is very close to a command is a command
|
|
1309
|
+
# typo, not a repository path. Existing directories retain the implicit
|
|
1310
|
+
# `ask <path>` grammar, including names that resemble commands.
|
|
1311
|
+
if not Path(arg).exists() and "/" not in arg and "\\" not in arg:
|
|
1312
|
+
_command_matches = difflib.get_close_matches(
|
|
1313
|
+
arg, sorted(_SUBCOMMANDS), n=3, cutoff=0.6
|
|
1314
|
+
)
|
|
1315
|
+
if _command_matches:
|
|
1316
|
+
_reject_unknown_command(arg)
|
|
1302
1317
|
_set_detected_path(arg)
|
|
1318
|
+
_tls.explicit_path = True
|
|
1303
1319
|
_path_index = i
|
|
1304
1320
|
if _path_index >= 0:
|
|
1305
1321
|
result.pop(_path_index)
|
|
@@ -1455,6 +1471,56 @@ def _inject_cache_meta(raw: str, meta: dict) -> str:
|
|
|
1455
1471
|
return raw
|
|
1456
1472
|
|
|
1457
1473
|
|
|
1474
|
+
def _inject_cache_meta_yaml(raw: str, meta: dict) -> str:
|
|
1475
|
+
"""Inject the same cache provenance into a YAML answer as into JSON."""
|
|
1476
|
+
try:
|
|
1477
|
+
from io import StringIO
|
|
1478
|
+
from ruamel.yaml import YAML as _YAML
|
|
1479
|
+
|
|
1480
|
+
yaml = _YAML()
|
|
1481
|
+
obj = yaml.load(raw)
|
|
1482
|
+
if isinstance(obj, dict):
|
|
1483
|
+
obj["_cache"] = meta
|
|
1484
|
+
if "cache_source" in meta:
|
|
1485
|
+
obj["cache_source"] = meta["cache_source"]
|
|
1486
|
+
out = StringIO()
|
|
1487
|
+
yaml.dump(obj, out)
|
|
1488
|
+
return out.getvalue()
|
|
1489
|
+
except Exception:
|
|
1490
|
+
pass
|
|
1491
|
+
return raw
|
|
1492
|
+
|
|
1493
|
+
|
|
1494
|
+
def _refresh_delivery_timing_yaml(raw: str, *, served_from_cache: bool, elapsed_ms: float) -> str:
|
|
1495
|
+
"""Refresh cache timing in YAML without routing the machine answer through JSON."""
|
|
1496
|
+
try:
|
|
1497
|
+
from io import StringIO
|
|
1498
|
+
from ruamel.yaml import YAML as _YAML
|
|
1499
|
+
|
|
1500
|
+
yaml = _YAML()
|
|
1501
|
+
obj = yaml.load(raw)
|
|
1502
|
+
if not isinstance(obj, dict):
|
|
1503
|
+
return raw
|
|
1504
|
+
meta = obj.setdefault("_meta", {})
|
|
1505
|
+
if not isinstance(meta, dict):
|
|
1506
|
+
return raw
|
|
1507
|
+
timing = meta.setdefault("timing_ms", {})
|
|
1508
|
+
if not isinstance(timing, dict):
|
|
1509
|
+
timing = {}
|
|
1510
|
+
meta["timing_ms"] = timing
|
|
1511
|
+
timing["total_ms"] = round(elapsed_ms, 2)
|
|
1512
|
+
timing["served_from_cache"] = served_from_cache
|
|
1513
|
+
timing["timing_basis"] = "served_from_snapshot" if served_from_cache else "computed"
|
|
1514
|
+
if served_from_cache:
|
|
1515
|
+
timing["phase_timing_basis"] = "snapshot_build"
|
|
1516
|
+
out = StringIO()
|
|
1517
|
+
yaml.dump(obj, out)
|
|
1518
|
+
return out.getvalue()
|
|
1519
|
+
except Exception:
|
|
1520
|
+
pass
|
|
1521
|
+
return raw
|
|
1522
|
+
|
|
1523
|
+
|
|
1458
1524
|
def _refresh_delivery_timing(
|
|
1459
1525
|
raw: str,
|
|
1460
1526
|
*,
|
|
@@ -3557,7 +3623,17 @@ def _resolve_files_option(
|
|
|
3557
3623
|
),
|
|
3558
3624
|
)
|
|
3559
3625
|
raise typer.Exit(code=1)
|
|
3560
|
-
|
|
3626
|
+
stdin_text = _text_input.read_stdin_text()
|
|
3627
|
+
if "diff --git " in stdin_text or "\n@@ " in stdin_text:
|
|
3628
|
+
_emit_error_json(
|
|
3629
|
+
INVALID_INPUT_CODE,
|
|
3630
|
+
"--files - expects a list of changed paths, not a raw unified diff.",
|
|
3631
|
+
hint=f"Pipe paths with: git diff --name-only REF | ask {command} . --files -",
|
|
3632
|
+
expected="One repository-relative file path per line.",
|
|
3633
|
+
reason="raw_diff_on_stdin",
|
|
3634
|
+
)
|
|
3635
|
+
raise typer.Exit(code=1)
|
|
3636
|
+
return _text_input.parse_path_lines(stdin_text), "stdin"
|
|
3561
3637
|
as_path = Path(raw)
|
|
3562
3638
|
if as_path.is_file():
|
|
3563
3639
|
return (
|
|
@@ -3587,6 +3663,20 @@ def _resolve_files_option(
|
|
|
3587
3663
|
def _git_changed_files(root: Path, since: Optional[str]) -> "tuple[list[str], str]":
|
|
3588
3664
|
"""Resolve a local Git diff without network access or repository mutation."""
|
|
3589
3665
|
if since:
|
|
3666
|
+
# Resolve the ref through the shared authority before asking Git for the
|
|
3667
|
+
# diff, so every consumer publishes the canonical ref error code.
|
|
3668
|
+
from sourcecode.git_checkout import RefError, resolve_ref
|
|
3669
|
+
|
|
3670
|
+
try:
|
|
3671
|
+
resolve_ref(root, since)
|
|
3672
|
+
except RefError as exc:
|
|
3673
|
+
_emit_error_json(
|
|
3674
|
+
exc.code,
|
|
3675
|
+
exc.message,
|
|
3676
|
+
hint="Check the repository and ref, or pass --files explicitly.",
|
|
3677
|
+
ref=since,
|
|
3678
|
+
)
|
|
3679
|
+
raise typer.Exit(code=1)
|
|
3590
3680
|
command = ["git", "diff", "--name-only", since, "HEAD"]
|
|
3591
3681
|
description = f"git diff {since} HEAD"
|
|
3592
3682
|
else:
|
|
@@ -5244,7 +5334,7 @@ def main(
|
|
|
5244
5334
|
# Bare invocation at a human terminal → branded quickstart instead of
|
|
5245
5335
|
# dumping a large JSON blob. Agents/pipes are untouched: any arg/flag, or a
|
|
5246
5336
|
# non-TTY stdout (piped), falls through to the normal analysis + JSON.
|
|
5247
|
-
if len(sys.argv) <= 1 and sys.stdout.isatty():
|
|
5337
|
+
if len(sys.argv) <= 1 and not _explicit_path_was_supplied() and sys.stdout.isatty():
|
|
5248
5338
|
_print_welcome()
|
|
5249
5339
|
raise typer.Exit()
|
|
5250
5340
|
|
|
@@ -6054,6 +6144,26 @@ def main(
|
|
|
6054
6144
|
)
|
|
6055
6145
|
except Exception:
|
|
6056
6146
|
pass # stale value better than crash
|
|
6147
|
+
else:
|
|
6148
|
+
_cache_hit_content = _inject_cache_meta_yaml(
|
|
6149
|
+
_cache_hit_content,
|
|
6150
|
+
_cache_meta_block(
|
|
6151
|
+
cache_source=_hit_source,
|
|
6152
|
+
git_head_at_generation=_git_sha,
|
|
6153
|
+
current_git_head=_git_sha,
|
|
6154
|
+
has_uncommitted_changes=False,
|
|
6155
|
+
has_uncommitted_changes_basis="YAML cache delivery",
|
|
6156
|
+
generated_at=None,
|
|
6157
|
+
data_scope=("COMPACT" if compact else ("AGENT" if agent else "FULL")),
|
|
6158
|
+
target=target,
|
|
6159
|
+
with_ris_fact=True,
|
|
6160
|
+
),
|
|
6161
|
+
)
|
|
6162
|
+
_cache_hit_content = _refresh_delivery_timing_yaml(
|
|
6163
|
+
_cache_hit_content,
|
|
6164
|
+
served_from_cache=True,
|
|
6165
|
+
elapsed_ms=(time.monotonic() - _t0) * 1000,
|
|
6166
|
+
)
|
|
6057
6167
|
# C1: token economy on the agent-facing views (cache-hit path).
|
|
6058
6168
|
if format == "json" and (compact or agent):
|
|
6059
6169
|
from sourcecode.token_estimate import inject_token_economy as _inj_te
|
|
@@ -7828,31 +7938,33 @@ def prepare_context_cmd(
|
|
|
7828
7938
|
# review-pr specific fields
|
|
7829
7939
|
if task == "review-pr":
|
|
7830
7940
|
if output.error_code:
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7941
|
+
if output.error_code == "no_diff":
|
|
7942
|
+
_no_diff_out = {
|
|
7943
|
+
"task": output.task,
|
|
7944
|
+
"ci_decision": "no_changes",
|
|
7945
|
+
"summary": "No changes detected",
|
|
7946
|
+
}
|
|
7947
|
+
_no_diff_json = _stamp_envelope(
|
|
7948
|
+
json.dumps(_no_diff_out, indent=2, ensure_ascii=_json_ensure_ascii())
|
|
7949
|
+
)
|
|
7950
|
+
if output_path is not None:
|
|
7951
|
+
_safe_write_file(output_path, _no_diff_json)
|
|
7952
|
+
else:
|
|
7953
|
+
sys.stdout.buffer.write(_no_diff_json.encode("utf-8"))
|
|
7954
|
+
sys.stdout.buffer.write(b"\n")
|
|
7955
|
+
sys.stdout.buffer.flush()
|
|
7956
|
+
raise typer.Exit()
|
|
7957
|
+
_error_hint = " ".join(str(h) for h in (output.error_hints or []))
|
|
7958
|
+
_emit_error_json(
|
|
7959
|
+
output.error_code,
|
|
7960
|
+
output.error_message or "review-pr could not complete.",
|
|
7961
|
+
command="review-pr",
|
|
7962
|
+
ci_decision=output.ci_decision or "error",
|
|
7963
|
+
since=output.since or None,
|
|
7964
|
+
hint=_error_hint or None,
|
|
7965
|
+
hints=output.error_hints or None,
|
|
7845
7966
|
)
|
|
7846
|
-
|
|
7847
|
-
_safe_write_file(output_path, _err_json)
|
|
7848
|
-
else:
|
|
7849
|
-
sys.stdout.buffer.write(_err_json.encode("utf-8"))
|
|
7850
|
-
sys.stdout.buffer.write(b"\n")
|
|
7851
|
-
sys.stdout.buffer.flush()
|
|
7852
|
-
# FIX: no_diff (no PR changes) is not an error — exit 0, consistent
|
|
7853
|
-
# with delta's no_changes handling. Only true errors exit non-zero.
|
|
7854
|
-
_review_pr_exit = 0 if output.error_code == "no_diff" else 1
|
|
7855
|
-
raise typer.Exit(code=_review_pr_exit)
|
|
7967
|
+
raise typer.Exit(code=1)
|
|
7856
7968
|
out["review_type"] = "pull_request"
|
|
7857
7969
|
if output.ci_decision:
|
|
7858
7970
|
out["ci_decision"] = output.ci_decision
|
|
@@ -13288,7 +13400,7 @@ def risk_cmd(
|
|
|
13288
13400
|
"""
|
|
13289
13401
|
_require_answer_delivery("risk")
|
|
13290
13402
|
_apply_jobs(jobs)
|
|
13291
|
-
from sourcecode.risk import build_risk
|
|
13403
|
+
from sourcecode.risk import build_risk, zero_risk_payload
|
|
13292
13404
|
|
|
13293
13405
|
path = _admit_path(path)
|
|
13294
13406
|
# B18: both spellings of the same fact, resolved by the crosswalk the report
|
|
@@ -13328,6 +13440,19 @@ def risk_cmd(
|
|
|
13328
13440
|
raise typer.Exit(code=1)
|
|
13329
13441
|
band_filter = _resolved_band
|
|
13330
13442
|
|
|
13443
|
+
# AUD-614-RISK-L0: a zero-row view must not construct the full repository-wide
|
|
13444
|
+
# composer only to discard every result. The payload says totals were skipped,
|
|
13445
|
+
# rather than turning an empty requested view into a false zero census.
|
|
13446
|
+
if limit == 0 and not table and not rule_filter and band_filter is None and not top_n:
|
|
13447
|
+
data = zero_risk_payload(
|
|
13448
|
+
path, min_band=min_band, profiles=_profile_set(profile),
|
|
13449
|
+
)
|
|
13450
|
+
_emit_command_output(
|
|
13451
|
+
_serialize_dict(data, format), output_path, copy,
|
|
13452
|
+
success_msg=f"risk written to {output_path} (0 rows requested)",
|
|
13453
|
+
)
|
|
13454
|
+
return
|
|
13455
|
+
|
|
13331
13456
|
# `risk` composes six axes by re-reading five other commands' authorities — it
|
|
13332
13457
|
# is the slowest command in the CLI (15s on BroadleafCommerce) and shipped
|
|
13333
13458
|
# without a single byte on stderr while it ran.
|
|
@@ -15675,6 +15800,7 @@ def impact_chain_cmd(
|
|
|
15675
15800
|
f"{evt_result.metadata.get('consumer_count', 0)} consumers)"
|
|
15676
15801
|
),
|
|
15677
15802
|
)
|
|
15803
|
+
_exit_on_unresolved_target(evt_result.resolution)
|
|
15678
15804
|
return
|
|
15679
15805
|
|
|
15680
15806
|
result = run_impact_chain(
|
|
@@ -16051,6 +16177,15 @@ def explain_cmd(
|
|
|
16051
16177
|
else:
|
|
16052
16178
|
output = explanation.render_text()
|
|
16053
16179
|
|
|
16180
|
+
if not explanation.found:
|
|
16181
|
+
_emit_error_json(
|
|
16182
|
+
INVALID_INPUT_CODE,
|
|
16183
|
+
f"Class '{class_name}' was not found in repository symbols.",
|
|
16184
|
+
hint="Pass an existing class name or FQN; use `ask repo-ir` for the symbol list.",
|
|
16185
|
+
expected="A class present in the analyzed repository.",
|
|
16186
|
+
)
|
|
16187
|
+
raise typer.Exit(code=1)
|
|
16188
|
+
|
|
16054
16189
|
_emit_command_output(output, output_path, copy,
|
|
16055
16190
|
success_msg=f"Explanation written to {output_path}")
|
|
16056
16191
|
|
|
@@ -18059,6 +18194,10 @@ def selftest_cmd(
|
|
|
18059
18194
|
"checkout's runner). Published with every verdict — the difference "
|
|
18060
18195
|
"ASK-09 hid in was the entry point, not the repository.",
|
|
18061
18196
|
),
|
|
18197
|
+
timeout: int = typer.Option(
|
|
18198
|
+
120, "--timeout", min=1,
|
|
18199
|
+
help="Maximum seconds allowed for each ledger row (default: 120).",
|
|
18200
|
+
),
|
|
18062
18201
|
output_path: Optional[Path] = typer.Option(
|
|
18063
18202
|
None, "--output", "-o", help="Write the verdicts to a file instead of stdout.",
|
|
18064
18203
|
),
|
|
@@ -18111,7 +18250,9 @@ def selftest_cmd(
|
|
|
18111
18250
|
_prog = Progress()
|
|
18112
18251
|
_prog.start("running the ledger against this repository")
|
|
18113
18252
|
try:
|
|
18114
|
-
data = run_selftest(
|
|
18253
|
+
data = run_selftest(
|
|
18254
|
+
target, rows=selected, entry_point=entry_point, timeout=timeout
|
|
18255
|
+
)
|
|
18115
18256
|
finally:
|
|
18116
18257
|
_prog.stop()
|
|
18117
18258
|
|
|
@@ -19362,7 +19503,8 @@ def cold_start_cmd(
|
|
|
19362
19503
|
finally:
|
|
19363
19504
|
_prog.stop()
|
|
19364
19505
|
if compact:
|
|
19365
|
-
#
|
|
19506
|
+
# Keep the fields that orient a reader; the resulting size depends on
|
|
19507
|
+
# the stored snapshot and is not a fixed token ceiling.
|
|
19366
19508
|
#
|
|
19367
19509
|
# B14. This used to be a fixed key allowlist and a silent `[:30]`, and it
|
|
19368
19510
|
# took 1 968 807 characters to 16 452 (−99,2 %) while removing 11 of 19
|
|
@@ -19413,7 +19555,7 @@ def cold_start_cmd(
|
|
|
19413
19555
|
_notice(
|
|
19414
19556
|
f"WARNING: Output is ~{_tokens // 1000}K tokens. This exceeds the context window of "
|
|
19415
19557
|
"most LLMs (GPT-4o: 128K, Claude Sonnet: 200K). "
|
|
19416
|
-
"Use --compact for a
|
|
19558
|
+
"Use --compact for a snapshot-dependent orientation subset, or --output FILE to save."
|
|
19417
19559
|
)
|
|
19418
19560
|
# AUD-596-D01: this payload builds its own `_meta` above (estimated tokens,
|
|
19419
19561
|
# token economy) and used to skip the envelope for that reason — so the one
|
sourcecode/error_schema.py
CHANGED
|
@@ -8,6 +8,19 @@ INVALID_USAGE_CODE = "INVALID_USAGE"
|
|
|
8
8
|
EXECUTION_FAILED_CODE = "EXECUTION_FAILED"
|
|
9
9
|
INTERNAL_ERROR_CODE = "INTERNAL_ERROR"
|
|
10
10
|
|
|
11
|
+
# Domain-specific producers historically used snake_case while the public error
|
|
12
|
+
# contract uses canonical uppercase identifiers. Keep the normalization here so
|
|
13
|
+
# every CLI surface that builds the shared envelope agrees.
|
|
14
|
+
_CANONICAL_CODE_ALIASES = {
|
|
15
|
+
"no_diff_source": "NO_DIFF_SOURCE",
|
|
16
|
+
"invalid_license": "INVALID_LICENSE",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def canonical_error_code(code: str) -> str:
|
|
21
|
+
"""Return the public spelling for a producer's error code."""
|
|
22
|
+
return _CANONICAL_CODE_ALIASES.get(code, code)
|
|
23
|
+
|
|
11
24
|
#: The exit code each error class leaves with — the other half of `AUD-596-D11`.
|
|
12
25
|
#:
|
|
13
26
|
#: That row closed the *code* half: `INVALID_INPUT` used to exit 1 or 2 depending
|
|
@@ -59,6 +72,7 @@ def build_error_object(
|
|
|
59
72
|
hint: str | None = None,
|
|
60
73
|
expected: str | None = None,
|
|
61
74
|
) -> dict[str, str]:
|
|
75
|
+
code = canonical_error_code(code)
|
|
62
76
|
return {
|
|
63
77
|
"code": code,
|
|
64
78
|
"message": message,
|
sourcecode/license.py
CHANGED
|
@@ -715,8 +715,8 @@ def require_pro(feature_name: str) -> None:
|
|
|
715
715
|
def activate_license(license_key: str) -> None:
|
|
716
716
|
"""Validate license_key via Edge Function, write ~/.sourcecode/license.json.
|
|
717
717
|
|
|
718
|
-
Outputs JSON to stdout; exits 0 on success, 1 on any failure.
|
|
719
|
-
|
|
718
|
+
Outputs JSON to stdout on success; exits 0 on success, 1 on any failure.
|
|
719
|
+
Errors use the CLI JSON envelope on stderr so stdout remains machine-safe.
|
|
720
720
|
"""
|
|
721
721
|
if not _LICENSE_KEY_RE.match(license_key):
|
|
722
722
|
_fail("invalid_license", "License key format is invalid.")
|
|
@@ -760,8 +760,10 @@ def activate_license(license_key: str) -> None:
|
|
|
760
760
|
# ---------------------------------------------------------------------------
|
|
761
761
|
|
|
762
762
|
def _fail(error: str, message: str) -> None:
|
|
763
|
-
"""Emit
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
763
|
+
"""Emit the shared structured error envelope to stderr and exit 1."""
|
|
764
|
+
from sourcecode.error_schema import build_error_envelope
|
|
765
|
+
|
|
766
|
+
payload = build_error_envelope(error, message, command="activate")
|
|
767
|
+
sys.stderr.write(json.dumps(payload, ensure_ascii=False) + "\n")
|
|
768
|
+
sys.stderr.flush()
|
|
767
769
|
sys.exit(1)
|
sourcecode/packs.py
CHANGED
|
@@ -22,6 +22,7 @@ import json
|
|
|
22
22
|
import subprocess
|
|
23
23
|
import sys
|
|
24
24
|
import tempfile
|
|
25
|
+
import threading
|
|
25
26
|
import time
|
|
26
27
|
from pathlib import Path
|
|
27
28
|
from typing import Any, Callable, Optional, Sequence
|
|
@@ -681,9 +682,29 @@ def _run_command(command: str, root: Path, output: Path, extra: list[str]) -> di
|
|
|
681
682
|
# Run from the repository so relative deployment inputs and build-tool
|
|
682
683
|
# discovery have the same meaning as they do for a user invoking ASK there.
|
|
683
684
|
started = time.monotonic()
|
|
684
|
-
|
|
685
|
-
argv,
|
|
685
|
+
process = subprocess.Popen(
|
|
686
|
+
argv,
|
|
687
|
+
stdout=subprocess.PIPE,
|
|
688
|
+
stderr=subprocess.PIPE,
|
|
689
|
+
text=True,
|
|
690
|
+
cwd=str(root),
|
|
686
691
|
)
|
|
692
|
+
stderr_lines: list[str] = []
|
|
693
|
+
|
|
694
|
+
def _forward_stderr() -> None:
|
|
695
|
+
assert process.stderr is not None
|
|
696
|
+
for line in process.stderr:
|
|
697
|
+
stderr_lines.append(line)
|
|
698
|
+
# Child diagnostics are operational progress/errors, never pack data.
|
|
699
|
+
sys.stderr.write(line)
|
|
700
|
+
sys.stderr.flush()
|
|
701
|
+
|
|
702
|
+
stderr_thread = threading.Thread(target=_forward_stderr, daemon=True)
|
|
703
|
+
stderr_thread.start()
|
|
704
|
+
assert process.stdout is not None
|
|
705
|
+
stdout_text = process.stdout.read()
|
|
706
|
+
process.wait()
|
|
707
|
+
stderr_thread.join(timeout=1)
|
|
687
708
|
duration_ms = int((time.monotonic() - started) * 1000)
|
|
688
709
|
payload: Any = None
|
|
689
710
|
if output.is_file():
|
|
@@ -694,9 +715,9 @@ def _run_command(command: str, root: Path, output: Path, extra: list[str]) -> di
|
|
|
694
715
|
return {
|
|
695
716
|
"command": command,
|
|
696
717
|
"invocation": ["ask", command, *extra, str(root), "--format", "json"],
|
|
697
|
-
"exit_code":
|
|
718
|
+
"exit_code": process.returncode,
|
|
698
719
|
"payload": payload,
|
|
699
|
-
"stderr":
|
|
720
|
+
"stderr": "".join(stderr_lines)[-4000:] if stderr_lines else None,
|
|
700
721
|
# AUD-600-D02: the clock the manifest was missing, taken where the
|
|
701
722
|
# component actually runs.
|
|
702
723
|
"duration_ms": duration_ms,
|
sourcecode/progress.py
CHANGED
|
@@ -523,13 +523,15 @@ class Progress:
|
|
|
523
523
|
|
|
524
524
|
def _slow_warning_loop(self) -> None:
|
|
525
525
|
"""Give non-interactive supervisors a delayed liveness signal only."""
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
526
|
+
while not self._stop_event.wait(timeout=self._slow_warning_after):
|
|
527
|
+
if self._stopped:
|
|
528
|
+
return
|
|
529
|
+
try:
|
|
530
|
+
elapsed = time.monotonic() - self._t0
|
|
531
|
+
self._write(
|
|
532
|
+
f"[ask] warning: run has exceeded {_duration(elapsed)}; "
|
|
533
|
+
"still working (set --progress stderr for periodic heartbeats)\n"
|
|
534
|
+
)
|
|
535
|
+
self._slow_warning_emitted = True
|
|
536
|
+
except Exception:
|
|
537
|
+
pass
|
sourcecode/risk.py
CHANGED
|
@@ -50,6 +50,67 @@ if TYPE_CHECKING:
|
|
|
50
50
|
RISK_SCHEMA = "risk-v1"
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
def zero_risk_payload(
|
|
54
|
+
root: Path,
|
|
55
|
+
*,
|
|
56
|
+
min_band: str,
|
|
57
|
+
profiles: "Optional[set[str]]" = None,
|
|
58
|
+
) -> dict:
|
|
59
|
+
"""Return an honest zero-row view without paying for repository analysis.
|
|
60
|
+
|
|
61
|
+
`--limit 0` asks for no risk rows. Running the full composer and then slicing
|
|
62
|
+
to zero paid the repository-wide analysis cost while producing no evidence.
|
|
63
|
+
Totals are therefore explicitly unknown rather than falsely reported as zero.
|
|
64
|
+
"""
|
|
65
|
+
from sourcecode import non_coverage
|
|
66
|
+
from sourcecode.provenance import build_evidence_manifest
|
|
67
|
+
|
|
68
|
+
payload: dict[str, Any] = {
|
|
69
|
+
"schema_version": RISK_SCHEMA,
|
|
70
|
+
"model": RISK_MODEL,
|
|
71
|
+
"status": "zero_row_view",
|
|
72
|
+
"note": (
|
|
73
|
+
"No risk rows were requested (`--limit 0`), so repository analysis was "
|
|
74
|
+
"skipped. `risks: []` means no rows were requested, not that the "
|
|
75
|
+
"repository has no defects. Re-run without zero limit to measure totals."
|
|
76
|
+
),
|
|
77
|
+
"total_defects": None,
|
|
78
|
+
"total_findings": None,
|
|
79
|
+
"composition_working_set": {
|
|
80
|
+
"source_files": None,
|
|
81
|
+
"audit_findings": None,
|
|
82
|
+
"candidate_defects": None,
|
|
83
|
+
"defects_composed": 0,
|
|
84
|
+
"basis": "analysis skipped because the requested row limit was zero",
|
|
85
|
+
},
|
|
86
|
+
"counts_note": (
|
|
87
|
+
"Totals are null because no repository analysis ran; risks is an empty "
|
|
88
|
+
"requested view, not a finding census."
|
|
89
|
+
),
|
|
90
|
+
"shown": 0,
|
|
91
|
+
"by_band": {},
|
|
92
|
+
"by_tier": {},
|
|
93
|
+
"risks": [],
|
|
94
|
+
"limit": 0,
|
|
95
|
+
"min_band": min_band,
|
|
96
|
+
"non_coverage": non_coverage.block("security_surface"),
|
|
97
|
+
"zero_row_view": {
|
|
98
|
+
"requested_limit": 0,
|
|
99
|
+
"analysis_skipped": True,
|
|
100
|
+
"rerun": "ask risk <repository> without --limit 0",
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
if profiles is not None:
|
|
104
|
+
payload["profile_set"] = sorted(profiles)
|
|
105
|
+
payload["evidence_manifest"] = build_evidence_manifest(
|
|
106
|
+
payload,
|
|
107
|
+
command="risk",
|
|
108
|
+
root=root,
|
|
109
|
+
inputs={"limit": 0, "min_band": min_band, "profiles": sorted(profiles) if profiles else None},
|
|
110
|
+
)
|
|
111
|
+
return payload
|
|
112
|
+
|
|
113
|
+
|
|
53
114
|
#: What a bounded row keeps, in one place, so the view and the declaration of the
|
|
54
115
|
#: view cannot be edited apart.
|
|
55
116
|
#:
|
sourcecode/selftest.py
CHANGED
|
@@ -87,7 +87,7 @@ class Subject:
|
|
|
87
87
|
entry_point: str
|
|
88
88
|
java_files: int = 0
|
|
89
89
|
platform: str = ""
|
|
90
|
-
timeout:
|
|
90
|
+
timeout: float = 900
|
|
91
91
|
|
|
92
92
|
def run(
|
|
93
93
|
self, *args: str, sanitize_environment: bool = False
|
|
@@ -872,6 +872,7 @@ def run_selftest(
|
|
|
872
872
|
*,
|
|
873
873
|
rows: "tuple[str, ...]" = (),
|
|
874
874
|
entry_point: "str | None" = None,
|
|
875
|
+
timeout: int = 120,
|
|
875
876
|
) -> "dict[str, Any]":
|
|
876
877
|
"""Run the ledger's assertions against *repo* and publish the verdicts."""
|
|
877
878
|
from sourcecode.repository_ir import find_java_files
|
|
@@ -881,7 +882,9 @@ def run_selftest(
|
|
|
881
882
|
entry_point=entry_point or _entry_point(),
|
|
882
883
|
platform=f"{_platform_label()} / python "
|
|
883
884
|
f"{_platform.python_version()}",
|
|
885
|
+
timeout=float(timeout),
|
|
884
886
|
)
|
|
887
|
+
deadline = time.monotonic() + float(timeout)
|
|
885
888
|
try:
|
|
886
889
|
subject.java_files = len(find_java_files(Path(repo)))
|
|
887
890
|
except Exception:
|
|
@@ -892,12 +895,51 @@ def run_selftest(
|
|
|
892
895
|
unknown = sorted(wanted - {row.id.upper() for row in ROWS})
|
|
893
896
|
|
|
894
897
|
results: "list[dict[str, Any]]" = []
|
|
895
|
-
for row in selected:
|
|
898
|
+
for index, row in enumerate(selected):
|
|
896
899
|
started = time.perf_counter()
|
|
900
|
+
remaining = deadline - time.monotonic()
|
|
901
|
+
if remaining <= 0:
|
|
902
|
+
outcome = _result(
|
|
903
|
+
ERROR,
|
|
904
|
+
f"the check did not start before the overall {timeout}s deadline",
|
|
905
|
+
)
|
|
906
|
+
results.append({
|
|
907
|
+
"row": row.id,
|
|
908
|
+
"what": row.what,
|
|
909
|
+
"assertion": row.assertion,
|
|
910
|
+
"seconds": round(time.perf_counter() - started, 2),
|
|
911
|
+
**outcome,
|
|
912
|
+
})
|
|
913
|
+
for pending in selected[index + 1:]:
|
|
914
|
+
results.append({
|
|
915
|
+
"row": pending.id,
|
|
916
|
+
"what": pending.what,
|
|
917
|
+
"assertion": pending.assertion,
|
|
918
|
+
"seconds": 0.0,
|
|
919
|
+
**_result(ERROR, f"not started: overall {timeout}s deadline exhausted"),
|
|
920
|
+
})
|
|
921
|
+
break
|
|
922
|
+
subject.timeout = remaining
|
|
897
923
|
try:
|
|
898
924
|
outcome = row.check(subject)
|
|
899
925
|
except subprocess.TimeoutExpired:
|
|
900
926
|
outcome = _result(ERROR, f"the check did not finish in {subject.timeout}s")
|
|
927
|
+
results.append({
|
|
928
|
+
"row": row.id,
|
|
929
|
+
"what": row.what,
|
|
930
|
+
"assertion": row.assertion,
|
|
931
|
+
"seconds": round(time.perf_counter() - started, 2),
|
|
932
|
+
**outcome,
|
|
933
|
+
})
|
|
934
|
+
for pending in selected[index + 1:]:
|
|
935
|
+
results.append({
|
|
936
|
+
"row": pending.id,
|
|
937
|
+
"what": pending.what,
|
|
938
|
+
"assertion": pending.assertion,
|
|
939
|
+
"seconds": 0.0,
|
|
940
|
+
**_result(ERROR, f"not started: overall {timeout}s deadline exhausted"),
|
|
941
|
+
})
|
|
942
|
+
break
|
|
901
943
|
except Exception as exc: # a broken check is not a passing row
|
|
902
944
|
outcome = _result(ERROR, f"{type(exc).__name__}: {exc}")
|
|
903
945
|
results.append({
|
|
@@ -907,6 +949,7 @@ def run_selftest(
|
|
|
907
949
|
"seconds": round(time.perf_counter() - started, 2),
|
|
908
950
|
**outcome,
|
|
909
951
|
})
|
|
952
|
+
subject.timeout = float(timeout)
|
|
910
953
|
|
|
911
954
|
counts = {status: 0 for status in (PASS, FAIL, NOT_APPLICABLE, ERROR)}
|
|
912
955
|
for result in results:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 5.9.
|
|
3
|
+
Version: 5.9.9
|
|
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
|
-

|
|
63
63
|

|
|
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.
|
|
199
|
+
**Current performance status.** Release 5.9.9 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,7 +218,7 @@ 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.
|
|
221
|
+
Release 5.9.9 also carries the boundary-contract hardening: missing consumer repositories,
|
|
222
222
|
malformed CVE feeds and unresolved `pack gate --since` refs fail as structured
|
|
223
223
|
input errors; duplicate same-commit baselines are never overwritten. `posture
|
|
224
224
|
--diff` validates unknown profiles when the repository declares profiles. In
|
|
@@ -251,7 +251,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
251
251
|
# pip / pipx
|
|
252
252
|
pipx install sourcecode # or: pip install sourcecode
|
|
253
253
|
|
|
254
|
-
ask version # ask 5.9.
|
|
254
|
+
ask version # ask 5.9.9 — and, on a build that has aged,
|
|
255
255
|
# how many releases have probably shipped since
|
|
256
256
|
```
|
|
257
257
|
|
|
@@ -501,6 +501,10 @@ The everyday loop: diff-based PR review, symptom-driven bug triage, and delta co
|
|
|
501
501
|
|
|
502
502
|
### 7 · Utilities
|
|
503
503
|
`ask rename-class` (word-boundary Java rename) · `ask chunk-file` (split large files for agents) · `ask cache` (status / warm / model / clear / freshness) · `ask self-audit` (does the installed build's own registry agree with itself?)
|
|
504
|
+
|
|
505
|
+
`rename-class` migration note: current releases require the explicit form
|
|
506
|
+
`ask rename-class <repo> --from OldName --to NewName`. Older positional forms are
|
|
507
|
+
not accepted; use `--dry-run` first when migrating scripts.
|
|
504
508
|
→ [reference](docs/USER_GUIDE.md)
|
|
505
509
|
|
|
506
510
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=TXUdRcR-lIVuPA34D4Cvw3nLwLIzsk1-nyQ8bLeS6AM,308
|
|
2
2
|
sourcecode/__main__.py,sha256=qDFVkobwsdZrX0B3stiRqsUNivVpybKO-egzAwCW4xw,165
|
|
3
|
-
sourcecode/_build_commit.py,sha256=
|
|
3
|
+
sourcecode/_build_commit.py,sha256=yGp-IrT0vxNwHnCuNvgYKtp_TTcmGqyszcIcHVpS10I,10274
|
|
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
|
|
@@ -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=
|
|
38
|
+
sourcecode/cli.py,sha256=X_gCZrrE_bQuCntOjE9rwzvjXirkdZ5ZPbmQjVhMvaw,943965
|
|
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
|
|
@@ -72,7 +72,7 @@ sourcecode/entrypoint_classifier.py,sha256=jhTYlyqDJH2AtdEcLVaRU3lYRTJuF8DkxVzl4
|
|
|
72
72
|
sourcecode/env_analyzer.py,sha256=tU2CcW-Lkl__60DMd4ibYnXnxfmPbV5Z9GrToSWLdSA,22029
|
|
73
73
|
sourcecode/envelope.py,sha256=rISj4NXJZTX0scP7O01fmxl6xnCr_D_c482LkpPzVc0,17407
|
|
74
74
|
sourcecode/environment_resolution.py,sha256=e86HT3RNbIIUIRChvz1Pu9Um81UvYu22BkpHNnwbUQI,30650
|
|
75
|
-
sourcecode/error_schema.py,sha256=
|
|
75
|
+
sourcecode/error_schema.py,sha256=DhxsIvpzAbEzluWFE4IWXYRyccECcJkq2mT2KJB5Tew,3485
|
|
76
76
|
sourcecode/evidence_provider.py,sha256=GSSL44JEaouO5AHks2sB3d1YvC9xIKIld1yBYxZpXxo,4277
|
|
77
77
|
sourcecode/execution_plan.py,sha256=R18LdPHnyfu8YMURwhs4PDUqpWFUVxJHgtb8bfg031k,30130
|
|
78
78
|
sourcecode/explain.py,sha256=soWg-KevnESW9UIBCLTMJOnpR3U0fQi5vwbJnh7xr1c,34744
|
|
@@ -96,7 +96,7 @@ sourcecode/identity_fallback.py,sha256=uRC_j_80uaI4Ue4tf-OhKX4h472dmufYcjEFGSxY2
|
|
|
96
96
|
sourcecode/integration_coordinates.py,sha256=rvuApnS7WdRUbeQLg_oKj-Gi0koO2RKdw6HenXNgrDo,12443
|
|
97
97
|
sourcecode/intent_router.py,sha256=hC_o0VYfSjzSXecFkpiD7vzFgvt44YAw6E_jrMeGiZ4,15555
|
|
98
98
|
sourcecode/jdk_exports.py,sha256=fCrlwNAXUT9gge_joq6kMnY3zJxYB2pxqy-0w3o3MJI,874
|
|
99
|
-
sourcecode/license.py,sha256=
|
|
99
|
+
sourcecode/license.py,sha256=eIEuIVQXywl_S9LOIACOEpJ4M5RyaPvWrqg_jSNOrXA,31088
|
|
100
100
|
sourcecode/manifest_cache.py,sha256=aPRHUs781d00fVbtlTiSaq0-AS9bAqS7_zJ0snZvlJs,2244
|
|
101
101
|
sourcecode/mcp_nudge.py,sha256=beNjfyb7z09T1rnS4t6Q4qfbLzZQNaP6ddCA4XpmV0s,3256
|
|
102
102
|
sourcecode/metrics_analyzer.py,sha256=gLoRWKygF18jLgwsqmGXSWopw-f5iO1VbM7Jjdif6ag,22809
|
|
@@ -112,7 +112,7 @@ sourcecode/output_ceiling.py,sha256=rMLDHbXeC6C_uP8iQSVbbCGiJ_n0gMr2YI9zt5nltFA,
|
|
|
112
112
|
sourcecode/output_encoding.py,sha256=OxiZQtr7uWBDUtblNQviNHsTH1Xo9AZEpsegPlQWKFk,2139
|
|
113
113
|
sourcecode/output_sink.py,sha256=Eya-PnXxqc3RGulJoLhuC9LUF73SZD0aO5O2B0xAMXU,8762
|
|
114
114
|
sourcecode/pack_manifest.py,sha256=Qm6et6SQsFCLc-HJ-Uhz1XlcOa77ZDHgks4JVDEENss,20820
|
|
115
|
-
sourcecode/packs.py,sha256=
|
|
115
|
+
sourcecode/packs.py,sha256=_f3bRzGIy_Cv2cSUMSco3e6nKxJbApT47PUYMEZwZEw,49506
|
|
116
116
|
sourcecode/parallel.py,sha256=QocwdZoUabdxzd_jEz7f6B2XMsYWFaCFaIl0tIEQs3g,10546
|
|
117
117
|
sourcecode/parse_cache.py,sha256=BaPzh-KygmY4OpJQm2YGh6PWztYsj58vHWTu2eSF4s8,38769
|
|
118
118
|
sourcecode/partial_contract.py,sha256=FZUBACkClvRAmPeYVjxNWeQmP6Y22KW4fPeeTfmT80A,5951
|
|
@@ -128,7 +128,7 @@ sourcecode/pr_impact.py,sha256=8RgTJgvEThezh-Vm65eiFvJBdb45VjcbewEk5P144-0,39184
|
|
|
128
128
|
sourcecode/pr_risk.py,sha256=Qbj1aK-JsdTQd_nji_-RaZAL3WUbwqDg9aH-cHQkoog,11354
|
|
129
129
|
sourcecode/prepare_context.py,sha256=QsJiKoJRNn5_1adyBbKguH_wiaM8hpPLE0fLG9J4ilU,245016
|
|
130
130
|
sourcecode/product_info.py,sha256=rpuCDf0KM2SD-WP8vw2tjdcbhWPsOGEVjSWqS8fkzlo,4966
|
|
131
|
-
sourcecode/progress.py,sha256=
|
|
131
|
+
sourcecode/progress.py,sha256=3tO-rKlRpPPFnRU-qIlQMyq4lsiecMo4GqxEsxAnpMA,23147
|
|
132
132
|
sourcecode/provenance.py,sha256=gLbsLv0gze7k7ChM4i5M3MwKfFPzs8wzM72rUd2C5sM,7342
|
|
133
133
|
sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,12970
|
|
134
134
|
sourcecode/readiness_timeline.py,sha256=KjghC1MWH7Tzto97TBR3N0F8FkR87w5z7lZ1QsWvAQE,8506
|
|
@@ -149,7 +149,7 @@ sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ
|
|
|
149
149
|
sourcecode/repository_ir.py,sha256=xW3ahhhejolAaQL-EMEjOPYmrxhEx5S_3BNGxTEX56U,523074
|
|
150
150
|
sourcecode/resource_budget.py,sha256=Bo-XMvVK9XyHY_S06fVirZrhXgwmnr3XI38-Li55NWY,7767
|
|
151
151
|
sourcecode/ris.py,sha256=zlns4esv1tK8VDIYzOzRmw5yM6VLx7oxaPNB_kN3it4,28617
|
|
152
|
-
sourcecode/risk.py,sha256=
|
|
152
|
+
sourcecode/risk.py,sha256=98MhXPM4unAJZFTKKEeAbEjLq4NZ0Wy-66AHQfA3x7c,102506
|
|
153
153
|
sourcecode/risk_model.py,sha256=RoC-G15qZP5BLEwwmcr4eJ0APgvIbh1WTqhTVT-5YFE,10895
|
|
154
154
|
sourcecode/rule_catalog.py,sha256=QCx6ToFTMCtDt5uuX1uBMLuw3b9Cd92gGRsX34kud4U,5328
|
|
155
155
|
sourcecode/rule_pass.py,sha256=DTNxD-nAecMJhIzI1LCd9FyRumInQSZjitacNh4Zoc8,14671
|
|
@@ -167,7 +167,7 @@ sourcecode/security_config_scan.py,sha256=LU8gfNUBB6wjKR8jkK-qRKtUrbkHZ301_Xsk6h
|
|
|
167
167
|
sourcecode/security_posture.py,sha256=ZWgWMsVdvkDHOkTtAy6T3Q9Kp8ufWsdqPsFQZVBNAFM,56885
|
|
168
168
|
sourcecode/selection_contract.py,sha256=O3q_zHBgUsp1SDukqmH9nOUO6HxLFH_7fAqVL7JMV_A,1974
|
|
169
169
|
sourcecode/self_audit.py,sha256=ZK6VZwbkTL66f4Q4U_JD6sdl6KjQ4DvtPcn5XqGcZDY,11541
|
|
170
|
-
sourcecode/selftest.py,sha256=
|
|
170
|
+
sourcecode/selftest.py,sha256=eaKk7Cm2MPX8hry5yZh0qUnRgv8GwJGXTcHyHH6LZVw,41742
|
|
171
171
|
sourcecode/semantic_analyzer.py,sha256=xGwQvhU9xeOlZ4N-TPXFZT8HBHx4Rg0nPwu8nDFl00Y,95621
|
|
172
172
|
sourcecode/semantic_impact_engine.py,sha256=t09IirGC3JjQDy33JZd1_WKzQVKXkoNl3-XEUr5kjis,20563
|
|
173
173
|
sourcecode/semantic_integration_engine.py,sha256=ZjyKJKOj-adScWlYBuWx8Ee_P0F4_OImUJVnCYgopuU,20952
|
|
@@ -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=
|
|
276
|
+
sourcecode/_docs/DEFECT-LEDGER.md,sha256=u_9OsNch-Ft1Qu_JnobW4FlVnP53MW9ABw3dZw89c6E,1132675
|
|
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=
|
|
280
|
-
sourcecode-5.9.
|
|
281
|
-
sourcecode-5.9.
|
|
282
|
-
sourcecode-5.9.
|
|
283
|
-
sourcecode-5.9.
|
|
284
|
-
sourcecode-5.9.
|
|
285
|
-
sourcecode-5.9.
|
|
279
|
+
sourcecode/_docs/USER_GUIDE.md,sha256=F1dkuPZSohjBLRhNx7YMnUBTJaGJRxwP7D9SZeh8Bqo,104970
|
|
280
|
+
sourcecode-5.9.9.dist-info/METADATA,sha256=Ugbu3bEkMEQj7JkQvbaNhzytfDnNUaJ4cX7IuQVvvS0,64978
|
|
281
|
+
sourcecode-5.9.9.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
282
|
+
sourcecode-5.9.9.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
|
|
283
|
+
sourcecode-5.9.9.dist-info/licenses/LICENSE,sha256=23-fk61oSWGVmkWyka-FIM0rhAPM2hcYHnsaridL8Uo,1859
|
|
284
|
+
sourcecode-5.9.9.dist-info/licenses/NOTICE,sha256=Br-AA8Y3iJo1WXa5uP8QBpzqI5nDu3iGLbFyvDt3BOs,321
|
|
285
|
+
sourcecode-5.9.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|