mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
feat(scan): re-ask a killed portion remainder instead of dropping it
The portioner iterated a fixed pre-split list, so a killed portion left its unanswered ids behind: on the plugin the list ran out at 1,263 s of a 3,600 s budget with those remainders lost, and completion sat far below what the budget could buy. It is now a work queue. A portion that comes back short pushes its unanswered ids to the front, to be re-asked at the budget the kill just halved; `fit` splits an over-budget head, so heads shrink toward single requests, and a single request a whole ceiling could not answer is dropped rather than looped on forever. A control asserts both the re-ask and that termination. This is the last piece of portioning-over-cap: streaming keeps what a kill finished, single-attempt timing keeps the sizer honest, coverage tells a too-big portion from a slow one, and the queue keeps working while budget and work remain. 1,425 tests pass, nineteen skipped. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
03011b8fe6
commit
6942446710
@@ -1311,3 +1311,25 @@ def test_the_portioner_asks_single_attempts_and_halves_on_a_short_answer(tmp_pat
|
||||
assert once_calls, "the single-attempt path was not used"
|
||||
# The portion that came back short of its ids was reported as not-whole (False) to the sizer.
|
||||
assert False in seen, seen
|
||||
|
||||
|
||||
def test_the_queue_re_asks_a_killed_remainder_and_terminates() -> None:
|
||||
"""A killed portion's unanswered ids are re-asked at a smaller budget; a lone hard request is dropped."""
|
||||
from openultrasast.model import scan
|
||||
|
||||
attempts: list[list[str]] = []
|
||||
|
||||
def batch(kind: str, requests: dict[str, dict[str, object]]) -> dict[str, list[object]] | None:
|
||||
attempts.append(sorted(requests))
|
||||
# "hard" never answers, whatever size it is asked in; everything else answers.
|
||||
answered = {rid: [] for rid in requests if rid != "hard"}
|
||||
return answered or None
|
||||
|
||||
weights = {"a": 500, "b": 500, "hard": 500, "c": 500}
|
||||
requests = {rid: {} for rid in weights}
|
||||
merged = scan._batched(batch, "taint", requests, weights=weights)
|
||||
assert merged is not None
|
||||
assert {"a", "b", "c"} <= set(merged) and "hard" not in merged, merged
|
||||
# It must stop: "hard" is asked, dropped once it is alone, and never re-queued forever.
|
||||
assert len(attempts) < 40, len(attempts)
|
||||
assert attempts.count(["hard"]) <= 1, attempts
|
||||
|
||||
Reference in New Issue
Block a user