Model Routing & Lifecycle

How Models Enter the System

Models reach the routing pool through two paths: static seed data at install time, and dynamic discovery at runtime.

Path 1: Static Seed (Install Time)

During container startup, docker-entrypoint.sh runs the following sequence:

[1/5] prisma migrate deploy           Schema and tables
[2/5] sync-provider-registry.ts       Providers from providers-registry.json
[3/5] seed.ts                         Agents, roles, model profiles, agent routing config
[4/5] detect-hardware.ts              Hardware profile for local AI
[5/5] source volume bootstrap          Sandbox workspace git init

Step 2 syncs the provider registry from packages/db/data/providers-registry.json. This creates or updates ModelProvider rows with connection details, auth methods, pricing, and modelRestrictions (an allowlist of model ID patterns the provider credential can access). Provider status and credentials are preserved on re-sync.

Step 3 seeds model profiles from packages/db/data/model-profiles.json. These are pre-evaluated profiles with hand-tuned dimension scores (codegen, reasoning, toolFidelity, etc.) and profileSource: "evaluated". Only models known to work with the default credential tier are included. The seed also:

Pre-evaluated profiles are protected from being overwritten by dynamic discovery.

Path 2: Dynamic Discovery (Runtime)

Local models (automatic): The local LLM provider (Docker Model Runner / Ollama) is checked on every page load of the AI Workforce providers page and during first-run bootstrap. If the provider is reachable, the platform calls its /v1/models endpoint, creates DiscoveredModel rows, and profiles them automatically using quality tier baselines from FAMILY_TIERS.

Cloud providers (manual): Cloud provider models are discovered when an admin clicks “Discover Models” on the provider detail form. This calls the provider’s model list API (e.g., Anthropic /v1/models), creates discovery records, then profiles each model. Models that don’t match the provider’s modelRestrictions allowlist are automatically retired with the reason “Model not accessible with provider credential type.”

There is currently no scheduled automatic discovery for cloud providers that support normal model listing APIs. If a provider releases a new model family, it will not appear until an admin manually triggers discovery.

Non-discoverable providers are handled differently. For providers such as Codex that cannot use /v1/models, the platform uses a curated known-model catalog plus a scheduled catalog reconciliation pass. The reconciler checks official provider documentation for candidate model IDs and deprecation signals, reseeds the runtime model catalog from the curated entries, and reports any new official candidates that are not yet approved for routing. Documentation makes a model a candidate; runtime probe and seeded metadata make it routable.

Quality Tiers

Every model is assigned a quality tier based on its model ID prefix. The tier system replaces opaque 0-100 scores as the primary configuration surface for admins.

Tier Examples Use Case
Frontier Claude Opus 4.x, Claude Sonnet 4.x, GPT-5, o1/o3/o4 Build Studio, complex code generation, multi-step tool orchestration
Strong Claude Haiku 4.x, Gemini 2.5 Pro, GPT-4o Admin tasks, compliance, finance, most agent work
Adequate Claude 3 Haiku, Gemini 2.5 Flash, GPT-4o-mini Basic conversation, simple queries
Basic Llama, Phi, Qwen, Mistral, DeepSeek (local models) Local-only, no cloud cost, limited capabilities

Tier assignment uses longest-prefix matching against the model ID. For example, claude-haiku-4-5-20251001 matches the prefix claude-haiku-4 and is assigned the strong tier.

Each tier maps to baseline dimension scores and minimum thresholds:

Tier Baseline Scores Minimum Thresholds (for agent config)
Frontier codegen: 90, toolFidelity: 90, reasoning: 90 codegen >= 85, toolFidelity >= 85, reasoning >= 85
Strong codegen: 75, toolFidelity: 75, reasoning: 75 codegen >= 70, toolFidelity >= 70, reasoning >= 70
Adequate codegen: 55, toolFidelity: 55, reasoning: 55 codegen >= 50, toolFidelity >= 50, reasoning >= 50
Basic codegen: 35, toolFidelity: 35, reasoning: 35 No minimums

