Pulls the per-D score computation out of KvAwarePolicy.select
into a top-level pure function that takes primitives. The
in-method behavior is unchanged — the loop now calls
score_candidate() instead of inlining the arithmetic.
Motivation:
Algorithm 1 (KVC_ROUTER_ALGORITHM.md §3.1) is the routing
core. Until now its only API was select(), which requires
building TraceRequest + SingleNodeTopology + RoutingState
to test even a single lex-score property. After this
extraction, unit tests can drive the four-tuple score
directly with integers.
What changed:
- Added module-level CandidateScore type alias.
- Added score_candidate(*, overlap, sticky, inflight,
assigned, mean_assigned, sticky_bonus,
load_floor_bonus) -> CandidateScore.
- KvAwarePolicy.select() loop body collapsed to a
score_candidate() call; sticky now bool (was int)
inside the call site.
- Moved the load-floor docstring from KvAwarePolicy
onto score_candidate where the formula lives.
Verified pure:
- same kwargs -> same tuple
- overlap=5 beats sticky-only (no load_floor): (5,0,0,0) > (1,1,0,0)
- load_floor gated off when sticky=True
No behavior change; follow-up commit adds the unit tests
this refactor enables.