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.
| Field | Examples | Meaning |
|---|---|---|
SourceKind | builtin, module, remote, pipeline, recipe | Where the strategy comes from. |
ObjectiveTags | fairness, sla, value, yield, reliability, eligibility | What it optimizes for. |
Capabilities | weighted, explain, allocation_plan, ranked_select | Optional features it supports. |
OutputShapes | single_selection, ranked_slate, multi_winner, allocation_plan, no_assignment | What a decision looks like. |
ExecutionShapes | linear_route, dag_strategy_step, pipeline_stage, batch_route, shadow, replay | How and where it runs. |
FactRequirements | keyed feature requirements with scope + freshness | The typed facts it reads. |
GovernanceRequirements | certification, learning_dataset, entitlement, quota, promotion, policy_pack | What must be in place to run it. |
StateModel | stateless, process_local, distributed, learner_backed, window_backed | Its state scope. |
Determinism | deterministic, seeded_random, exploratory_bandit, nondeterministic_remote | Reproducibility profile. |
FallbackPolicy | fail_closed, warn, fallback_strategy, use_neutral_score | How 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:
| Recipe | Objective |
|---|---|
fair_distribution | Smooth fair distribution. |
sla_rescue | Deadline / SLA rescue. |
value_optimized_leads | Outcome-value / yield optimization. |
portfolio_balance | Portfolio share balancing. |
availability_aware | Calendar-aware routing. |
quota_cost_guarded | Connector quota and cost guardrails. |
brownout_failover | Brownout / reliability failover. |
eligibility_preferred_match | Recipient eligibility preferred matching. |
ranked_slate_fallback | Ranked alternates before assignment. |
batch_margin_allocation | Batch assignment under constraints. |
custom_strategy_canary | Remote / 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 API | MCP tool |
|---|---|
ListStrategyContracts | routing.strategy.contracts |
GetStrategyContract | routing.strategy.describe_contract |
ValidateStrategyPlan | routing.strategy.validate_plan |
ListStrategyRecipes | routing.strategy.recipes |
GenerateStrategyRecipe | routing.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 DAGrouting.strategysteps.pipeline_stage— a child strategy inside a strategy pipeline.batch_route— allocation-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.
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.
Related
Pipelines
Compose existing strategies into one bounded, validated filter → select → fallback primitive, declared separately from strategy_options, with a stage-by-stage explain trace.
Governance
Ship strategy changes safely — shadow campaigns, experiments, certification, optimization, and governed remote strategies, plus the five distinct evaluation surfaces.