From 83adf049be9ff78bed56f702f2a22710e1930f50 Mon Sep 17 00:00:00 2001 From: Marius Ciepluch <11855163+norandom@users.noreply.github.com> Date: Thu, 10 Sep 2026 08:30:38 +0200 Subject: [PATCH] feat(model): say when the region cap hid most of the repository Paid Memberships Pro yields 4,463 regions from 637 files. The default budget examines 500 of them -- 11% of the repository -- and nothing in the output said so. A report whose silence covers the other 89% invites that silence to be read as a clean bill of health. `budget_exhausted` is a different fact and already reported: that is the JUDGE running out on work already collected. This is the work never being collected. Renders in "What could not be analysed" as: only the N highest-ranked of M regions were examined, and nothing above says anything about the rest. 850 tests pass. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG --- src/openultrasast/model/scan.py | 15 +++++++++++++++ src/openultrasast/reports.py | 4 ++++ tests/test_model_scan.py | 27 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/openultrasast/model/scan.py b/src/openultrasast/model/scan.py index b8a66cc..55f5a69 100644 --- a/src/openultrasast/model/scan.py +++ b/src/openultrasast/model/scan.py @@ -152,6 +152,21 @@ def scan_repository( # `add_filter("hook", )`. See `mapping.php_hook_callbacks`. hooks = _hook_callbacks(root, regions) + # The region cap is a COVERAGE fact, not a tuning knob nobody needs to hear about. Paid Memberships Pro + # yields 4,463 regions from 637 files, so a default budget of 500 examines 11% of the repository -- and + # a report that does not say so invites its silence to be read as a clean bill of health for the other + # 89%. `budget_exhausted` is a different thing: that is the JUDGE running out, this is the work never + # being collected at all. + if len(ordered_regions) > limits.max_regions: + degradations.append( + { + "stage": "model", + "reason": "regions_truncated", + "examined": limits.max_regions, + "total": len(ordered_regions), + } + ) + # Phase 1: collect the whole scan's questions, for at most `max_regions` regions. Nothing is asked yet. work: list[tuple[str, ScanRegion, ArbiterSpec]] = [] for region in ordered_regions[: limits.max_regions]: diff --git a/src/openultrasast/reports.py b/src/openultrasast/reports.py index e5fadc1..be66bd7 100644 --- a/src/openultrasast/reports.py +++ b/src/openultrasast/reports.py @@ -436,6 +436,10 @@ _COVERAGE_DEGRADATIONS: dict[str, str] = { "files_unparsed": "{count} file(s) could not be parsed and are absent from the graph: {files}.", "query_failed": "A `{kind}` query failed for {requests} region(s), so those regions were not decided.", "budget_exhausted": "The model-call budget ran out with {regions_unasked} region(s) still unasked.", + "regions_truncated": ( + "Only the **{examined} highest-ranked of {total} regions** were examined. Nothing above says anything " + "about the rest of the repository." + ), "region_failed": "The region `{path}` could not be arbitrated.", } diff --git a/tests/test_model_scan.py b/tests/test_model_scan.py index 84b9299..ba09a74 100644 --- a/tests/test_model_scan.py +++ b/tests/test_model_scan.py @@ -390,3 +390,30 @@ def test_a_query_the_engine_could_not_answer_is_recorded_not_ignored() -> None: assert failures, "a query that could not be asked must be reported" assert failures[0]["kind"] == "taint" assert result.findings == () + + +def test_a_region_cap_that_hides_most_of_a_repository_is_reported() -> None: + """Paid Memberships Pro yields 4,463 regions from 637 files. A default budget of 500 examines 11% of it, + and a report that does not say so invites its silence to be read as a clean bill of health for the rest. + """ + from openultrasast.model.regions import ScanRegion + from openultrasast.model.scan import ScanBudget, scan_repository + + class _Backend: + def available(self) -> bool: + return True + + def build(self, root, *, language=""): # type: ignore[no-untyped-def] + from openultrasast.cpg.backend import CpgResult + + return CpgResult(cpg_path=Path("cpg.bin"), run=lambda query, params: []) + + regions = [ + ScanRegion(path=f"f{n}.py", function="handler", language="python", families=("injection",), rank=0.5, source="entry_point") + for n in range(30) + ] + result = scan_repository(Path("."), regions, backend=_Backend(), budget=ScanBudget(max_model_calls=0, max_regions=10)) + + truncated = [d for d in result.degradations if d.get("reason") == "regions_truncated"] + assert truncated, "the cap hid two thirds of the regions and said nothing" + assert truncated[0]["examined"] == 10 and truncated[0]["total"] == 30