fix(adapter): honor MODEL/MODEL_PROVIDER env (persona-env convention) #9

Merged
claude-ceo-assistant merged 1 commits from fix/dispatch-on-model-env-task-181 into main 2026-05-08 21:12:25 +00:00

1 Commits

Author SHA1 Message Date
1742b60e62 fix(adapter): honor MODEL/MODEL_PROVIDER env (persona-env convention)
Some checks failed
CI / Adapter unit tests (push) Successful in 1m40s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 19s
CI / Adapter unit tests (pull_request) Failing after 52s
CI / validate (push) Failing after 2m17s
CI / validate (pull_request) Successful in 13m19s
Fixes the 2026-05-08 dev-tree wedge: 22/27 non-lead workspaces (minimax tier)
stuck in degraded after /org/import, every chat hanging on
`Control request timeout: initialize`.

Root cause
----------
The persona env files (`~/.molecule-ai/personas/<name>/env`) declare a TWO-
variable convention:
  - MODEL          = model id   ("MiniMax-M2.7-highspeed")
  - MODEL_PROVIDER = provider slug ("minimax")

The runtime wheel's legacy `workspace/config.py` interprets MODEL_PROVIDER
as the *model id* — a name chosen long before there was a separate MODEL
env. With both set, the legacy code reads MODEL_PROVIDER="minimax" into
runtime_config.model. The literal string "minimax" doesn't match any
registry prefix (`minimax-` requires a hyphen suffix), falls through to
providers[0] (anthropic-oauth), the auth check fails on the absent
CLAUDE_CODE_OAUTH_TOKEN, the claude CLI launches anyway, and the SDK's
`query.initialize()` 60s control timeout fires.

The brief hypothesised `claude_sdk_executor.py` lacked dispatch logic.
Phase 1 evidence: dispatch ALREADY exists in adapter.py — model -> provider
-> base_url + auth_env routing was correctly built for #180. The bug was
upstream: MODEL_PROVIDER's name collision with the persona-env convention
silently corrupted the picked model BEFORE adapter.py saw it.

Fix
---
New helper `_resolve_model_and_provider_from_env` reconciles env vars
against YAML inside adapter.setup() and create_executor():

  1. MODEL env -> picked_model (authoritative when set).
  2. MODEL_PROVIDER env -> explicit_provider IFF the value matches a
     registered provider name. Backward-compat: if MODEL is unset and
     MODEL_PROVIDER doesn't match a registered slug, treat it as a
     legacy model id (canvas Save+Restart pre-this-fix).
  3. YAML runtime_config.{model,provider} fills any field env didn't
     supply.

Contained in the template repo per the brief's scope guidance — does NOT
touch the runtime wheel's workspace/config.py (which would need a separate
molecule-core PR), and does NOT change the persona-env dispatch policy
(Phase 2 mapping 2026-05-08).

Tests
-----
Eleven new cases in tests/test_env_model_provider_dispatch.py covering:
  - persona-env shape (minimax, GLM, lead claude-code) -> correct model + slug
  - legacy MODEL_PROVIDER-as-model-id shape still works
  - env wins over YAML
  - YAML fallback when env unset
  - whitespace/empty defensive handling
  - case-insensitive provider slug matching

Full adapter test suite: 76/76 pass.

Verification path
-----------------
After image rebuild + workspace re-provision, ws-* containers will boot
with provider=minimax (not anthropic-oauth), ANTHROPIC_BASE_URL set to
https://api.minimax.io/anthropic, MINIMAX_API_KEY projected onto
ANTHROPIC_AUTH_TOKEN, and the SDK init handshake succeeding.

Refs: task #181, brief 2026-05-08, related #180 (#7 in this repo)
2026-05-08 14:11:42 -07:00