How Session Pinning State Is Persisted and Invalidated in WorkWeave Router
WorkWeave Router persists session pinning state in a PostgreSQL table using a store facade with Get, Upsert, and Clear operations, while invalidating pins through TTL expiration, consecutive error thresholds, degenerate response detection, loop detection, or policy-deadline violations.
WorkWeave Router maintains multi-turn conversation coherence by "pinning" specific models to user sessions. According to the WorkWeave Router source code, this session pinning state is durably stored in PostgreSQL and automatically invalidated when health signals indicate the pin is stale, erroneous, or otherwise undesirable.
PostgreSQL Storage Schema
The underlying persistence layer relies on a table named session_pins defined in the migration file db/migrations/0010_session_pin_last_served_model.up.sql. This schema stores the correlation between an installation, a role, and the selected model.
Key columns include:
installation_idandrole— the composite key identifying the conversation contextlast_served_model— the pinned model identifierexpires_at— timestamp for TTL-based evictionaction_history— a JSONB column capturing runtime attributes such as consecutive error counters, usage statistics, and TTL metadata
The Session Pin Store Interface
The router interacts with persistence through an abstraction defined in internal/router/sessionpin/store.go. This store acts as a thin I/O façade over the concrete repository implementation.
The interface exposes three primary operations:
Get(ctx, installationID, role)— retrieves the current pin if it existsUpsert(ctx, pin)— inserts a new pin or updates an existing one using PostgreSQL'sON CONFLICT ... DO UPDATEsemanticsClear(ctx, installationID, role)— removes the pin entirely
The concrete SQL implementation resides in internal/postgres/session_pin_repo.go, where Upsert executes an INSERT ... ON CONFLICT (installation_id, role) DO UPDATE statement to atomically persist state changes.
Loading and Persisting Pins
When processing a request, the router loads the existing pin to determine if a model has already been selected for the session. If the routing logic decides to pin a model—whether via the /force-model endpoint, a turn-type heuristic, or the HMM side-car—it writes the state back through the store.
Loading an existing pin:
pin, ok, err := router.SessionPinStore.Get(ctx, installationID, role)
if err != nil {
// Handle repository error
return err
}
if ok {
// Pin exists: proceed with pin.Model, check pin.ExpiresAt, etc.
selectedModel = pin.Model
}
Creating or updating a pin:
pin := router.SessionPin{
InstallationID: installationID,
Role: role,
Model: chosenModel,
ExpiresAt: time.Now().Add(pinTTL),
ActionHistory: updatedHistoryJSON,
// ... additional metadata
}
if err := router.SessionPinStore.Upsert(ctx, pin); err != nil {
log.Error("session pin upsert failed", "err", err)
}
State Evolution and Runtime Attributes
After a pin is loaded, the router enriches it with ephemeral runtime data stored in the action_history JSONB column. This allows the pin to accumulate health signals across multiple turns.
The helper logic in internal/router/percallband/state_snapshot.go and features.go updates this blob after each turn, tracking:
- Consecutive error counters for upstream failure detection
- Usage statistics such as request frequency and model utilization
- TTL boundaries derived from policy configuration
This mutable state snapshot enables the router to make eviction decisions based on historical behavior rather than single-turn heuristics.
Invalidation and Eviction Policies
WorkWeave Router implements five distinct invalidation mechanisms that can clear a session pin:
TTL Expiration — The internal/proxy/pin_eviction.go file checks the expires_at column against the current time. When the deadline passes, the pin is considered stale and eligible for eviction.
Consecutive Upstream Errors — The evictPinAfterDegenerateResponse function monitors the error counter stored in action_history. After a configurable threshold of non-retryable errors, the pin is removed to prevent repeated routing to a failing model.
Degenerate Response Detection — If a model returns low-confidence output, repetitive text, or other classifications defined as "degenerate," the router triggers the same evictPinAfterDegenerateResponse path to invalidate the pin.
Loop Detection — The turn-loop logic in internal/proxy/turnloop.go analyzes conversation patterns for repetition cycles. When a loop is detected, the system clears the pin to force a fresh model selection on the next turn.
Policy-Deadline Fallback — In internal/proxy/service.go, when the HMM side-car misses its decision deadline, the router evaluates the existing pin's freshness. If the pin exceeds policy-defined staleness thresholds (the "kill-switch" logic), the system drops the pin and falls back to default routing.
When any eviction rule fires, the store either calls Upsert with an empty pin state or invokes Clear to delete the row, effectively removing the persisted state.
Asynchronous Write-Back Behavior
After a successful turn completes, the router writes the updated pin state back to PostgreSQL. This write-back includes the latest last_served_model, incremented usage counters, refreshed TTL values, and updated action_history.
Errors during this phase are logged but do not abort the user request. As implemented in internal/proxy/service.go:
observability.Get().Error("session pin usage writeback failed",
"installation_id", installationID,
"role", role,
"error", err)
This fire-and-forget approach ensures that database latency or transient failures do not impact response times, at the cost of potentially losing the most recent usage statistics.
Summary
- Session pins are stored in a PostgreSQL
session_pinstable with a composite key ofinstallation_idandrole. - The
internal/router/sessionpin/store.gofaçade providesGet,Upsert, andClearmethods, backed byinternal/postgres/session_pin_repo.go. - Runtime state including error counts and usage stats accumulates in the JSONB
action_historycolumn via helpers ininternal/router/percallband/state_snapshot.go. - Invalidation triggers include TTL expiration (
pin_eviction.go), consecutive errors, degenerate responses, loop detection (turnloop.go), and policy-deadline violations (service.go). - Write-back operations occur asynchronously after request completion, with errors logged non-fatally.
Frequently Asked Questions
How does WorkWeave Router store session pinning state?
WorkWeave Router stores session pinning state in a PostgreSQL table named session_pins, using columns for the installation ID, role, selected model, expiration timestamp, and a JSONB blob for runtime metadata. The internal/postgres/session_pin_repo.go file implements the actual SQL SELECT and UPSERT logic, while internal/router/sessionpin/store.go provides the abstracted interface used by the proxy service.
What triggers the invalidation of a session pin?
Session pins are invalidated by five primary mechanisms: TTL expiration when expires_at passes, consecutive upstream errors exceeding configured thresholds, detection of degenerate model responses, conversation loop detection in internal/proxy/turnloop.go, and policy-deadline violations when the HMM side-car fails to respond within acceptable windows.
What happens if the session pin write-back fails?
If the asynchronous write-back operation fails after a successful turn, the error is logged via the observability package in internal/proxy/service.go but the user request completes normally. This design prioritizes low latency over strict consistency, meaning the pin state may lag behind reality until the next successful write or until the pin is evicted and recreated.
Can session pins be cleared manually?
Yes, explicit clearing is supported through the store's Clear method, which removes the row from PostgreSQL. This is typically invoked via administrative endpoints such as /unforce-model, which allows operators or automated systems to reset routing decisions for specific installation-role pairs without waiting for TTL expiration or error-based eviction.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →