Routing Strategies

Scoring

Score candidates on a blend of signals — priority, multi-objective, SLA deadline, yield, portfolio balance, and predicted value.

Scoring strategies compute a number for every candidate and route to the top score. Unlike balance strategies, they weigh multiple signals — load, capacity, deadline, geography, fairness, margin, conversion — and most of them are explainable: they return a per-dimension breakdown so you can see exactly why a candidate won.

Five scoring strategies are built in; predicted_value is provided by the optional predicted module.

StrategyModesCapabilitiesNotes
Priority scorescoringweighted, explainBlends weight, load, availability.
Multi-objective scorescoring, weightedweighted, explainThe flagship tunable scorer.
SLA deadlinescoringexplainDeadline- and catchall-aware.
Yield optimizedscoring, weightedweighted, explainMaximizes expected value.
Portfolio balanceweighted, scoringweighted, explainCaps recent share.
Predicted valuescoringexplainOptional predicted module.

Fallbacks are first-class

Every scoring strategy takes a fallback_strategy (or base_strategy) param naming another registered strategy — usually smooth_weighted_round_robin. It is used to break ties and to route when the primary scorer has no confident answer. The fallback name is resolved through the registry.

Priority score

Modes: scoring · Capabilities: weighted, explain

Scores each candidate by a blend of weight, current load, and availability, then routes to the top score. Ties break via smooth weighted round-robin, and it returns explainable per-candidate scores.

Why use it. A step up from plain balance when you want load and availability to nudge the weighted split but you don't need the full multi-objective knob set. It's the "weighted, but load-aware and explainable" option.

How to configure it. No exposed sliders in the product surface — it derives its blend internally from Candidate.Weight, CurrentLoad, and availability facts. Enable explain on the route to capture the per-candidate breakdown.

Where it fits. The Select stage. Reach for multi-objective score when you need to tune the weights.

Multi-objective score

Modes: scoring, weighted · Capabilities: weighted, explain

The flagship business-objective strategy. It blends capacity, deadline pressure, geography (distance), load, fairness, margin, and confidence into one tunable composite score, and returns a per-dimension breakdown for full explainability. If you only adopt one scorer, this is it.

Why use it. When "the best recipient" depends on several competing goals at once — you want the fast, nearby, high-margin, not-overloaded recipient, and you want to control the relative pull of each factor. Every dimension has a weight slider, so you tune the objective without writing code.

How to configure it.

Prop

Type

{
  "strategy": "multi_objective_score",
  "options": {
    "capacity_weight": 2,
    "deadline_weight": 3,
    "distance_penalty_weight": 1,
    "min_confidence": 0.2
  }
}

Where it fits. The Select stage. It consumes the typed feature snapshot (capacity ratio, margin, confidence, and more), which makes its decisions replayable.

SLA deadline

Modes: scoring · Capabilities: explain

Deadline-aware routing. Scores recipients by deadline pressure, SLA quality, response time, load, and capacity, preferring those likely to beat the routable's ExpiresAt. A CatchallAt threshold sets fall-through behavior when no candidate can comfortably meet the SLA.

Why use it. When time-to-serve is the objective — support tickets that must be answered within an SLA, leads that must be worked before they go cold, jobs with a hard deadline. It explicitly prefers recipients whose predicted response beats the clock.

How to configure it.

Prop

Type

The catchall_mode enum is the key control:

  • best_effort — still route to the best available candidate even if none beats the SLA.
  • fallback — hand off to fallback_strategy.
  • fail_closed — return no assignment rather than route to a recipient that will miss the SLA.

Where it fits. The Select stage, reading SLA-compliance and response-time features from the snapshot.

Yield optimized

Modes: scoring, weighted · Capabilities: weighted, explain

The revenue strategy for lead and order routing. It maximizes expected yield — conversion rate × outcome value × quality × margin — while penalizing return rate and SLA risk, and it gates on a minimum-outcomes confidence floor so it doesn't over-trust recipients with thin history.

Why use it. When routing decisions have a dollar value and you want to send each opportunity to whoever will extract the most from it. It is the strategy for marketplaces, lead distribution, and order assignment where conversion and margin matter more than even load.

How to configure it.

Prop

Type

Cold-start protection

min_outcomes matters: a brand-new recipient with one lucky conversion looks infinitely good. Set a floor so yield scoring only trusts recipients with enough history, and let the fallback distribute to the rest.

Where it fits. The Select stage, driven by conversion, outcome-value, quality, return-rate, and margin features from the snapshot.

Portfolio balance

Modes: weighted, scoring · Capabilities: weighted, explain

Keeps each recipient's (or metadata group's) recent assignment share under a configured cap, rebalancing toward under-served recipients. Concentration control for routing.

Why use it. When no single recipient — or no single group of recipients (one region, one vendor, one team) — should dominate recent volume, regardless of how attractive they score. It wraps a base strategy and simply vetoes candidates that are already over their share.

How to configure it.

Prop

Type

Where it fits. The Select stage. Its batch-scale counterpart is the portfolio quota allocator, which enforces caps across a whole batch instead of per-decision.

Predicted value

Modes: scoring · Capabilities: explain · Module: predicted

Scores candidates by predicted outcome value learned from outcome and quality history, routing to the highest predicted payoff.

Why use it. When you want a learned estimate of what each recipient will deliver, rather than a hand-tuned blend. It's the supervised sibling of yield optimized: instead of you weighting the factors, it predicts the value directly from history.

How to configure it. It is an optional module and must be registered with the outcome and quality stores it learns from (RegisterPredicted in modules/strategies/predicted/register.go). Without those repositories it is not available. It has no product-surface sliders — its behavior comes from the learned model.

Optional module

predicted_value (with Thompson sampling and LinUCB) ships in the predicted module, not the built-in pack. It requires the outcome and quality repositories and, for the bandits, the strategy state store for durable posteriors.

Where it fits. The Select stage. See Learning for the bandit strategies in the same module.

Behavior change: per-candidate outcome features

Each candidate is now scored on its own outcome history. Previously the per-recipient outcome stats query ignored the recipient filter and returned the pool's highest-total-value recipient, so every candidate received identical conversion_rate, avg_outcome_value, and total_outcomes features — the predicted score could not tell recipients apart by track record. Those features now differ per candidate, so predicted-value rankings will shift toward recipients with genuinely stronger histories. The same corrected features feed the learning bandits, which read them as context.