fix(scan): a portion sizer that never extrapolates from a sample with no work in it

Measured on the plugin, first run of the adaptive sizer. A 600-visit portion answered in 184 s and
the budget was correctly cut to 262. The next portion, 199 visits, answered in 73 s -- three seconds
of work above the 70 s fixed start -- and 3 s over 199 visits read as 0.015 s each, so the budget
grew thirteen-fold to 3,404 visits. The ceiling killed that portion. The harvest kept the 13
answers it had streamed, which is the mechanism working; the sizing was not.

Two rules. A sample with less than ten seconds of work above the fixed start is not extrapolated
from, because it carries no information about cost per visit. And a budget may double per portion,
never more: the sizing may still be wrong, but by at most one factor of two, which a kill undoes.

1,423 tests pass, nineteen skipped; the sizer control asserts both rules.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
Marius Ciepluch
2026-09-22 22:17:30 +02:00
co-authored by Claude Fable 5.1
parent e3edf11cf8
commit 1aa7f175e9
2 changed files with 18 additions and 3 deletions
+11 -3
View File
@@ -922,6 +922,8 @@ def _batched(
# a mis-estimate is bounded and a kill costs little.
PORTION_TARGET_SECONDS = 120.0
PORTION_FIXED_SECONDS = 70.0 # measured start-and-load on a 4 MB graph
PORTION_MIN_INFORMATIVE_SECONDS = 10.0 # less work than this above the fixed start says nothing about cost per visit
PORTION_MAX_GROWTH = 2.0 # a budget may double per portion, never more
class _PortionSizer:
@@ -965,9 +967,15 @@ class _PortionSizer:
self.budget = max(1.0, self.budget / 2)
logger.info("cpg portion of %d sink visit(s) did not answer in %.0fs; budget halved to %d", visits, seconds, int(self.budget))
return
per_visit = max(0.0, seconds - PORTION_FIXED_SECONDS) / visits
if per_visit > 0:
self.budget = max(1.0, (PORTION_TARGET_SECONDS - PORTION_FIXED_SECONDS) / per_visit)
work = seconds - PORTION_FIXED_SECONDS
# A portion that finished inside the fixed start carries no information about cost per visit -- 3 s
# of work over 199 visits read as 0.015 s each and the next budget grew thirteen-fold to 3,404, which
# the ceiling then killed. Measured on the plugin. So a sample below the floor is not extrapolated
# from, and growth is capped at doubling per step: the sizing may be wrong, but never by more than
# one factor of two per portion, which a kill can then undo.
if work >= PORTION_MIN_INFORMATIVE_SECONDS:
fitted = (PORTION_TARGET_SECONDS - PORTION_FIXED_SECONDS) / (work / visits)
self.budget = max(1.0, min(fitted, self.budget * PORTION_MAX_GROWTH))
logger.info("cpg portion of %d sink visit(s) answered in %.0fs; next budget %d visit(s)", visits, seconds, int(self.budget))