Skip to main content
Version: v0.4

Protection

Overview​

Protection keeps agent conversations stable without making continuity a semantic route. Each request still routes through normal decisions first. After adaptation proposes a model, protection decides whether to hold the current model, allow the switch, or perform a bounded rescue switch.

Key Advantages​

  • Keeps model choices stable inside agent conversations or whole sessions.
  • Protects prefix cache, tool-loop continuity, and handoff cost.
  • Suppresses random exploration during protocol-sensitive steps.
  • Still permits deterministic switches and bounded rescue when evidence is strong enough.
  • Lets sensitive decisions bypass protection through decision-local controls.

What Problem Does It Solve?​

Agent requests are not independent. Tool calls, provider state, prefix cache, and user-visible continuity can make unnecessary model switches expensive or confusing. Protection gives the router a scoped stability guard without turning session continuity into a semantic decision rule.

When to Use​

  • A conversation should keep using the same model unless a switch is worth the stability cost.
  • A full session should remain stable across multiple user-initiated runs.
  • Tool-loop or protocol state makes random exploration unsafe.
  • A weaker protected model should still be escapable through bounded rescue.

Configuration​

global:
router:
learning:
enabled: true
adaptation:
enabled: false
protection:
enabled: true
scope: conversation
identity:
headers:
session: x-session-id
conversation: x-conversation-id
tuning:
idle_timeout_seconds: 300
min_turns_before_switch: 1
switch_margin: 0.05
stability_weight: 1.0

This enables protection independently of online model-choice adaptation. Both components otherwise default to enabled when the master switch is enabled.

Scopes​

ScopeWhat is protectedWhat can re-route
conversationTurns sharing one x-conversation-id.A new x-conversation-id in the same x-session-id.
sessionTurns sharing one x-session-id.Idle timeout or a decision with adaptations.mode: bypass.

Use conversation when each agent run should be independently routed. Use session when one session-level model choice should remain stable across multiple user-initiated runs. In conversation scope, a changed decision resets minimum-turn and continuity-cost preferences. In session scope, a changed decision or conversation alone does not release an eligible current model; idle timeout, bypass, candidate exclusion, or qualified rescue can release it. Neither scope can retain a previous model outside the admitted candidate set.

If the configured identity headers are missing, protection fails open and records diagnostics instead of failing the request.

Guards​

Protection has two guard points:

  • preflight suppresses stochastic sampling during tool/protocol/routine continuation steps.
  • switch guard accepts or rejects adaptation's proposed model using cache, handoff, tool-loop, session, and switch-history cost.

The switch rule is:

switch if proposal_gain >= switch_margin + stability_weight * switch_cost

Protection can also allow a deterministic rescue_switch after repeated backend failures or replay-linked outcomes identify the current model as underpowered. With protection identity present, backend 429 and 5xx responses supply failure observations even when adaptation is disabled; this does not enable adaptive model choice or quality updates. A correction or retry prompt alone is not an owned model outcome, though its signals can change the matched decision. Rescue requires another eligible proposal and sufficient evidence; it is not immediate backend failover.

Rescue is limited to eligible models at a portable turn boundary. It cannot override an active tool-loop lock or a nonportable-context lock. Minimum-turn and session-continuity preferences may yield to rescue when those hard boundaries are absent. Adaptation and protection also preserve any candidate restrictions imposed by the decision's selector, including lexicographic bands.

With protection in apply mode and the configured identity present, a hard ownership lock whose previous model is outside the admitted pool returns 503 instead of transferring ownership or forcing that model. Router-owned Responses history becomes portable after the router has expanded it into the complete stateless request; its retained previous_response_id alone does not create a lock. An active tool loop still does. Unknown response IDs fail during history lookup, before selection.

Progress Gate​

The progress gate evaluates recent outcomes before accepting a model switch proposed by protection or rescue. It does not select a model, call a model or tool, or replace the request's authorization and routing policies.

Configuration​

global:
router:
learning:
enabled: true
protection:
enabled: true
scope: conversation
identity:
headers: {session: x-session-id, conversation: x-conversation-id}
tuning:
progress_gate:
enabled: true
mode: observe
calibration_id: ""
window_size: 8
window_ttl_seconds: 900
min_window_outcomes: 3
min_consecutive_regressions: 2
min_consecutive_recoveries: 2
cooldown_seconds: 120
max_switches_per_window: 2