Pre-evaluated profiles (from model-profiles.json) may have scores that differ from the tier baselines. These manually tuned scores are more accurate and are preserved across discovery cycles.

Agent Model Configuration

Each agent has an AgentModelConfig row that controls which models it can use:

Field Purpose
minimumTier The lowest quality tier the agent will accept (e.g., “frontier” for build-specialist)
budgetClass Cost strategy: minimize_cost, balanced, or quality_first
pinnedProviderId Exceptional preference override for a specific eligible provider (optional)
pinnedModelId Exceptional preference override for a specific eligible model (optional)

Default configurations are seeded during installation. Admins can override them via the platform UI. The normal posture is unpinned: seed and first-run bootstrap leave pins empty, and startup emits a [pin-audit] warning when a persisted pin exists. Use a pin only when a provider/model dependency is intentional and documented; use tier, capability, sensitivity, residency, and budget controls for normal routing. The seed respects existing rows – if an admin has already configured an agent, the seed will not overwrite it.

Default Agent Tiers

Agent Minimum Tier Budget Pinned To
build-specialist strong quality_first (auto)
coo strong balanced (auto)
platform-engineer strong balanced (auto)
admin-assistant strong balanced (auto)
ea-architect adequate balanced (auto)
onboarding-coo basic minimize_cost (auto)

How Routing Selects a Model

When an agent needs to call an LLM, the routing pipeline runs in this order:

  1. Load AgentModelConfig for the agent. If a DB row exists, use its tier and budget. Otherwise, fall back to code defaults.
  2. Convert tier to minimum dimensions. For example, frontier becomes { codegen: 85, toolFidelity: 85, reasoning: 85 }.
  3. Load all endpoint manifests. Query ModelProfile where modelStatus is active or degraded, joined with ModelProvider where status is active or degraded.
  4. Screen the task payload before dispatch. The provider setup facts are not a one-time approval for every future request. Before an LLM call leaves the install, DPF screens the actual system prompt, messages, tool-call arguments, and tool results for sensitive data classes such as customer and employee records, financial and clinical data, student/youth records, legal privilege, criminal-justice information, safety cases, credentials, source code, regulated decisions, and unknown governed data. Sensitive-data policy packs compile every detected class into the shared data-policy engine; there is no second vertical policy authority. Classification requires value-shaped evidence or a governed field/path hint: ordinary instruction prose such as “let me know,” role vocabulary such as “employee,” and static schema vocabulary such as a parameter named password do not prove that source code, an employee record, or a password value is present. Static tool declarations remain part of the tamper-evident payload hash and are revalidated before dispatch. The screen records only hashes, data classes, pack/policy versions, and transformation status; it does not store raw prompt content or detected secret values.
  5. Compile data-policy constraints into the request. If the payload is public or ordinary internal work, the low-cost eligible providers can remain available. If the payload contains confidential, restricted, or unknown governed data, the data-governance decision can narrow allowed providers, deny external services, require local/private routing, require masking or tokenization, or stop for human review. Provider cost, model quality, capacity, and pins cannot loosen that result.
  6. Hard filter (V2 pipeline). Exclude models that fail any of:
    • Provider is outside RequestContract.allowedProviders
    • Provider is present in RequestContract.deniedProviders (deny wins if both lists contain it)
    • Status not active/degraded
    • Model class not chat or reasoning
    • Sensitivity clearance doesn’t cover the request
    • Missing required capability (e.g., tool use)
    • Any dimension score below the minimum threshold

    preferred endpoint replaces the cost/quality winner only when it remains in the eligible candidate set. A pin cannot override provider allow/deny, sensitivity, residency, capability, model-class, status, or other hard exclusions. If its target is unavailable or excluded, routing records a warning and keeps the V2-selected route.

  7. Revalidate at the dispatch seam. Immediately before every provider attempt—including retries and fallbacks—DPF screens the actual payload again and compares its safe hash, policy outcome, obligations, destination, and asset/classification/authority versions with the route-time receipt. If anything changed, the call stops and must be routed again. A tool-stripping reroute receives a new receipt for the reduced payload rather than reusing evidence for content that is no longer being sent.

