model.jsonAtlas was wired so its default brain was a Bedrock model (kimi-k2.5), with the model slugs buried in atlas.yaml alongside behaviour. Two things were wrong. (1) kimi-k2.5 is unstable for our use: it intermittently omits AtlasTurn fields → the brain retries (30–40s each, several times) → the run looks like a hang (Bug-1310). DeepSeek v3.2 via OpenRouter is clean — verified live: forum grounding works (proceeding_type "MCIA arbitration · sole arbitrator"), usage 3426/728, no retries. (2) Bedrock is being dropped entirely (India frontier gap; ADR-116 multi-provider). No Bedrock model should remain as a default or tier anywhere.
The naïve fix — just swap the kimi slug for a DeepSeek slug in atlas.yaml — misses the point twice: it leaves model config tangled with behaviour in one YAML, and it leaves the provider defaulting to bedrock with a Bedrock fallback literal (apac.amazon.nova-lite-v1:0) one env-var away. The decision: extract the model into a dedicated per-agent model.json, make OpenRouter the only provider path, and purge every Bedrock model slug from config, defaults, and scripts.
BEFORE AFTER
atlas.yaml models: {bedrock slugs} atlas.yaml (behaviour only — NO model block)
provider: bedrock model.json provider=openrouter, base_url, route,
config.py default literal nova-lite tiers{quick/advisory/fallback}=DeepSeek,
provider.py default branch = bedrock default=quick, ZDR, coming_soon_routes
config.py reads model.json; default literal = DeepSeek
provider.py default branch = openrouter; ZDR on every call
resolution order (unchanged contract): ENV > model.json > safe DeepSeek literal
ATLAS_INFERENCE_PROVIDER / ATLAS_MODEL_ID / ATLAS_OPENROUTER_BASE_URL / ATLAS_TEMPERATURE /
ATLAS_MAX_TOKENS / ATLAS_REGION — env always wins (broker/deploy override path)
Route policy (founder, 2026-06-15). Inference is a firm setting. TODAY exactly one live route: Global → OpenRouter zero-data-retention, no region restriction — the default and only resolvable tier. US / EU / India region tiers are coming-soon stubs (no live route; recorded in coming_soon_routes). The broker (pramaan-functions) is governance — it reads firm policy, resolves Global→OpenRouter-ZDR, and passes provider/model in agent_invoke. Atlas is execution — one OpenRouter path.
| Piece | What | WHY |
|---|---|---|
agents/atlas/model.json (NEW) | The model-config surface: provider=openrouter, base_url, route=global, zero_data_retention=true, region=null, tiers (quick/advisory/fallback, all DeepSeek), default=quick, coming_soon_routes, temperature, max_tokens. | One file a dev opens to change Atlas's brain — split from behaviour. All slugs are OpenRouter DeepSeek; zero Bedrock. |
atlas.yaml | The entire models: block removed; a pointer comment left in its place. Keeps caps, identity, reflection, truth boundary, tools, transport. | atlas.yaml is now behaviour only; the model moved to model.json. |
config.py | New _load_model_config() reads model.json; provider default → openrouter; default tier quick; fallback literal → deepseek/deepseek-v3.2 (was apac.amazon.nova-lite-v1:0). Pricing table re-keyed to DeepSeek slugs. | Removes the last Bedrock literal; cost stays accurate for the real model (Bug-1307 lineage). |
provider.py | Default branch flipped to openrouter (bedrock branch kept dormant as a mechanism, never default). model_settings() now pins ZDR on every OpenRouter call via extra_body={"provider":{"data_collection":"deny"}}. | Founder Global-ZDR policy enforced on the wire, per call — OpenRouter may then route only to providers that do not store content. |
scripts/deploy-atlas-staging.sh · scripts/dev-atlas-openrouter.sh | ATLAS_MODEL_ID default → deepseek/deepseek-v3.2; kimi/claude-haiku comment examples → DeepSeek. | The deploy/dev defaults must not reintroduce a dropped model. |
Doctrine: AGENTS.md · agents/atlas/AGENTS.md | Model row → DeepSeek v3.2 (OpenRouter) + a model.json row; egress prose made provider-accurate ("inference provider", OpenRouter, ADR-116) instead of "Bedrock-only". | The brain now calls OpenRouter, not Bedrock — the single-egress principle is preserved, the provider corrected. No silent drift. |
Tests: test_atlas_config.py, test_atlas_provider.py (NEW); test_atlas_usage.py, test_atlas_runtime.py updated | Assert the no-env default resolves provider=openrouter + model=deepseek/deepseek-v3.2 driven BY model.json; no Bedrock slug in config values; ZDR present on OpenRouter settings; DeepSeek pricing. | Locks the founder decision in CI — any reintroduction of a Bedrock slug or a dropped ZDR fails the suite. |
provider.data_collection="deny" in the body. A request can only land on a provider that does not store content. Verified by test_openrouter_settings_pin_zero_data_retention.| Check | Result |
|---|---|
(cd agents/atlas && pytest tests -q) | 90 passed (incl. new config + provider tests for the OpenRouter/DeepSeek default and ZDR). |
ruff check agents/ · ruff check tests/ scripts/ | All checks passed. |
python scripts/check_brain_egress.py | brain-egress check: clean (ADR-110 §6 / ADR-121 D5). |
| Default path (no env) | AtlasConfig.load() → provider openrouter, model deepseek/deepseek-v3.2, base_url https://openrouter.ai/api/v1 — driven by model.json (asserted). |
| Zero Bedrock | No Bedrock model slug in model.json values, config, atlas.yaml, or the deploy/dev scripts. Remaining "kimi" strings are history-noting comments only. |
prompts.py/models.py untouched). The model-config surface moved and the provider flipped; the resolution contract (ENV > file > literal) is unchanged. Live staging runtime untouched — the runtime recreate / env wiring is the Bug-1310 / pramaan-infra cutover, separate.