The gate is disabled when the section or enabled is omitted. enabled and mode are independent: observe records soft evidence decisions without applying them; enforce applies them. Neither mode relaxes hard constraints. The outer protection apply/observe/bypass modes remain separate.

calibration_id identifies the external profile that supplied the thresholds, using name@version. It is required when enabling enforce; an empty value in observe explicitly means uncalibrated defaults. For example, team/session-policy@2026-09 is an identity, not a calibration result. The router neither trains a calibrator nor verifies the quality of that profile. Do not use the synthetic E2E fixture identity as a production calibration.

window_size accepts 1–256 samples; window_ttl_seconds accepts 1–86400 seconds. Threshold relationships are validated after omitted fields receive defaults. For example, reducing the window to two samples also requires reducing the default min_window_outcomes: 3. Evidence TTL is distinct from session idle expiry and the shared store's retention TTL. Retained history cannot be recreated by subsequently increasing the window.

Evidence and decisions​

Response capture records positive output as progress, explicitly reported zero output as no_progress, and unavailable usage as missing. Positive output is a transport-level proxy, not a claim that the answer was correct. Authenticated, owned outcome ingest can refine a turn to progress or regression. Known provider failures remain non-attributable even if a later verdict says failed. tool_error is supported as a typed fact; the response path does not guess tool success from a tool message's presence.

Facts are bounded by count and event time. Duplicate request/model samples merge; different models and unknown request identities do not. Ingest owns the quality verdict, capture owns measured usage, and known infrastructure failure provenance wins over both. Missing confidence, cost, or latency remains unknown, not a fabricated zero. Cost trends use configured pricing; latency trends use observed time to first token. These optional trends are exposed for replay, not used as an additional undocumented selection score.

ReasonCondition
hard_constraint_conflictActive tool loop or non-portable context
cold_startNo observable evidence, including a fully expired window
insufficient_evidenceToo few attributable samples, or unmet streak/trend thresholds
cooldownTime since the last recorded model change is below the configured interval
oscillation_guardThe age-bounded window already contains the configured maximum switches

Escalation requires consecutive same-category negative outcomes and a non-positive progress trend; de-escalation requires consecutive recoveries. Known de-escalation is determined from the same quality index and exact candidate reasoning effort. Missing or incomparable quality evidence uses the conservative escalation path. Missing and non-attributable outcomes neither extend nor break a streak, but do not count toward attributable coverage.

Hard constraints and Replay​

Before applying a proposal or restoring the current model, the integration rechecks the existing candidate boundary, configured backend, context limit, protocol capabilities, portability locks, and the selected algorithm's hard SLO/quality filters. The gate cannot introduce a candidate excluded by policy. If the current model is no longer eligible, a soft suppression cannot restore it. When no eligible outcome remains, the request fails closed through the existing selection error path.

Replay's session_policy.switch_gate contains the evidence version, calibration_id, evidence metrics and their availability, decision/reason, switch direction, candidate names, switch history, and actual application. enforced describes the gate mode; applied, application_reason, and final_model describe what happened. A hard lock can apply in either mode. A rejected rescue is retained in rescue_switch_gate alongside the eventual main-path decision. A non-switch does not invent a gate verdict.

The maintained progress-gate E2E profile exercises request capture, authenticated idempotent feedback, suppression, allowed switches, cooldown, oscillation, and Replay over HTTP. Its dedicated deployment leaves router-replay tests unchanged.

Decision Boundaries​

Most decisions do not need local configuration. Use bypass for hard policy boundaries:

routing:
decisions:
- name: local_privacy_policy
description: Keep privacy-sensitive traffic on the local model.
priority: 200
modelRefs:
- model: local-private-model
adaptations:
mode: bypass

Use observe to collect diagnostics without changing the final model:

adaptations:
protection:
mode: observe

Diagnostics​

x-vsr-learning-methods: protection
x-vsr-learning-actions: protection=hold_current
x-vsr-learning-scopes: protection=conversation
x-vsr-learning-reasons: protection=cache_cost_high

Client UIs should translate raw actions into user-facing text. For example, hold_current can display as "kept run model", allow_switch as "switch allowed", rescue_switch as "rescue switch", and bypass as "learning bypassed".

Router Replay stores the full protection trace: identity source and hash, protected model, base model, proposal model, final model, switch cost, cache evidence, tool-loop state, mode, scope, action, and reason.