Provider allow/deny constraints are request-level hard policy, distinct from the provider registry’s modelRestrictions discovery allowlist. An explicitly empty allowedProviders list means no provider is eligible; routing returns no endpoint rather than treating the empty list as “allow any.” Cost, quality, provider health, capacity, and pin preferences only rank the providers that remain after the hard filter.

The work changes which providers are eligible

DPF evaluates the work being done, not only the company and provider name. The activity contract can identify governed data assets and fields, the approved processing purpose, and a bounded workload class. The company archetype and its current value-stream stage supply conservative defaults only when governed activity detail is not already available.

That means one connected provider can be available for public marketing copy and unavailable for patient, student, financial-customer, public-sector, source-code, or credential work in the same company. Governed data classification always wins over an archetype or activity default. Missing or conflicting classification on high-risk work requires review and cannot be relaxed by cost, model quality, occupation, or a provider pin.

An employee’s occupation focuses the explanations and recommendations they see. It does not grant data access, expand coworker or tool authority, or make a blocked provider eligible.

When a caller declares that sensitive details are replaceable, the PDP can authorize DPF to omit, redact, partially reveal, tokenize, or aggregate matched values before contract inference, preview construction, memory projection, tool-result serialization, and provider dispatch. The transformed payload is screened again, while its receipt keeps the source data classes, protection state, and current policy versions. Stable tokens have an opaque, short-lived rehydration handle; the token map stays only inside the controlled runtime and is never written to route logs, memory, previews, vector storage, receipts, or exception text.

If the exact value is material to correctness—or the classifier cannot prove a safe transform—DPF does not mask around the restriction. The task remains on an eligible internal, local, or enterprise route, or is blocked with a short explanation and next action. Response rehydration remains masked until the separate actor, role, purpose, and surface authorization gate approves it.

Evidence belongs to the connected account

Provider claims are not vendor-wide. A business agreement, BAA, DPA, no-training term, regional entitlement, or router allowlist applies only to the connected account it was reviewed for. It does not make a personal API key, an individual subscription, or a second tenant for the same vendor suitable.

Open Platform → AI Operations → Providers & Routing, choose a provider, and review Provider trust evidence. Each claim shows whether its evidence is current, missing, expired, rejected, conflicting, or replaced, along with its age, expiry, and safest next action. Operator declarations expire after 90 days unless stronger reviewed evidence replaces them. Expiry removes the claim from restricted-work eligibility immediately; it does not unnecessarily block separately governed public work.

Every governed route may carry a privacy-safe suitability receipt. The receipt records policy and input versions, the selected provider and any router-selected underlying provider, the connection’s channel/account posture, exclusions, obligations, and explanation codes. It deliberately excludes prompts, messages, credentials, vendor account identifiers, organization identifiers, evidence documents, and free-text explanations.

For activity-governed work, the same suitability projection used during provider setup is bound into the live RequestContract before routeEndpointV2 selects a model. Existing operator restrictions can only become narrower: provider allowlists intersect, denials accumulate, and the stricter residency rule wins. A preferred provider/model, cost tuning, model health, or fallback cannot restore a connection removed by this gate.

Open the route decision on the provider detail page or Platform → Audit → Route Decisions to see Provider suitability evidence. It shows the hashed connection reference, execution channel, account class, compiler/policy versions, processing-region obligation, and explanation codes. Workload-class detail is aggregated only after at least five similar decisions; smaller cohorts remain hidden.

Continuous review and rollout

