Routing Strategies

Contracts

The governance layer — strategy contracts, versioned recipes, the contract/recipe API and MCP surface, feature snapshots, and the durable strategy state plane.

Contracts are the machine-readable governance layer the rest of the routing surface stands on. A descriptor renders the authoring UI; a contract tells headless tools and validators what a strategy can safely consume and produce. With contracts in place, strategies can be versioned, replayed, experimented on, and gated before they ever touch live traffic.

They are also the foundation the certified-strategy marketplace stands on: a strategy someone else authored can only be trusted in your routing once it resolves to a contract and passes certification against it. Pricing that adoption now ships as an opt-in surface (artifact_pricing.strategy.enabled): a price pins an exact strategy version, so publishing a new version never inherits an old price, and the price is resolved at promotion — the gate a tenant passes before adoption. An unpriced strategy is not a free one.

Still on the roadmap: returning the adopted terms on the wire. The price is load-bearing at the gate — a resolution failure refuses promotion — but the promotion response carries no price field, so a client cannot yet read the terms it accepted.

The one rule everything else enforces

Every new strategy must ship its descriptor and contract in the same change, and no strategy name, alias, recipe reference, or MCP/API surface may exist unless it resolves to a contract. A contract that declares a ranked or allocation output but doesn't advertise the matching capability fails Validate() — the inconsistency is caught at registration, not in production.

Strategy contract

A machine-readable, versioned declaration of everything a strategy needs, emits, and guarantees — validated for compatibility before it runs.

FieldExamplesMeaning
SourceKindbuiltin, module, remote, pipeline, recipeWhere the strategy comes from.
ObjectiveTagsfairness, sla, value, yield, reliability, eligibilityWhat it optimizes for.
Capabilitiesweighted, explain, allocation_plan, ranked_selectOptional features it supports.
OutputShapessingle_selection, ranked_slate, multi_winner, allocation_plan, no_assignmentWhat a decision looks like.
ExecutionShapeslinear_route, dag_strategy_step, pipeline_stage, batch_route, shadow, replayHow and where it runs.
FactRequirementskeyed feature requirements with scope + freshnessThe typed facts it reads.
GovernanceRequirementscertification, learning_dataset, entitlement, quota, promotion, policy_packWhat must be in place to run it.
StateModelstateless, process_local, distributed, learner_backed, window_backedIts state scope.
Determinismdeterministic, seeded_random, exploratory_bandit, nondeterministic_remoteReproducibility profile.
FallbackPolicyfail_closed, warn, fallback_strategy, use_neutral_scoreHow it degrades.

Cross-field rules are enforced. Validate() rejects contradictions — an allocation output without the allocation_plan capability, a ranked output without ranked_select, a replayable remote strategy that doesn't record remote evidence, or an exploratory bandit that lacks a learning-dataset governance requirement. That last rule is why Thompson sampling and LinUCB must declare a dataset before they can be certified.

Strategy recipe

A versioned authoring template for a business objective. A recipe generates route or pipeline starter payloads and verification checklists — but it never executes a strategy. It is the packaging layer between "I want fair distribution" and a concrete, contract-backed route.

Built-in recipes cover the common objectives:

RecipeObjective
fair_distributionSmooth fair distribution.
sla_rescueDeadline / SLA rescue.
value_optimized_leadsOutcome-value / yield optimization.
portfolio_balancePortfolio share balancing.
availability_awareCalendar-aware routing.
quota_cost_guardedConnector quota and cost guardrails.
brownout_failoverBrownout / reliability failover.
eligibility_preferred_matchRecipient eligibility preferred matching.
ranked_slate_fallbackRanked alternates before assignment.
batch_margin_allocationBatch assignment under constraints.
custom_strategy_canaryRemote / custom strategy rollout.

missing_dependencies

A recipe reports missing_dependencies when the module that owns its target strategy isn't installed. For example, value_optimized_leads can reference predicted_value, which belongs to the contrib predicted module; a tenant remote strategy requires a promoted deployment. Install or enable the owner before applying the generated starter.

