sourcecode 5.9.10__py3-none-any.whl → 5.9.11__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.
- sourcecode/__init__.py +1 -1
- sourcecode/_build_commit.py +3 -3
- sourcecode/_docs/DEFECT-LEDGER.md +77 -0
- sourcecode/_docs/USER_GUIDE.md +23 -6
- sourcecode/answer_bounds.py +11 -0
- sourcecode/cache.py +68 -0
- sourcecode/classifier.py +120 -21
- sourcecode/cli.py +57 -0
- sourcecode/detectors/java.py +22 -1
- sourcecode/detectors/jvm_ext.py +79 -13
- sourcecode/manifest_cache.py +49 -11
- sourcecode/output_ceiling.py +33 -8
- sourcecode/phased_run.py +34 -2
- sourcecode/progress.py +85 -12
- sourcecode/release_info.py +1 -1
- {sourcecode-5.9.10.dist-info → sourcecode-5.9.11.dist-info}/METADATA +24 -11
- {sourcecode-5.9.10.dist-info → sourcecode-5.9.11.dist-info}/RECORD +21 -21
- {sourcecode-5.9.10.dist-info → sourcecode-5.9.11.dist-info}/WHEEL +0 -0
- {sourcecode-5.9.10.dist-info → sourcecode-5.9.11.dist-info}/entry_points.txt +0 -0
- {sourcecode-5.9.10.dist-info → sourcecode-5.9.11.dist-info}/licenses/LICENSE +0 -0
- {sourcecode-5.9.10.dist-info → sourcecode-5.9.11.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'}, 'AUD-612-07': {'commit': '', 'status': 'uncited'}, 'AUD-612-09': {'commit': '', 'status': 'uncited'}, 'AUD-612-14': {'commit': '', 'status': 'uncited'}, 'AUD-612-B03': {'commit': '', 'status': 'uncited'}, 'AUD-612-B07': {'commit': '', 'status': 'uncited'}, 'AUD-612-B13': {'commit': '', 'status': 'uncited'}, 'AUD-612-E': {'commit': '', 'status': 'uncited'}, 'AUD-612-G': {'commit': '', 'status': 'uncited'}, 'AUD-612-I': {'commit': '', 'status': 'uncited'}, 'AUD-612-K': {'commit': '', 'status': 'uncited'}, 'AUD-612-M': {'commit': '', 'status': 'uncited'}, 'AUD-612-R': {'commit': '', 'status': 'uncited'}, 'AUD-613-ROOT': {'commit': '', 'status': 'uncited'}, 'AUD-614-07': {'commit': '', 'status': 'uncited'}, 'AUD-614-E': {'commit': '', 'status': 'uncited'}, 'AUD-614-F': {'commit': '', 'status': 'uncited'}, 'AUD-614-G': {'commit': '', 'status': 'uncited'}, 'AUD-614-I': {'commit': '', 'status': 'uncited'}, 'AUD-614-INTENT-TIE': {'commit': '', 'status': 'uncited'}, 'AUD-614-JUNIT4': {'commit': '', 'status': 'uncited'}, 'AUD-614-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-614-SELFTEST-TIMEOUT': {'commit': '', 'status': 'uncited'}, 'AUD-615-07': {'commit': '', 'status': 'uncited'}, 'AUD-615-COLD-COMPACT': {'commit': '', 'status': 'uncited'}, 'AUD-615-DRIFT': {'commit': '', 'status': 'uncited'}, 'AUD-615-E': {'commit': '', 'status': 'uncited'}, 'AUD-615-F': {'commit': '', 'status': 'uncited'}, 'AUD-615-INTENT-TIE': {'commit': '', 'status': 'uncited'}, 'AUD-615-JUNIT4': {'commit': '', 'status': 'uncited'}, 'AUD-615-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-615-SELFTEST-TIMEOUT': {'commit': '', 'status': 'uncited'}, '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 = '443139a89c4a318cca61eba18eb95a291255d75d'
|
|
4
|
+
CLOSURE_PROVENANCE = {'AUD-611-B05': {'commit': '', 'status': 'uncited'}, 'AUD-611-B06': {'commit': '', 'status': 'uncited'}, 'AUD-611-B07': {'commit': '', 'status': 'uncited'}, 'AUD-611-B08': {'commit': '', 'status': 'uncited'}, 'AUD-611-B09': {'commit': '', 'status': 'uncited'}, 'AUD-611-B10': {'commit': '', 'status': 'uncited'}, 'AUD-611-B11': {'commit': '', 'status': 'uncited'}, 'AUD-612-07': {'commit': '', 'status': 'uncited'}, 'AUD-612-09': {'commit': '', 'status': 'uncited'}, 'AUD-612-14': {'commit': '', 'status': 'uncited'}, 'AUD-612-B03': {'commit': '', 'status': 'uncited'}, 'AUD-612-B07': {'commit': '', 'status': 'uncited'}, 'AUD-612-B13': {'commit': '', 'status': 'uncited'}, 'AUD-612-E': {'commit': '', 'status': 'uncited'}, 'AUD-612-G': {'commit': '', 'status': 'uncited'}, 'AUD-612-I': {'commit': '', 'status': 'uncited'}, 'AUD-612-K': {'commit': '', 'status': 'uncited'}, 'AUD-612-M': {'commit': '', 'status': 'uncited'}, 'AUD-612-R': {'commit': '', 'status': 'uncited'}, 'AUD-613-ROOT': {'commit': '', 'status': 'uncited'}, 'AUD-614-07': {'commit': '', 'status': 'uncited'}, 'AUD-614-E': {'commit': '', 'status': 'uncited'}, 'AUD-614-F': {'commit': '', 'status': 'uncited'}, 'AUD-614-G': {'commit': '', 'status': 'uncited'}, 'AUD-614-I': {'commit': '', 'status': 'uncited'}, 'AUD-614-INTENT-TIE': {'commit': '', 'status': 'uncited'}, 'AUD-614-JUNIT4': {'commit': '', 'status': 'uncited'}, 'AUD-614-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-614-SELFTEST-TIMEOUT': {'commit': '', 'status': 'uncited'}, 'AUD-615-07': {'commit': '', 'status': 'uncited'}, 'AUD-615-COLD-COMPACT': {'commit': '', 'status': 'uncited'}, 'AUD-615-DRIFT': {'commit': '', 'status': 'uncited'}, 'AUD-615-E': {'commit': '', 'status': 'uncited'}, 'AUD-615-F': {'commit': '', 'status': 'uncited'}, 'AUD-615-INTENT-TIE': {'commit': '', 'status': 'uncited'}, 'AUD-615-JUNIT4': {'commit': '', 'status': 'uncited'}, 'AUD-615-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-615-SELFTEST-TIMEOUT': {'commit': '', 'status': 'uncited'}, 'AUD-616-E-PROV': {'commit': '', 'status': 'uncited'}, 'AUD-616-F, -RISK-L0, -SELFTEST-TIMEOUT, -INTENT-TIE, -DRIFT, -COLD-COMPACT, -E, -07': {'commit': '', 'status': 'uncited'}, 'AUD-616-INTENT-TIE-COV': {'commit': '', 'status': 'uncited'}, 'AUD-616-JUNIT4-SELF': {'commit': '', 'status': 'uncited'}, 'AUD-616-KOTLIN': {'commit': '', 'status': 'uncited'}, 'AUD-616-O': {'commit': '', 'status': 'uncited'}, 'AUD-616-P': {'commit': '', 'status': 'uncited'}, 'Window': {'commit': '', 'status': 'uncited'}, '`ASK-FIELD-01`': {'commit': '829f9fa', 'status': 'present'}, '`ASK-FIELD-02`': {'commit': '6a659fa', 'status': 'present'}, '`ASK-FIELD-03`': {'commit': '55b0d51', 'status': 'present'}, '`AUD-588-F05`': {'commit': '470e4a4', 'status': 'present'}, '`AUD-590-B01` / `B-01`, `F-01`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B02` / `B-02`, `F-02`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B03` / `B-03`, `F-03`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B04` / `B-04`, `F-04`': {'commit': '', 'status': 'uncited'}, '`AUD-590-R01` / `R-01`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A01` / `A-1`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A02` / `A-2`, `A-4`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A03` / `A-3`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A05` / `A-5`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A06` / `A-6`, `A-7`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A08` / `A-8`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A09` / `A-9`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A10` / `A-10`': {'commit': '', 'status': 'uncited'}, '`AUD-592-A02` / `A-1` second half, `§12.5`': {'commit': '63f3d61', 'status': 'present'}, '`AUD-592-B01` / `B-8`': {'commit': '8329614', 'status': 'present'}, '`AUD-592-D01` / `D-1`': {'commit': '0f8e03f', 'status': 'present'}, '`AUD-592-D02` / `D-2`': {'commit': '1d3db4d', 'status': 'present'}, '`AUD-592-D03` / `D-3`': {'commit': 'f28fe40', 'status': 'present'}, '`AUD-592-R01` / `A-1`': {'commit': '216025f', 'status': 'present'}, '`AUD-592-R02` / `N-4`, reopens `AUD-513-N04`': {'commit': 'ec3050d', 'status': 'present'}, '`AUD-593-N01` / `N-01`': {'commit': 'dd6a706', 'status': 'present'}, '`AUD-593-N02` / `N-02`': {'commit': 'f539787', 'status': 'present'}, '`AUD-593-N03` / `N-03`': {'commit': '1dd0b1c', 'status': 'present'}, '`AUD-593-N05` / `N-05`': {'commit': 'aa89388', 'status': 'present'}, '`AUD-593-N06` / `N-06`': {'commit': 'aa584a6', 'status': 'present'}, '`AUD-594-N04` / `D-5`, disclosure half of `B-3`': {'commit': 'a0060b7', 'status': 'present'}, '`AUD-594-X01` / `A-1`, residual of `AUD-593-N03`': {'commit': 'dd222b9', 'status': 'present'}, '`AUD-594-X02` / `A-1` second half': {'commit': '1193937', 'status': 'present'}, '`AUD-594-X03` / `X-03`, residual of `AUD-593-N01`': {'commit': 'e89e8f0', 'status': 'present'}, '`AUD-594-X04`': {'commit': '05b8cc9', 'status': 'present'}, '`AUD-595-A02` / `N-5`, advisory half of `AUD-588-B11`': {'commit': '3e15227', 'status': 'present'}, '`AUD-595-A03` / `B-6` exit-code half': {'commit': 'ab7285f', 'status': 'present'}, '`AUD-595-A05`': {'commit': 'e0b3d10', 'status': 'present'}, '`AUD-595-A07`, narrow half of `AUD-592-A02`': {'commit': 'cc15177', 'status': 'present'}, '`AUD-595-B01` / §B': {'commit': 'b77a1ad', 'status': 'present'}, '`AUD-595-Q01`': {'commit': '91a6566', 'status': 'present'}, '`AUD-596-A01` / `ASK-DET-001`': {'commit': 'fa508b4', 'status': 'present'}, '`AUD-596-A02` / `ASK-SELF-001`': {'commit': '4dfadc8', 'status': 'present'}, '`AUD-596-A03` / `ASK-C1-001`': {'commit': 'bffa3a6', 'status': 'present'}, '`AUD-596-A04` / `ASK-UX-003` + `ASK-UX-004`': {'commit': 'f0aac3c', 'status': 'present'}, '`AUD-596-B01` / `B-01`, harder witness for `BUG-6`': {'commit': '567d09d', 'status': 'present'}, '`AUD-596-B02` / `B-02`, class residual of `R2`': {'commit': '', 'status': 'uncited'}, '`AUD-596-B03` / `B-03`': {'commit': 'a8cb814', 'status': 'present'}, '`AUD-596-B06` / `B-06`': {'commit': '8107927', 'status': 'present'}, '`AUD-596-B07` / `B-07`, class residual of `AUD-592-R02`': {'commit': 'e060485', 'status': 'present'}, '`AUD-596-B09` / `B-09`': {'commit': '5b8146c', 'status': 'present'}, '`AUD-596-B10` / `B-10`': {'commit': '5cb994d', 'status': 'present'}, '`AUD-596-B16` / `B-16`': {'commit': '1507f69', 'status': 'present'}, '`AUD-596-D01` / `B-13` + `B-14`': {'commit': '0b1bdf0', 'status': 'present'}, '`AUD-596-D02` / `B-08`, residual of `AUD-594-N04`': {'commit': 'e7f73c3', 'status': 'present'}, '`AUD-596-D03` / `ASK-AGT-001`': {'commit': '8c46967', 'status': 'present'}, '`AUD-596-D04` / `B-18`': {'commit': '040f027', 'status': 'present'}, '`AUD-596-D05` / `B-19`': {'commit': 'fd2bf68', 'status': 'present'}, '`AUD-596-D06` / `B-20`': {'commit': '784cd83', 'status': 'present'}, '`AUD-596-D07` / `B-17`, half refuted': {'commit': 'c3ff822', 'status': 'present'}, '`AUD-596-D08` / `B-22`, class residual of `AUD-591-A06`': {'commit': 'b396c6a', 'status': 'present'}, '`AUD-596-D09` / `B-23`, surviving half of `B26`': {'commit': 'fd3d63f', 'status': 'present'}, '`AUD-596-D10` / `B-29`': {'commit': '', 'status': 'uncited'}, '`AUD-596-D11` / `ASK-UX-005`': {'commit': 'f4b4f77', 'status': 'present'}, '`AUD-596-D12` — six hints that send the reader back to the error': {'commit': '498df44', 'status': 'present'}, '`AUD-596-D13` / `B-28`': {'commit': '1014c72', 'status': 'present'}, '`AUD-596-D14` / `B-15`, narrow half of `B20`': {'commit': '10f2827', 'status': 'present'}, '`AUD-596-R01` / `B-05`': {'commit': '', 'status': 'uncited'}, '`AUD-596-X01` / `ASK-GATE-001`, gating half of `B24`': {'commit': 'e59c655', 'status': 'present'}, '`AUD-596-X02` / `ASK-UX-001` + `ASK-UX-002` + `B-21`': {'commit': 'f6ebf50', 'status': 'present'}, '`AUD-597-A01` / `ASK-UX-001` + `N-01`': {'commit': 'f8cf061', 'status': 'present'}, '`AUD-597-A02` / `ASK-UX-008` + `ASK-DOC-002`': {'commit': '9d47a4c', 'status': 'present'}, '`AUD-597-A03` / `ASK-DET-001`': {'commit': '4c7622a', 'status': 'present'}, '`AUD-597-A04` / `ASK-LEDGER-001`': {'commit': '46fb870', 'status': 'present'}, '`AUD-597-D01` / `B-04`': {'commit': '1094e6a', 'status': 'present'}, '`AUD-597-D02` / `ASK-DOC-001`': {'commit': 'ecb1bb7', 'status': 'present'}, '`AUD-597-D03` / `N-06`': {'commit': '9784333', 'status': 'present'}, '`AUD-597-D04` / `N-05`': {'commit': 'e51c087', 'status': 'present'}, '`AUD-597-D05` / `N-04`, fourth appearance of `C4-27`': {'commit': '9cccc15', 'status': 'present'}, '`AUD-597-D06` / `ASK-CLI-001`': {'commit': '178aa6d', 'status': 'present'}, '`AUD-597-D07` / `N-03`': {'commit': '9245e35', 'status': 'present'}, '`AUD-597-R01` / `ASK-PERF-001`': {'commit': '6910054', 'status': 'present'}, '`AUD-597-R02` / `ASK-ENC-001`': {'commit': 'ce6dac1', 'status': 'present'}, '`AUD-597-R03` / `ASK-UX-002`': {'commit': 'c57e50b', 'status': 'present'}, '`AUD-597-X01` / `ASK-BUILD-001` + `ASK-META-001` / `B-12`': {'commit': '354338b', 'status': 'present'}, '`AUD-598-B01` / `ASK-META-001`': {'commit': 'b2f7c65', 'status': 'present'}, '`AUD-598-B02` / `ASK-UX-002`': {'commit': '56a4bf7', 'status': 'present'}, '`AUD-598-B03` / `B-11`': {'commit': 'c63cb24', 'status': 'present'}, '`AUD-598-B04` / `N-02`': {'commit': '187937f', 'status': 'present'}, '`AUD-598-R01` / `ASK-PERF-002`': {'commit': '', 'status': 'uncited'}, '`AUD-598-X01` / `N-01` + `B-13` + `B-14` + `B-29` + `N-06`': {'commit': '6f2f5a4', 'status': 'present'}, '`AUD-600-B01` / `P-01`': {'commit': '366ffed', 'status': 'present'}, '`AUD-600-B02` / `P-02` + `ASK-PACK-002`': {'commit': '2a814c3', 'status': 'present'}, '`AUD-600-B03` / `ASK-PACK-001`': {'commit': '452f2a0', 'status': 'present'}, '`AUD-600-D01`': {'commit': '1224e1b', 'status': 'present'}, '`AUD-600-D02`': {'commit': 'eab8172', 'status': 'present'}, '`AUD-600-R01` / `R-01`': {'commit': '88add64', 'status': 'present'}, '`AUD-601-B01` / `N-02`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-B02` / `ASK-PACK-004`': {'commit': '', 'status': 'uncited'}, '`AUD-601-D01` / `N-01`, `ASK-UX-001`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-D02` / `B-13`, `B-14`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-M01` / `B-26`, `B-27` retracted': {'commit': '', 'status': 'uncited'}, '`AUD-601-R01` / `R-02`': {'commit': '', 'status': 'uncited'}, '`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': 161, 'present': 91, 'absent': 0, 'unresolved': 0, 'uncited': 70, 'unknown': 0}
|
|
@@ -150,6 +150,83 @@ Spring detection controls, baseline safety and the prior `/dev/null` fix matrix.
|
|
|
150
150
|
intermittent endpoint-security retrieval issue had 0/18 failures in this pass and stays
|
|
151
151
|
under observation because its historical rate is about 2% over 51 runs.
|
|
152
152
|
|
|
153
|
+
**External audit intake `AUD-616`, re-audit of release 5.9.10 — 2026-08-28.** The scope was
|
|
154
|
+
again restricted to `testing/`, with direct re-verification of all twelve `AUD-615` items, a
|
|
155
|
+
robustness sweep over six previously unaudited repositories and five parallel forks, each
|
|
156
|
+
with its own scratch directory — the methodology control `AUD-615` required is now applied.
|
|
157
|
+
The report publishes three headline numbers (8.5 provisional, 8.0 corrected, 8.1 on a
|
|
158
|
+
per-axis rubric); **the governing score is the conservative 8.0/10**, and as before the rows
|
|
159
|
+
below record evidence, not a headline. The installed artefact self-declares `"stale": true`;
|
|
160
|
+
the auditor continued against it deliberately, so every timing is a property of that build.
|
|
161
|
+
No target-repository writes, commits or Java-file changes were reported.
|
|
162
|
+
|
|
163
|
+
The governing item of this round is the `tutorials` trend: five consecutive releases, each
|
|
164
|
+
slower than the last, with no measurement out of order. It is escalated to critical and
|
|
165
|
+
reclassified from an availability observation to a sustained performance regression.
|
|
166
|
+
|
|
167
|
+
**Verdict on the round (5.9.11).** Seven of the nine rows are closed, two are partially
|
|
168
|
+
closed and one — `WIN-PROC` — is carried forward as not actionable from a Darwin checkout.
|
|
169
|
+
The two critical rows are the two partial ones, and both for the same reason, stated plainly
|
|
170
|
+
because it changes what a future round should measure: **neither timing symptom reproduces
|
|
171
|
+
in this checkout.** Root `--compact` on `tutorials` completes in 54.9 s, exit 0, 15 KB, and
|
|
172
|
+
`audit-report --format markdown` on `thingsboard` ran 2/2 clean at 39.3 s and 26.8 s. The
|
|
173
|
+
4m00→7m00 trend and the four-round non-completion are properties of the auditor's build and
|
|
174
|
+
platform, and re-measurement there is the outstanding work on both rows.
|
|
175
|
+
|
|
176
|
+
What was done instead of accepting or dismissing them was to attribute the cost and remove
|
|
177
|
+
the silence. The cost is now published per phase against file count, and two mechanisms
|
|
178
|
+
behind it are closed with a measured **×1.69** on the governing subject — detection 30.3 s →
|
|
179
|
+
9.2 s, total 54.9 s → 32.5 s, payload byte-identical. The silence is closed at both ends: the
|
|
180
|
+
root analysis stops at `ASK_MAX_ANALYSIS_SECONDS` and returns a structured partial envelope
|
|
181
|
+
with a gate verdict instead of zero bytes at exit 124, and the delayed liveness warning names
|
|
182
|
+
the phase, its counts and whether it has changed — the one signal that separates a blocked
|
|
183
|
+
run from a slow one, and the one four consecutive rounds did not have.
|
|
184
|
+
|
|
185
|
+
Three findings of this round belong to the checkout rather than to the report, and are
|
|
186
|
+
recorded so they are not re-derived. `AUD-616-P`'s race did not reproduce serialized, and the
|
|
187
|
+
double miss reproduced here first was **our own concurrency** — the same contamination
|
|
188
|
+
`AUD-615` had to correct for; a deterministic mechanism with the identical symptom was found
|
|
189
|
+
underneath it and fixed. `AUD-616-JUNIT4-SELF`'s filed remedy would not have closed the row —
|
|
190
|
+
JUnit4 has three `main()`-bearing entry points, so "no entry point" was the wrong condition.
|
|
191
|
+
And the root analysis joining the bounded tier exposed three batteries comparing the two
|
|
192
|
+
command-name vocabularies as raw strings, or walking only to command leaves; each now goes
|
|
193
|
+
through the authority that already reconciles them.
|
|
194
|
+
|
|
195
|
+
| ID | Severity | Status | Evidence and required closure |
|
|
196
|
+
|---|---:|---|---|
|
|
197
|
+
| AUD-616-TUTORIALS | Critical / sustained regression | partially closed 5.9.11 — perf attributed and bounded; the reported trend does not reproduce here | **Not reproducible as filed on this platform.** Root `--compact` on `tutorials` (24,073 Java files) completes in 54.9 s, exit 0, 15 KB, four serialized cold runs (54.9 / 52.7 / 53.5 / 57.9, max/min 1.10). The report's 4m00→7m00 with exit 124 is a property of the auditor's build and platform, not of this checkout. What was verified and fixed instead is the cost and the silence. **Cost, attributed per phase against file count** (`SOURCECODE_PERF`): discovery 3.5 s, detection 30.3 s, analysis 13.4 s, serialize 6.0 s. Two mechanisms found by cProfile and closed: `manifest_cache._key()` ran fourteen `rglob` walks per call and was called by both `load()` and `save()` for each of 246 modules (1.5 M `posix.scandir` calls, 26 s) — now one `os.walk`, memoised per root, digest unchanged and asserted against the fourteen calls it replaces; and `TypeClassifier` walked the file tree seven times per `enrich` × 246 modules — now once, threaded. **Measured: detection 30.3 → 9.2 s, total 54.9 → 32.5 s (×1.69), payload byte-identical, max/min 1.003.** **Silence, closed:** the root analysis is a `PhasedRun` over the four `perf` seams, so `ASK_MAX_ANALYSIS_SECONDS` bounds it and a spent budget emits the `partial-answer-v1` envelope — phases completed and pending, elapsed, budget and its provenance — instead of zero bytes. It declares `--ci`/`--allow-partial` and exits 75 on a cut gated run. **Still open:** a published repository-size envelope (the scale contract), and re-measurement on the auditor's platform. |
|
|
198
|
+
| AUD-616-L | Critical / regression | partially closed 5.9.11 — stage-level progress shipped; the unbounded run does not reproduce | **Not reproducible as filed here.** `audit-report --format markdown` on `thingsboard` ran 2/2 clean: exit 0, 3,662 bytes both times, 39.3 s and 26.8 s — consistent with the audit's own isolated fork (3m1.4s, 1m49.7s, 3,701 bytes) and nowhere near a timeout. The **bounded structured failure** the row asks for is already shipped and verified: the command is a `PhasedRun`, and under `ASK_MAX_ANALYSIS_SECONDS=1` it returns a report declaring `Status: partial` and `Total defects: 0 (floor — this run was cut short)`. **The heartbeat half was a real defect and is fixed.** The delayed warning read `run has exceeded 5.0m; still working` and repeated that sentence unchanged for the life of the run — identical at 60 s and 420 s, which is why four rounds could not separate a blocked run from a slow one: a blocked process is also still alive. It now names the phase, the stage and its counts, how long the run has been in that phase, and whether the phase changed since the previous warning. Writing it surfaced a second defect: the first version read `_stage_t0`, which only restarts on a *counted* stage change, and published `parsing manifests for 21.1s` about a phase entered at 3 s — the run's elapsed time under a label claiming to be the phase's. A phase clock now exists separately from the counted-stage clock `_eta_seconds` uses. **Still open:** reproduction under deliberate contention on the auditor's platform. |
|
|
199
|
+
| AUD-616-KOTLIN | Medium / confident falsehood | closed 5.9.11 | Reproduced exactly, then closed at both ends. `JvmExtDetector` also admits on source population now, with the sibling `.java` count as the bar rather than an absolute number, so a Kotlin repository on the Groovy DSL — or with no root build file, which is `ktor-samples` — surfaces a Kotlin stack, while a Java monolith carrying three `.kt` tests does not. Admission by source publishes `detection_method: heuristic`, no `manifests` and no `package_manager`: a build file was not read, so nothing only a build file attests is claimed. The real authority for `confidence` was `TypeClassifier._enrich_stack`, which recomputes what the detector published from a score summing *declarations* and gives the source population one point whether there are two files or twenty thousand; both ends are corrected there, once, for every stack with a source extension — zero source files caps at `low` with a `no_source_evidence` signal, and owning the largest source population against a competing manifest-backed claim floors at `medium`. `ktor-samples` now reports `kotlin` primary at `medium`, `java` at `low`, and headlines "Kotlin library / package". Controls re-measured unchanged: `spring-security`, `shenyu`, `thingsboard` keep `java` primary at `high`. |
|
|
200
|
+
| AUD-616-O | Medium | closed 5.9.11 | `--output FILE` moves ahead of the format substitutions, and each substitution now states in its own text that it re-runs the command and that its cost on this repository is unmeasured. The module's ordering rule is unchanged and was not the defect; the reading of "the same answer" was. A bounding flag and `--output FILE` keep the rendering that has already run to completion here — that run is why the refusal has a size to report at all — while a different `--format` is a different composition this module has measured nothing about. The bypass stays last, the file destination is untouched, and the population is still the intersection of `FORMAT_REGISTRY` with the command's declared options, so nothing branches on a command name. |
|
|
201
|
+
| AUD-616-P | Low-medium / investigation | closed 5.9.11 — race not reproduced, a deterministic mechanism with the same symptom fixed | **The race does not reproduce serialized.** On `tutorials`, store cleared first, four sequential runs: 82.1 s cold and `false`, then 1.58 / 1.48 / 1.47 s, all `true`. A double miss was reproduced here first and then attributed to two `ask` processes analysing the repository at once — runs against one repository share `~/.sourcecode/cache/<repo_id>/` whatever scratch directory they are given, so per-fork scratch isolation does not separate them. **A deterministic mechanism with the same symptom did reproduce and is fixed.** The GC's size pass sorted candidates oldest-first and could walk to the end of that list, and the end of it is the entry the calling run has just written: a core larger than the whole store budget was written and deleted inside one process, on every run, for ever. It is reachable at this scale rather than hypothetically — `tutorials` stores a 9.4 MB core against a 50 MB default. The newest core is no longer a candidate; everything older still is. When the newest entry alone exceeds the budget the store stays over budget, and `cache.status()` publishes `size_budget_mb`, `largest_core_bytes` and an `over_budget_reason` naming the variable to raise. |
|
|
202
|
+
| AUD-616-JUNIT4-SELF | Low-medium / classification | closed 5.9.11 | **One correction to the remedy as filed:** it proposed classifying single-module JVM repositories "with no entry point", and JUnit4 has three — `TestRunner`, `JUnitCore` and `Version` each carry a `main()` — so that condition would have left the row open. The multi-module requirement was what was doing the wrong work: module count describes a build layout, not whether a repository is a library, and it was in the branch only because the row that introduced it (JobRunr) happened to be multi-module. By the time control reaches that point every stronger reading has been ruled out by the branches above, so what is left is a JVM repository with sources and no application-defining framework — the same inference the multi-module branch already makes. Measured over thirteen JVM repositories before and after: three change and all three are libraries (`junit4`, `mybatis-3`, `Mybatis-PageHelper`, each `unknown` → `library`); the other ten are byte-identical. A repository with no detected stack still publishes no type, which is the `shopizer` case confirmed correct by design. |
|
|
203
|
+
| AUD-616-WIN-PROC | Medium | open, carried forward — not actionable on this platform | Unchanged from `AUD-614-WIN-PROC` and `AUD-615-WIN-PROC`. The subject is native Windows process-tree teardown and the auditor's own sample was declared inconclusive (contaminated by unrelated sessions). Nothing in this round can be reproduced or refuted from a Darwin checkout, and shipping a Job Objects path that no test here can exercise would be a change made blind. It needs a native Windows/Git Bash run, and the narrowed reading stands: late cleanup and non-response to the parent's termination signal, not a permanent leak. |
|
|
204
|
+
| AUD-616-INTENT-TIE-COV | Low / coverage | closed 5.9.11 | Behaviour was already correct and nothing held it there. Both sides are covered now, with the tying questions derived from the shipped vocabulary rather than invented: `central classes deployment shape` ties two repository-wide intents at 2.8284, and `what endpoints break cross module impact` ties two target-required ones at 5.1962. Each refuses, names both candidates and the reason, offers no command, and reports no intent — so no consumer reads the rest of the payload as describing a losing candidate. A first test asserts the fixtures still tie, so a vocabulary change that destroys the tie fails the test depending on it instead of passing vacuously; a second asserts both candidates really are of one requirement class, without which the battery would silently become another copy of the rows it complements. |
|
|
205
|
+
| AUD-616-E-PROV | Low / ledger hygiene | closed 5.9.11 | **The command is `ask impact-chain <EventClass> <repo> --type events`** — the event-topology query, a third sibling of `impact` and `impact-chain` and the one the re-audit could not identify. `EventTopologyResult` declares `risk_level: str = "low"` as its dataclass default and the `not_found` branch returned the result without overriding it, so a class absent from the CIR was published as a resolved low-risk answer while its two siblings said `unknown`. The value was corrected in 5.9.9; what nothing covered is the **parity** — three commands, three code paths, one question, and no test that fails when one drifts again. A battery now runs all three through the CLI on a repository, the way an external evaluator would, and asserts they agree. Re-verified: all three publish `resolution: not_found` and `risk_level: unknown`. |
|
|
206
|
+
| AUD-616-F, -RISK-L0, -SELFTEST-TIMEOUT, -INTENT-TIE, -DRIFT, -COLD-COMPACT, -E, -07 | — | closed 5.9.9 (reverified in 5.9.10) | 6/6 correct exits with stdout to `/dev/null`; `risk --limit 0` in 2.8 s with `analysis_skipped: true`; `selftest --timeout` exit 75 with a 7.2 KB structured diagnostic; intent behaviour coherent with the reclassification; `drift` default profile normalized; `cold-start --compact` size stable across repeated runs. No regression in any of them. |
|
|
207
|
+
|
|
208
|
+
**Size-datum note.** The report records `cold-start --compact` as 14,959-14,960 bytes in one
|
|
209
|
+
block and 88,265-88,266 bytes in another. Different repositories; no reconciliation is
|
|
210
|
+
claimed, and the one-byte delta is the redirection newline, not a payload change.
|
|
211
|
+
|
|
212
|
+
**Robustness sweep.** Five of six new repositories behaved correctly and open no row:
|
|
213
|
+
`spock` (674 Java + 607 Groovy, languages not confused), `Chronicle-Queue` (CLI tool,
|
|
214
|
+
`spring_detected: false`), `javaee7-samples` (classic Java EE), `camel-examples` (474
|
|
215
|
+
dependencies, no crash) and `shopizer-v1.1.5` (no root `pom.xml`, `project_type: null` with
|
|
216
|
+
an explicit explanation — correct by design, the type genuinely cannot be inferred). The
|
|
217
|
+
sixth, `ktor-samples`, is the first Kotlin subject in the series and opened `AUD-616-KOTLIN`.
|
|
218
|
+
|
|
219
|
+
**Also stable.** `pack gate --since develop` measured 208.6 s, consistent with the 234 s
|
|
220
|
+
recovery in 5.9.9: the four-round upward trend on that command remains broken. The full
|
|
221
|
+
earlier closure set was re-exercised without a single regression.
|
|
222
|
+
|
|
223
|
+
**Commercially**, the band stays `$25-40/developer/month` with the standing no-large-scale
|
|
224
|
+
CI-gating condition. The `TUTORIALS` trend strengthens that condition rather than relaxing
|
|
225
|
+
it: a degradation visible across five consecutive releases is precisely the evidence an
|
|
226
|
+
annual reliability commitment cannot be sold against. Full triage is in
|
|
227
|
+
[`AUDIT-2026-08-28-EXTERNAL-5.9.10.md`](AUDIT-2026-08-28-EXTERNAL-5.9.10.md).
|
|
228
|
+
|
|
229
|
+
|
|
153
230
|
**Commercial boundary.** The corrected assessment is `$25-40/developer/month`, not the
|
|
154
231
|
initial `$45-58` headline. Do not market enterprise-scale support or production CI
|
|
155
232
|
gating for repositories above roughly 3,000 files until the four critical/high
|
sourcecode/_docs/USER_GUIDE.md
CHANGED
|
@@ -46,23 +46,40 @@ 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.11, `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.11, 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.11
|
|
59
|
+
|
|
60
|
+
**A deadline the root analysis honours.** `ASK_MAX_ANALYSIS_SECONDS=<seconds>` now
|
|
61
|
+
stops `ask <repo>` at a measured phase boundary. A run that spends its budget
|
|
62
|
+
returns a structured `partial-answer-v1` payload naming the phases that completed,
|
|
63
|
+
the phases that did not, the elapsed time and the budget that decided it — not an
|
|
64
|
+
empty stdout. Counts in a partial answer are floors over the completed phases
|
|
65
|
+
only. Add `--ci` to make a cut run fail with exit 75, and `--allow-partial` to
|
|
66
|
+
accept one on purpose; both are published back in the payload as `exit_code`
|
|
67
|
+
beside `gate_exit_code`, so a pipeline can tell a chosen zero from a defaulted one.
|
|
68
|
+
|
|
69
|
+
**Progress that distinguishes slow from blocked.** On a non-interactive run, the
|
|
70
|
+
delayed liveness warning names the phase, the stage inside it and its counts, how
|
|
71
|
+
long the run has been in that phase, and whether the phase has changed since the
|
|
72
|
+
previous warning. Two warnings naming different phases are progress; two naming
|
|
73
|
+
the same phase are a stretch that is not reporting.
|
|
59
74
|
|
|
60
75
|
**Known availability limits.** Do not place `ask audit-report . --format markdown`
|
|
61
76
|
or the root compact command behind a strict timeout on repositories above roughly
|
|
62
77
|
3,000 files. Markdown audit-report delivery is still contention-sensitive; for a
|
|
63
78
|
supervised or CI workflow, save `risk` first and use `audit-report --from-risk`,
|
|
64
|
-
prefer JSON/YAML, and retain progress output.
|
|
65
|
-
|
|
79
|
+
prefer JSON/YAML, and retain progress output. Both limits are held open
|
|
80
|
+
deliberately: neither reproduces on the engineering platform, so neither is closed
|
|
81
|
+
on measurements taken there. The current closure status is in the
|
|
82
|
+
[development roadmap](DEVELOPMENT-ROADMAP.md#external-audit-intake--aud-616-re-audit-release-5910).
|
|
66
83
|
|
|
67
84
|
Invalid analysis inputs fail before expensive work and use the structured
|
|
68
85
|
`INVALID_INPUT` envelope: a missing `--consumer` repository, a malformed CVE
|
|
@@ -243,7 +260,7 @@ pipx install sourcecode # isolated install, no venv needed
|
|
|
243
260
|
|
|
244
261
|
# Verify
|
|
245
262
|
ask version
|
|
246
|
-
# ask 5.9.
|
|
263
|
+
# ask 5.9.11
|
|
247
264
|
```
|
|
248
265
|
|
|
249
266
|
Requires Python 3.9+.
|
sourcecode/answer_bounds.py
CHANGED
|
@@ -111,6 +111,17 @@ def _gaps(payload: "dict[str, Any]") -> "list[dict[str, Any]]":
|
|
|
111
111
|
"field": "_partial",
|
|
112
112
|
"what": "the run stopped before every phase completed",
|
|
113
113
|
})
|
|
114
|
+
elif payload.get("partial") is True:
|
|
115
|
+
# AUD-616-TUTORIALS. A cut run can carry its declaration in two places: a
|
|
116
|
+
# `_partial` block attached to an answer that was still produced, and — for
|
|
117
|
+
# a command that has no partial answer to attach it to — the whole payload
|
|
118
|
+
# being the `partial-answer-v1` envelope. Only the first was recognised, so
|
|
119
|
+
# a bounded deadline failure published `complete: true` beside
|
|
120
|
+
# `"partial": true` in the same object.
|
|
121
|
+
found.append({
|
|
122
|
+
"field": "partial",
|
|
123
|
+
"what": "the run stopped before every phase completed",
|
|
124
|
+
})
|
|
114
125
|
return found
|
|
115
126
|
|
|
116
127
|
|
sourcecode/cache.py
CHANGED
|
@@ -402,6 +402,7 @@ def status(repo_root: Path) -> dict[str, Any]:
|
|
|
402
402
|
total_bytes = sum(f.stat().st_size for f in all_files if f.exists())
|
|
403
403
|
return {
|
|
404
404
|
"cache_dir": str(cache_d),
|
|
405
|
+
**_size_budget_fields(cores, snapshots, cas_blobs),
|
|
405
406
|
"cached_cores": len(cores),
|
|
406
407
|
"snapshots": len(snapshots),
|
|
407
408
|
"views": len(views),
|
|
@@ -419,6 +420,51 @@ def status(repo_root: Path) -> dict[str, Any]:
|
|
|
419
420
|
}
|
|
420
421
|
|
|
421
422
|
|
|
423
|
+
def _size_budget_fields(
|
|
424
|
+
cores: "list[Path]", snapshots: "list[Path]", cas_blobs: "list[Path]"
|
|
425
|
+
) -> "dict[str, Any]":
|
|
426
|
+
"""What the store's size budget is, and whether one entry cannot fit under it.
|
|
427
|
+
|
|
428
|
+
AUD-616-P. The GC no longer evicts the entry a run has just written, so a
|
|
429
|
+
repository whose core is larger than `SOURCECODE_CACHE_MAX_SIZE_MB` keeps its
|
|
430
|
+
cache and the store sits over budget. That is the honest outcome and it must
|
|
431
|
+
not be silent: the alternative the code used to take was to delete the entry
|
|
432
|
+
and leave a second identical run reporting `served_from_cache: false` with no
|
|
433
|
+
explanation anywhere.
|
|
434
|
+
"""
|
|
435
|
+
try:
|
|
436
|
+
budget_mb = int(
|
|
437
|
+
os.environ.get("SOURCECODE_CACHE_MAX_SIZE_MB", _DEFAULT_MAX_SIZE_MB)
|
|
438
|
+
)
|
|
439
|
+
except ValueError:
|
|
440
|
+
budget_mb = _DEFAULT_MAX_SIZE_MB
|
|
441
|
+
counted = [p for p in (*cores, *snapshots, *cas_blobs) if p.exists()]
|
|
442
|
+
counted_bytes = sum(p.stat().st_size for p in counted)
|
|
443
|
+
largest = max((p.stat().st_size for p in cores if p.exists()), default=0)
|
|
444
|
+
budget_bytes = budget_mb * 1024 * 1024
|
|
445
|
+
reason: "Optional[str]" = None
|
|
446
|
+
if budget_bytes > 0 and counted_bytes > budget_bytes:
|
|
447
|
+
if largest > budget_bytes:
|
|
448
|
+
reason = (
|
|
449
|
+
f"one cached analysis is {round(largest / (1024 * 1024), 2)} MB, "
|
|
450
|
+
f"larger than the whole {budget_mb} MB store budget. It is kept "
|
|
451
|
+
f"rather than evicted, so this repository is still served from "
|
|
452
|
+
f"cache; raise SOURCECODE_CACHE_MAX_SIZE_MB to bring the store "
|
|
453
|
+
f"back under budget."
|
|
454
|
+
)
|
|
455
|
+
else:
|
|
456
|
+
reason = (
|
|
457
|
+
f"the store holds {round(counted_bytes / (1024 * 1024), 2)} MB "
|
|
458
|
+
f"against a {budget_mb} MB budget; older entries are evicted on "
|
|
459
|
+
f"the next write."
|
|
460
|
+
)
|
|
461
|
+
return {
|
|
462
|
+
"size_budget_mb": budget_mb,
|
|
463
|
+
"largest_core_bytes": largest,
|
|
464
|
+
"over_budget_reason": reason,
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
|
|
422
468
|
#: E-37. Which environment variable relocates which store. Two exist, they cover
|
|
423
469
|
#: different stores, and the difference is invisible in a payload that prints a
|
|
424
470
|
#: path and no variable — so an operator who moves or empties "the cache" moves
|
|
@@ -1058,9 +1104,31 @@ def _gc(cache_d: Path) -> None:
|
|
|
1058
1104
|
if total > max_size_bytes:
|
|
1059
1105
|
# Sort oldest-first; evict core+snapshot files until under budget
|
|
1060
1106
|
size_candidates.sort(key=lambda p: p.stat().st_mtime)
|
|
1107
|
+
# AUD-616-P. The loop below used to be able to reach the end of that
|
|
1108
|
+
# list, and the end of that list is the entry the run that called
|
|
1109
|
+
# this GC has just written. A single core larger than the whole
|
|
1110
|
+
# budget was therefore stored and deleted again inside one process,
|
|
1111
|
+
# on every run, for ever: the repository could never be served from
|
|
1112
|
+
# cache, `served_from_cache` stayed `false` on a second identical
|
|
1113
|
+
# run, and nothing anywhere said why. `tutorials` stores a 9.4 MB
|
|
1114
|
+
# core against a 50 MB default, so six distinct tree states of one
|
|
1115
|
+
# large repository are enough to reach it.
|
|
1116
|
+
#
|
|
1117
|
+
# Eviction exists to bound a store, not to undo the work of the
|
|
1118
|
+
# process performing it. The newest core is never a candidate; when
|
|
1119
|
+
# it alone exceeds the budget the store stays over budget, which is
|
|
1120
|
+
# a fact `status()` publishes rather than a state the GC destroys
|
|
1121
|
+
# answers to hide.
|
|
1122
|
+
_newest_core = max(
|
|
1123
|
+
(p for p in size_candidates if p.name.startswith("core-")),
|
|
1124
|
+
key=lambda p: p.stat().st_mtime,
|
|
1125
|
+
default=None,
|
|
1126
|
+
)
|
|
1061
1127
|
for evict in size_candidates:
|
|
1062
1128
|
if evict.name.startswith("view-"):
|
|
1063
1129
|
continue
|
|
1130
|
+
if _newest_core is not None and evict == _newest_core:
|
|
1131
|
+
continue
|
|
1064
1132
|
total -= evict.stat().st_size if evict.exists() else 0
|
|
1065
1133
|
_safe_unlink(evict)
|
|
1066
1134
|
surviving = [p for p in surviving if p != evict]
|
sourcecode/classifier.py
CHANGED
|
@@ -145,21 +145,47 @@ class TypeClassifier:
|
|
|
145
145
|
stacks: Sequence[StackDetection],
|
|
146
146
|
entry_points: Sequence[EntryPoint],
|
|
147
147
|
) -> tuple[list[StackDetection], str | None]:
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
# AUD-616-TUTORIALS. One walk of the tree, threaded through every helper
|
|
149
|
+
# below. `flatten_file_tree` rebuilds the whole path list on each call and
|
|
150
|
+
# this class called it seven times per `enrich` — once for the primary-stack
|
|
151
|
+
# authority, once per stack for the extension count, and once each inside
|
|
152
|
+
# `_classify_project_type`, `_is_multi_module`,
|
|
153
|
+
# `_localized_adapter_frameworks` and `_api_is_center_of_gravity`. On a
|
|
154
|
+
# monorepo `enrich` itself runs once per detected module (247 times on
|
|
155
|
+
# `tutorials`), so the walks multiply: the profile attributes 22.9 s to
|
|
156
|
+
# `_count_extension_hits` alone and 21.5 s to `flatten_file_tree` across
|
|
157
|
+
# 9.6 M recursive calls, out of a 55 s run. The tree is built once and is
|
|
158
|
+
# not mutated here, so walking it once is not an optimisation with a
|
|
159
|
+
# trade-off — it is the same answer with the repetition removed.
|
|
160
|
+
flat_paths = flatten_file_tree(file_tree)
|
|
161
|
+
|
|
162
|
+
# AUD-616-KOTLIN. The source population of every stack, measured once, so
|
|
163
|
+
# `_enrich_stack` can compare a stack against its siblings instead of only
|
|
164
|
+
# against itself. A per-stack score cannot tell "no source evidence" from
|
|
165
|
+
# "the most source evidence in the tree", and those are the two ends of the
|
|
166
|
+
# confidence scale.
|
|
167
|
+
source_hits = {
|
|
168
|
+
stack.stack: self._count_extension_hits(flat_paths, stack.stack)
|
|
169
|
+
for stack in stacks
|
|
170
|
+
}
|
|
171
|
+
enriched = [
|
|
172
|
+
self._enrich_stack(flat_paths, stack, entry_points, source_hits, stacks)
|
|
173
|
+
for stack in stacks
|
|
174
|
+
]
|
|
175
|
+
project_type = self._classify_project_type(flat_paths, enriched, entry_points)
|
|
150
176
|
# Delegate to the single primary-stack authority. enrich only ever sees the
|
|
151
177
|
# ROOT stacks; the authority runs again at model finalization over the full
|
|
152
178
|
# root+workspace set (that call is the one consumers read).
|
|
153
|
-
final_stacks = select_primary_stack(
|
|
154
|
-
enriched, project_type, flatten_file_tree(file_tree)
|
|
155
|
-
)
|
|
179
|
+
final_stacks = select_primary_stack(enriched, project_type, flat_paths)
|
|
156
180
|
return final_stacks, project_type
|
|
157
181
|
|
|
158
182
|
def _enrich_stack(
|
|
159
183
|
self,
|
|
160
|
-
|
|
184
|
+
flat_paths: "Sequence[str]",
|
|
161
185
|
stack: StackDetection,
|
|
162
186
|
entry_points: Sequence[EntryPoint],
|
|
187
|
+
source_hits: "dict[str, int] | None" = None,
|
|
188
|
+
all_stacks: "Sequence[StackDetection] | None" = None,
|
|
163
189
|
) -> StackDetection:
|
|
164
190
|
signals = list(stack.signals)
|
|
165
191
|
score = 0
|
|
@@ -179,7 +205,13 @@ class TypeClassifier:
|
|
|
179
205
|
score += 2
|
|
180
206
|
signals.extend(f"entry:{entry.path}" for entry in matching_entry_points)
|
|
181
207
|
|
|
182
|
-
|
|
208
|
+
if source_hits is None:
|
|
209
|
+
source_hits = {}
|
|
210
|
+
if all_stacks is None:
|
|
211
|
+
all_stacks = [stack]
|
|
212
|
+
extension_hits = source_hits.get(stack.stack)
|
|
213
|
+
if extension_hits is None:
|
|
214
|
+
extension_hits = self._count_extension_hits(flat_paths, stack.stack)
|
|
183
215
|
if extension_hits >= 2:
|
|
184
216
|
score += 1
|
|
185
217
|
signals.append(f"extensions:{extension_hits}")
|
|
@@ -189,15 +221,52 @@ class TypeClassifier:
|
|
|
189
221
|
signals.append("method:heuristic")
|
|
190
222
|
|
|
191
223
|
confidence = self._score_to_confidence(score)
|
|
224
|
+
|
|
225
|
+
# AUD-616-KOTLIN. The score above is a sum of *declarations* — manifests,
|
|
226
|
+
# a package manager, framework coordinates — and source files contribute a
|
|
227
|
+
# single point to it whether there are two of them or twenty thousand. On
|
|
228
|
+
# `ktor-samples` that arithmetic published `java` at `high` on the strength
|
|
229
|
+
# of one appengine `pom.xml` in a subdirectory, with zero `.java` files in
|
|
230
|
+
# the tree, and left the 151 `.kt` files at `low`. Both ends are corrected
|
|
231
|
+
# here, once, for every stack that has a source extension at all — the rule
|
|
232
|
+
# is the product's own: never a confident falsehood, and never call the
|
|
233
|
+
# best evidence in the repository the weakest claim available.
|
|
234
|
+
known_exts = _STACK_EXTENSIONS.get(stack.stack)
|
|
235
|
+
if known_exts:
|
|
236
|
+
if extension_hits == 0:
|
|
237
|
+
# A manifest attests that something is built here. It does not
|
|
238
|
+
# attest the language, and nothing else in the tree does either.
|
|
239
|
+
confidence = "low"
|
|
240
|
+
signals.append(
|
|
241
|
+
f"no_source_evidence:0 {'/'.join(known_exts)} files in tree"
|
|
242
|
+
)
|
|
243
|
+
elif (
|
|
244
|
+
confidence == "low"
|
|
245
|
+
and extension_hits >= max(source_hits.values(), default=0)
|
|
246
|
+
and any(
|
|
247
|
+
other.stack != stack.stack and other.manifests
|
|
248
|
+
for other in all_stacks
|
|
249
|
+
)
|
|
250
|
+
):
|
|
251
|
+
# This stack owns the largest source population in the tree and a
|
|
252
|
+
# *different* stack is making a manifest-backed claim over the same
|
|
253
|
+
# repository. Leaving the measured language at the bottom of the
|
|
254
|
+
# scale while the declared one sits above it is what let `java`
|
|
255
|
+
# outrank 151 `.kt` files. The floor is deliberately conditioned on
|
|
256
|
+
# a competing claim: a lone heuristic stack with two source files
|
|
257
|
+
# and nothing to contradict it is honestly `low`, and stays there.
|
|
258
|
+
confidence = "medium"
|
|
259
|
+
signals.append(f"largest_source_population:{extension_hits}")
|
|
192
260
|
return replace(stack, confidence=confidence, signals=self._unique(signals))
|
|
193
261
|
|
|
194
262
|
def _classify_project_type(
|
|
195
263
|
self,
|
|
196
|
-
|
|
264
|
+
flat_paths: "Sequence[str]",
|
|
197
265
|
stacks: Sequence[StackDetection],
|
|
198
266
|
entry_points: Sequence[EntryPoint],
|
|
199
267
|
) -> str | None:
|
|
200
|
-
|
|
268
|
+
all_paths = flat_paths
|
|
269
|
+
flat_paths = set(all_paths)
|
|
201
270
|
stack_names = {stack.stack for stack in stacks}
|
|
202
271
|
framework_names = {framework.name for stack in stacks for framework in stack.frameworks}
|
|
203
272
|
|
|
@@ -213,7 +282,7 @@ class TypeClassifier:
|
|
|
213
282
|
# unaffected — its dominant module *does* use the framework. Drop such
|
|
214
283
|
# localized frameworks from the set that drives the app-type decision below.
|
|
215
284
|
_app_frameworks = framework_names & (_WEB_FRAMEWORKS | _API_FRAMEWORKS)
|
|
216
|
-
_localized = self._localized_adapter_frameworks(
|
|
285
|
+
_localized = self._localized_adapter_frameworks(all_paths, stacks, _app_frameworks)
|
|
217
286
|
framework_names = framework_names - _localized
|
|
218
287
|
|
|
219
288
|
if len(stack_names) >= 2 and self._is_fullstack(stacks):
|
|
@@ -240,7 +309,7 @@ class TypeClassifier:
|
|
|
240
309
|
|
|
241
310
|
if framework_names & _API_FRAMEWORKS:
|
|
242
311
|
if not (stack_names & _JVM_STACKS) or self._api_is_center_of_gravity(
|
|
243
|
-
|
|
312
|
+
all_paths, stacks, entry_points
|
|
244
313
|
):
|
|
245
314
|
return "api"
|
|
246
315
|
# A JVM API framework is present but is NOT the repo's center of gravity.
|
|
@@ -249,7 +318,7 @@ class TypeClassifier:
|
|
|
249
318
|
# locatable minority module). Do not headline the whole system as an API —
|
|
250
319
|
# the framework stays in stacks[].frameworks as a secondary capability and
|
|
251
320
|
# the primary type falls back to the structural center of gravity.
|
|
252
|
-
if self._is_multi_module(
|
|
321
|
+
if self._is_multi_module(all_paths):
|
|
253
322
|
return "library"
|
|
254
323
|
|
|
255
324
|
# All app-defining frameworks were localized to optional adapter submodules
|
|
@@ -277,7 +346,7 @@ class TypeClassifier:
|
|
|
277
346
|
# structured codebase (e.g. JobRunr: core + per-framework adapter modules).
|
|
278
347
|
# This is checked BEFORE the weak `bin/`-directory CLI heuristic so a build
|
|
279
348
|
# output / wrapper `bin/` dir does not mislabel a library as a CLI.
|
|
280
|
-
if stack_names & _JVM_STACKS and self._is_multi_module(
|
|
349
|
+
if stack_names & _JVM_STACKS and self._is_multi_module(all_paths):
|
|
281
350
|
return "library"
|
|
282
351
|
|
|
283
352
|
# A JUnit 4-only repository is a test suite, not an unknown application.
|
|
@@ -295,14 +364,42 @@ class TypeClassifier:
|
|
|
295
364
|
if any(path.startswith("bin/") for path in flat_paths):
|
|
296
365
|
return "cli"
|
|
297
366
|
|
|
367
|
+
# AUD-616-JUNIT4-SELF. The JUnit4 *framework* repository — the one that
|
|
368
|
+
# provides `junit:junit` — returned `unknown`. `AUD-615-JUNIT4` closed the
|
|
369
|
+
# consumer case (a repo that only *uses* JUnit is a test suite) and does
|
|
370
|
+
# not reach this one: the framework has production sources under
|
|
371
|
+
# `src/main/java`, so the `test` branch above rejects it, and it is a
|
|
372
|
+
# single module, so the `library` branch above rejects it too.
|
|
373
|
+
#
|
|
374
|
+
# One correction to the remedy as filed. The report proposed requiring
|
|
375
|
+
# "no entry point", and JUnit4 has three — `TestRunner`, `JUnitCore` and
|
|
376
|
+
# `Version` all carry a `main()`. Requiring their absence would have left
|
|
377
|
+
# the row open. The multi-module requirement is what was doing the wrong
|
|
378
|
+
# work: module count is a fact about a build layout, not about whether a
|
|
379
|
+
# repository is a library, and it was only ever there because the row that
|
|
380
|
+
# introduced the branch (JobRunr) happened to be multi-module.
|
|
381
|
+
#
|
|
382
|
+
# By the time control reaches here every stronger reading has been ruled
|
|
383
|
+
# out by the branches above — a web framework, a server-side template
|
|
384
|
+
# engine, an API framework at the centre of gravity, a CLI framework, an
|
|
385
|
+
# explicit `cli` entry point, a JUnit4-only test tree, a `bin/` directory.
|
|
386
|
+
# What is left is a JVM repository with sources and no application-defining
|
|
387
|
+
# framework, which is the same inference the multi-module branch already
|
|
388
|
+
# makes. `unknown` there is not caution, it is a vacuous answer about a
|
|
389
|
+
# clearly structured codebase.
|
|
390
|
+
if stack_names & _JVM_STACKS and any(
|
|
391
|
+
path.endswith(_JVM_CODE_EXTS) for path in flat_paths
|
|
392
|
+
):
|
|
393
|
+
return "library"
|
|
394
|
+
|
|
298
395
|
return "unknown" if stacks else None
|
|
299
396
|
|
|
300
|
-
def _is_multi_module(self,
|
|
397
|
+
def _is_multi_module(self, flat_paths: "Sequence[str]") -> bool:
|
|
301
398
|
"""True when the repo has >1 source module (distinct `*/src/...` roots)."""
|
|
302
399
|
_CODE_EXTS = (".java", ".kt", ".kts", ".scala", ".groovy")
|
|
303
400
|
modules = {
|
|
304
401
|
self._module_of(p)
|
|
305
|
-
for p in
|
|
402
|
+
for p in flat_paths
|
|
306
403
|
if p.endswith(_CODE_EXTS)
|
|
307
404
|
}
|
|
308
405
|
modules.discard("")
|
|
@@ -327,7 +424,7 @@ class TypeClassifier:
|
|
|
327
424
|
|
|
328
425
|
def _localized_adapter_frameworks(
|
|
329
426
|
self,
|
|
330
|
-
|
|
427
|
+
flat_paths: "Sequence[str]",
|
|
331
428
|
stacks: Sequence[StackDetection],
|
|
332
429
|
candidate_frameworks: set[str],
|
|
333
430
|
) -> set[str]:
|
|
@@ -345,7 +442,7 @@ class TypeClassifier:
|
|
|
345
442
|
|
|
346
443
|
_CODE_EXTS = (".java", ".kt", ".kts", ".scala", ".groovy")
|
|
347
444
|
module_file_counts: dict[str, int] = {}
|
|
348
|
-
for p in
|
|
445
|
+
for p in flat_paths:
|
|
349
446
|
if not p.endswith(_CODE_EXTS):
|
|
350
447
|
continue
|
|
351
448
|
mod = self._module_of(p)
|
|
@@ -383,7 +480,7 @@ class TypeClassifier:
|
|
|
383
480
|
|
|
384
481
|
def _api_is_center_of_gravity(
|
|
385
482
|
self,
|
|
386
|
-
|
|
483
|
+
flat_paths: "Sequence[str]",
|
|
387
484
|
stacks: Sequence[StackDetection],
|
|
388
485
|
entry_points: Sequence[EntryPoint],
|
|
389
486
|
) -> bool:
|
|
@@ -394,7 +491,7 @@ class TypeClassifier:
|
|
|
394
491
|
``_API_PRIMARY_SHARE`` of the JVM source — otherwise it is a minor capability
|
|
395
492
|
of an engine/platform/library, not the primary type."""
|
|
396
493
|
counts: dict[str, int] = {}
|
|
397
|
-
for p in
|
|
494
|
+
for p in flat_paths:
|
|
398
495
|
if p.endswith(_JVM_CODE_EXTS):
|
|
399
496
|
mod = self._module_of(p)
|
|
400
497
|
counts[mod] = counts.get(mod, 0) + 1
|
|
@@ -452,9 +549,11 @@ class TypeClassifier:
|
|
|
452
549
|
has_api = True
|
|
453
550
|
return has_web and has_api
|
|
454
551
|
|
|
455
|
-
def _count_extension_hits(self,
|
|
552
|
+
def _count_extension_hits(self, flat_paths: "Sequence[str]", stack: str) -> int:
|
|
456
553
|
extensions = _STACK_EXTENSIONS.get(stack, ())
|
|
457
|
-
|
|
554
|
+
if not extensions:
|
|
555
|
+
return 0
|
|
556
|
+
return sum(1 for path in flat_paths if path.endswith(extensions))
|
|
458
557
|
|
|
459
558
|
def _score_to_confidence(self, score: int) -> ConfidenceLevel:
|
|
460
559
|
if score >= 6:
|