DPF watches the provider catalog, account attestations, contract and regional-entitlement expiry, conflicting/rejected evidence, repeated route failures, and differences between the observed connection and its attested channel, account class, plan, endpoint, or credential kind. These observations create an owner attention item with a direct repair link. Expired, rejected, or mismatched evidence continues to fail closed for restricted work.

Telemetry is advisory. It may lower a recommendation, request review, or tune ordering among connections that are already allowed. It cannot override a data-policy decision, contract boundary, residency requirement, operator denial, or missing account evidence.

The rollout is cumulative and ordered: compiler shadow → admin preview → onboarding recommendation → selected regulated/technical verticals → restricted OpenRouter → evidence enforcement → continuous tuning. The default installation enforces account evidence while leaving tuning advisory. Dental/healthcare, retail, credit-union, training/education, and software-platform journeys use the same work-context derivation and compiler; there is no vertical-specific policy engine.

How Routing Adapts Over Time

Event What Happens
Admin connects a new provider Provider goes active. Admin must click “Discover Models” to populate model profiles.
Admin clicks “Discover Models” Calls provider’s model list API, creates discovery records, profiles using quality tiers.
Model restricted by modelRestrictions Profiling auto-retires it with reason “Model not accessible with provider credential type.”
New model pulled into Docker Model Runner Discovered automatically on next providers page load.
Model returns inference errors Fallback chain tries the next model. After repeated failures, model status degrades to retired — unless the error looks like infrastructure/capacity (timeouts, connection failures, a busy engine) or a credential gap, or unless retiring would strand a sensitivity class (see below).
Model times out under load Not retired. Capacity and admission timeouts describe a busy engine, not a broken model — a local engine holding a large model can exceed its admission budget from load or a cold weight-load alone.
Retiring would strand a sensitivity class Not retired. The model is marked degraded instead and a warning names the exposed class.
Model disappears from provider API After 2+ discovery cycles without seeing it, model is retired.
Pre-evaluated profile re-profiled Metadata is updated but dimension scores are NOT overwritten (profileSource: "evaluated" is protected).
Admin overrides AgentModelConfig Takes effect immediately. Seed will not overwrite admin-configured rows on next restart.
Provider evidence expires or is rejected The affected claim stops authorizing restricted work; the provider detail page shows the repair action.
Provider evidence is replaced The prior revision remains an audit record but no longer authorizes work.
Provider posture drifts from its attestation The owner receives an attention item; restricted routing stays blocked until the posture or evidence is reviewed.
A workload cohort is small Provider/model totals remain available, but workload-class detail stays hidden until the privacy threshold is met.

Provider-Specific Notes

Anthropic (OAuth Subscription)

The anthropic-sub provider uses OAuth authorization code flow. The subscription tier determines which models are accessible via the API:

The modelRestrictions field in providers-registry.json is set to ["claude-haiku-*", "claude-3-haiku-*"] to reflect this. Models discovered outside this allowlist are automatically retired during profiling.

If you have a Team or Enterprise subscription with Sonnet/Opus API access, update the restrictions via the admin UI or directly in the database.

ChatGPT (OpenAI Subscription)

The ChatGPT provider uses Server-Sent Events (SSE) for the Responses API. Known issue: some models (e.g., gpt-5.4) may return empty responses through the SSE adapter. The quality gate catches these and returns a fallback response. If you see “I wasn’t able to help with that” messages consistently, check the portal logs for [quality-gate] Response too short entries.

Docker Model Runner (Local)

Local models are automatically discovered and profiled. They are assigned the basic quality tier by default, which means they will only be selected for agents configured with minimumTier: "basic" (e.g., the onboarding assistant). To use local models more broadly, adjust agent tiers downward or improve local model dimension scores via profiling.

Troubleshooting

All coworker responses say “I wasn’t able to help”:

Agent uses wrong model:

Models missing after connecting a provider:

Every request in one sensitivity class fails with “No AI model can handle this request right now”:

A provider is active but restricted work is blocked:

Local model not appearing: