[P2.19] Provider-aware pre-flight key checks + Think-phase provider resolution (#768) - #860
Conversation
… resolution (#768) - New codeframe/core/llm_resolution.py: single source of truth for the provider chain (flag -> CODEFRAME_LLM_PROVIDER -> config.yaml llm: -> anthropic) and the provider -> required-key-env mapping - runtime.py and ui/routers/prd_v2.py deduped onto the shared helper; runtime pre-flight now also fails loudly on missing OPENAI_API_KEY - work start/batch run: builtin-engine key check validates the key matching the resolved provider instead of always ANTHROPIC_API_KEY - work retry: provider-aware key check (env -> config -> default) - prd stress-test + tasks generate: new --llm-provider/--llm-model flags; both route through the shared resolution instead of hardcoding Anthropic; provider threaded through generate_from_prd
- tasks generate: reject --recursive with --no-llm explicitly (was an AttributeError on a None provider after the provider-threading change) - get_provider: honor a model override for the anthropic provider by building a ModelSelector pinned to that model (previously --llm-model / CODEFRAME_LLM_MODEL / config model was silently dropped for anthropic)
|
Claude finished @frankbria's task in 7m 12s —— View job ✅ GLM precision review: no defects foundReviewed the full diff for #768 (provider-aware pre-flight key checks + Think-phase provider resolution) against the checked-out source and their callers/callees. Scope: concrete defects only (logic errors, null/unhandled crash paths, security, data loss, breaking API changes, resource leaks) — not style/architecture/coverage. No confirmed findings. Key things verified:
Investigated and ruled out (non-fatal): the batch |
Cross-family review — codex (OpenAI family, fallback reviewer)
Pre-PR pass (commit 1853f53): 2 Major (P2) findings, both verified and fixed in f8dfe00:
Post-PR pass (f8dfe00, full branch diff vs main):
Verdict: APPROVE |
Review: [P2.19] Provider-aware pre-flight key checks + Think-phase provider resolution (#768)No prior reviews on this PR to reconcile with — this is the first pass. Overall this is a clean, well-scoped refactor: One real gap:
|
…CI test seam - discovery_v2 POST /generate-tasks now resolves the provider via the standard chain and threads it into generate_from_prd (claude-review + internal review Major finding: the web task-generation surface still hardcoded Anthropic) — with router regression tests - test_tasks_generate_without_recursive_uses_existing_behavior: patch the get_provider factory seam like its sibling; in CI (no env key, no CredentialManager entry) provider construction exited before generate_from_prd, failing the routing assertion - work start --execute help text: key requirement is now provider-aware
Feedback triage (as of 263046e)claude-review (Major): Internal review (advisory, Claude family):
CI Backend Unit Tests failure on f8dfe00 — root-caused: Related: the anthropic model-override fix in f8dfe00 resolves the model half of #780 (P3.9); base_url for anthropic remains open there. |
Precision review in progress
|
|
Claude finished @frankbria's task in 6m 6s —— View job ✅ GLM precision review: no defects foundReviewed the full branch diff for #768 (provider-aware pre-flight key checks + Think-phase provider resolution) against the checked-out source and callers/callees. Scope: concrete defects only (logic errors, null/unhandled crash paths, security, data loss, breaking API changes, resource leaks). High precision over recall — no finding I couldn't attach a concrete failure scenario to. No confirmed findings. Key high-risk areas verified:
Investigated and ruled out (non-fatal): Verdict: APPROVE (no action requested).
|
Review (at 263046e)Re-reviewed after the discovery_v2.py fix landed. This is the fourth review pass on this PR (GLM, codex, my own prior pass, and this one) and the codebase is in good shape — I only have one small, non-blocking item to add. Confirms prior findings are resolved
One small gap left over from my last reviewI'd flagged that provider = None
if use_llm:
...
provider = create_provider(resolve_llm_settings(workspace.repo_path))So a missing Other spot checks (no issues found)
No security concerns — pre-flight checks fail closed, and none of the resolved settings (provider/model/base_url) are logged or echoed in a way that would leak a key value. Nice work tightening this up across three review rounds — looks ready to merge, with the discovery_v2 missing-key polish as an optional fast-follow. |
Review: [P2.19] Provider-aware pre-flight key checks + Think-phase provider resolution (#768)Follow-up pass on top of the existing review thread (GLM precision review: no findings; codex cross-family: 2 Major fixed, approved; my earlier pass: the Confirmed fixed, matches the triage comment:
Still open, both already flagged and explicitly accepted as non-blocking (no change since last pass):
New spot-checks that came back clean:
Test coverage: thorough — precedence matrix in No new blocking issues. This looks ready to merge; the two open items above are cosmetic/already-scoped-out and shouldn't hold it up. |
|
Claude finished @frankbria's task in 5m 12s —— View job ✅ GLM precision review: no defects foundIndependently re-verified the full branch diff for #768 (provider-aware pre-flight key checks + Think-phase provider resolution) against the checked-out source and callers/callees. Scope: concrete defects only (logic errors, null/unhandled crash paths, security, data loss, breaking API/contract changes, resource leaks) — not style/architecture/coverage. High precision over recall. No confirmed findings. Highest-risk areas verified against real code:
Investigated and ruled out (non-fatal, pre-existing / out-of-scope — not re-flagged): Verdict: APPROVE (no action requested).
|
Review (at b81fbd4, latest commit)This is my fourth pass on this PR; the codebase has settled after GLM's two precision passes, codex's cross-family review, and my three prior comments. I re-read the full diff against current Confirms the last open item is now closed:
Independent spot-checks (no new issues):
Still open, already scoped out on purpose (no change needed here):
No new security, correctness, or test-coverage concerns. This is ready to merge. |
…hain (#861) (#874) * feat(core): route 4 remaining provider sites through llm_resolution chain (#861) Follow-up to #768 (PR #860) which introduced codeframe/core/llm_resolution.py as the single source of truth for the provider chain (CODEFRAME_LLM_PROVIDER -> .codeframe/config.yaml -> anthropic). Four code paths still constructed providers directly, bypassing the chain. Sites migrated: - codeframe/core/conductor.py:SupervisorResolver.llm -- was get_provider() (bare -> anthropic). Concrete failure fixed: cf work batch run --strategy auto --llm-provider ollama now uses ollama for blocker auto-resolution instead of demanding an Anthropic key downstream of a passing pre-flight. - codeframe/core/dependency_analyzer.py:analyze_dependencies -- the private _get_default_provider() helper (hardcoded AnthropicProvider + ValueError on missing key) is dropped; the call site now inlines create_provider(resolve_llm_settings(workspace.repo_path)) when provider=None. - codeframe/core/prd_discovery.py:PrdDiscoverySession.__post_init__ -- api_key remains as a backward-compat field (constructs AnthropicProvider when set, preserving every existing caller). When api_key is unset the chain is used. The missing-key check is generalized via LLMSettings.required_key_env so the existing NoApiKeyError contract is preserved AND extended to any keyed provider (not just Anthropic). - codeframe/core/adapters/streaming_chat.py:StreamingChatAdapter -- the no-provider fallback (used only by tests/external callers; production session_chat_ws.py already passes provider= explicitly) now uses the chain. The vestigial api_key parameter is kept for backward compat. Regression coverage: tests/core/test_provider_resolution_chain.py adds one test class per site (9 tests total) mirroring the reference pattern in tests/ui/test_discovery_generate_tasks.py -- set CODEFRAME_LLM_PROVIDER=ollama, delete ANTHROPIC_API_KEY, mock create_provider, assert the chain is invoked and the resulting provider is the one used. All existing tests in the 4 affected areas pass unchanged (67+17=84 tests). * fix(streaming-chat): preserve api_key= legacy contract + tighten regression tests Addresses cross-family review (opencode/GLM) on PR for #861: Major 1 + Major 2: streaming_chat docstring lied about api_key behavior, and the api_key= legacy contract was asymmetric with PrdDiscoverySession (which preserves it). Restore the legacy branch: when api_key is set AND provider is None, construct AnthropicProvider(api_key=...) — mirroring prd_discovery. Update docstring to describe the new contract accurately. Suggestion 1: test_explicit_provider_skips_chain for dependency_analyzer previously passed task_ids=[] which hit the early-return at line ~63 before the provider was ever consulted. Seed a task and assert explicit_provider.complete was actually called, so the test fails if explicit-provider handling breaks. Nitpick 2: lock in model_flag= propagation in streaming_chat (the only site that threads model into resolve_llm_settings) via an assertion on settings.model. New test_legacy_api_key_skips_chain locks in the Major 2 fix. Total tests: 10 (was 9). * chore: gitignore .opencode-tmp scratch dir (review artifacts) * fix(tests): patch llm_resolution.create_provider in prd_generate CLI tests The 8 CLI tests in tests/cli/test_prd_generate.py patched codeframe.core.prd_discovery.AnthropicProvider to substitute a mock LLM. After #861 migrated PrdDiscoverySession to use the shared llm_resolution chain, the construction path moved to create_provider -> get_provider -> codeframe.adapters.llm.anthropic.AnthropicProvider, which the existing patch target didn't intercept. CI surfaced this as 401 'invalid x-api-key' errors reaching the real Anthropic API. Fix: retarget the patch to codeframe.core.llm_resolution.create_provider across all 8 affected test methods. The mock setup pattern is unchanged (mock.return_value = mock_llm_provider); only the patch site moved. This is a contract change documented in issue #861 (PrdDiscoverySession now resolves via the shared chain) — test patches are updated to reflect the new contract, per the anti-mutation rule's allowance for documented contract changes. * docs(prd-discovery): mark api_key as optional in class docstring Per claude[bot] review on PR #874: the PrdDiscoverySession class docstring still claimed api_key is 'required' even though #861 made it optional (when unset, the provider is resolved via the llm_resolution chain). Aligns with the streaming_chat docstring fix from the same migration.

Summary
Implements #768: provider-selection gaps — pre-flight key check and Think-phase ignored
--llm-provider.codeframe/core/llm_resolution.py: single source of truth for the effective-provider chain (--llm-providerflag →CODEFRAME_LLM_PROVIDER→.codeframe/config.yaml llm:→anthropic) and the provider → required-key-env mapping (anthropic → ANTHROPIC_API_KEY,openai → OPENAI_API_KEY, local providers → none).runtime.pyandui/routers/prd_v2.pydeduped onto the shared helper; runtime pre-flight now also fails loudly on a missingOPENAI_API_KEYfor the openai provider.cf work start/cf work batch run: builtin-engine key check validates the key matching the resolved provider instead of alwaysANTHROPIC_API_KEY;cf work retryresolves env → config → default.cf prd stress-testandcf tasks generate: new--llm-provider/--llm-modelflags; both route through the shared resolution instead of hardcoding Anthropic; provider threaded throughgenerate_from_prd.--recursive --no-llmis now rejected explicitly, and a model override for the anthropic provider is honored via a pinnedModelSelector(previously silently dropped).Acceptance Criteria
work start/batch/retrypre-flight honors the resolved provider (non-anthropic no longer demands ANTHROPIC_API_KEY; openai demands OPENAI_API_KEY)prd stress-testandtasks generateroute through the same provider resolutionTest Plan
tests/core/test_llm_resolution.py, +9 CLI integration testsruff check .)Known Limitations / Intentionally Deferred
cf work retrygets no--llm-provider/--llm-modelflags — it re-executes viaexecute_agent, which already honors env/config; only its pre-flight became provider-aware.generate_from_prd(provider=None)(core default path, no CLI in play) still falls back to plainget_provider()(anthropic) rather than the full env/config chain — existing test seams patch the zero-argget_provider(); all CLI/server paths pass an explicitly resolved provider.base_urlremains config →OPENAI_BASE_URLonly (no dedicated CLI flag), matching pre-existing behavior.Implementation Notes
Plan was self-authored (issue had no implementation plan). Two runtime behavior improvements ride along by design: missing
OPENAI_API_KEYfor the openai provider now fails loudly at execution pre-flight, and model overrides now reachAnthropicProvidervia a purpose-pinnedModelSelector.Closes #768