API and MCP surface

Contracts and recipes are read-only governance objects exposed on the Routing API and mirrored to MCP clients:

Routing APIMCP tool
ListStrategyContractsrouting.strategy.contracts
GetStrategyContractrouting.strategy.describe_contract
ValidateStrategyPlanrouting.strategy.validate_plan
ListStrategyRecipesrouting.strategy.recipes
GenerateStrategyReciperouting.strategy.generate_recipe

ValidateStrategyPlan checks a planned strategy or pipeline against the surface it will run on, without executing strategy code. Validate for the surface you intend:

  • linear_route — normal route requests and pool defaults.
  • dag_strategy_step — workflow DAG routing.strategy steps.
  • pipeline_stage — a child strategy inside a strategy pipeline.
  • batch_routeallocation-plan consumers.
  • dry_run, shadow, replay — non-production simulation modes.

A strategy can have valid params and still fail plan validation if its output shape, execution shape, replay mode, required facts, or governance requirements are incompatible with the surface.

Strategy feature snapshot

A typed, read-only fact contract loaded once per decision — after eligibility filtering, before selection — and handed to the strategy as SelectRequest.Features. Strategies read the snapshot; they never query outcome, quality, or connector repositories directly. That single discipline is what makes decisions replayable, shadowable, and fair to compare in experiments.

The loader projects candidate facts under canonical numeric keys, including:

conversion_rate            avg_outcome_value       total_outcomes
quality_score              quality_confidence      return_rate
response_time              sla_compliance          compliance_score
current_load_ratio         available_capacity      attribute_match_score
margin                     cost                    portfolio_share
availability.*             reliability.*           eligibility.*

Rules the snapshot enforces:

  • known bounded values are validated as finite numbers; confidence values must be in [0,1];
  • missing data is an absent key, not a zero — a strategy can tell "no data" from "genuinely zero";
  • decision metadata records only compact status keys (strategy_features_status, strategy_features_version, strategy_features_candidates) — never the full feature map.

If no loader is configured, selection is unchanged. A partial loader error warns and continues by default, preserving local capacity and deadline facts; a strategy can opt into fail-closed loading with strategy_options.require_features=true. Invalid snapshots always fail closed.

Strategy state plane

Feature snapshots are decision inputs. The state plane is the routing-owned, mutable state that changes because a decision was accepted — SWRR current weights, fair-catchup assignment windows, bandit posteriors, cooldowns, exploration budgets, scheduler cursors. It lives in application/routing/strategystate/, separate from both feature snapshots and outcome-learning evidence.

Read a snapshot, emit an intent. During Select a strategy may read only the frozen StrategyStateReadSet on SelectRequest.State. It does not write durable state. Instead it returns deterministic StateMutationIntents on the response; the routing finalizer applies them only after the decision is durably accepted, then records receipts linked to the decision. Retries reuse the idempotency key so a duplicate finalizer attempt returns the same receipt rather than re-applying the transition.

frozen StrategyStateReadSet StateMutationIntents on response apply intents receipt linked to decision decision durably accepted Strategy.Select State plane Routing finalizer

State is addressed by a strict key. A valid scope carries tenant_id, a route identity (routing_bundle_id or route_version_id), pool_id, strategy_name, and a version (strategy_version or contract_version); the full key adds a kind and entity_key. Built-in kinds include swrr_weights, assignment_window, fairness_debt, cooldown, exploration_budget, bandit_model_state_ref, and scheduler_cursor. The plane exposes Inspector / Ledger / Mutator / Reader / Snapshotter / Store ports.

Why state lives outside Select

Mutating state inside Select would make the strategy unreplayable and unsafe to shadow-evaluate — a shadow run would corrupt live state. Read-snapshot / intent / receipt keeps selection pure, so the same decision can be replayed, shadowed, and audited without side effects. Postgres is the authoritative audit/replay store; Redis may be a fast path behind the shared adapter but never the sole authority.