Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/adr/0002-product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@
- Figma File ID: N/A. This repository has no customer UI. A UI-owning repository must replace N/A with its real Figma File ID before a UI PR is accepted and must provide Storybook and design-token evidence.
- Consequence: The document is an operational snapshot, not a merge authorization or substitute for protected GitHub review. Hourly agents must re-collect exact head SHAs, reviews, threads, and required Checks before merge. Papers/standards live in `docs/doctoring/product-technical-gap-baseline.md` and must remain consistent with this ADR.

## Amendment: provider-specific direct-OpenAI fallback routing (2026-08-24)

The Strix control plane must route an `openai-direct/<model>` fallback to the
OpenAI platform API base, while retaining provider-specific credentials and
failing closed when no fallback credential or structured vulnerability report
exists. ContextualWisdomLab/.github#1295 exact head
`d376c33a3fdf013c588ab7fd30971f54f9dcee1b` records that routing and its
provider-404 regression contracts. This does not convert provider failure into
clean security evidence or authorize a merge; current-head Checks and
independent approvals remain required.

## Amendment: central Strix fallback contract (2026-08-25)

The current `main` workflow (`a724582`) intentionally replaced the unavailable
Expand Down
1 change: 1 addition & 0 deletions docs/product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ flowchart LR
- ContextualWisdomLab/.github#1265 stays GitHub CLEAN on `d4d4c2b0589065976e4bdcf5c5ae429bc21ed680` with hosted Checks green and no current-head OpenCode APPROVE after repeated `@opencode-agent` requests. CLEAN is not merge authorization.
- ContextualWisdomLab/.github#1263 head `50a6ad9129b2da55d049600ecd2516ee0999d7f1` still has required Strix FAILURE on protected-main gate.
- Open count is 98. No additional `.github` PR merged this pass.
- ContextualWisdomLab/.github#1295 exact head `d376c33a3fdf013c588ab7fd30971f54f9dcee1b` now binds `openai-direct/<model>` fallbacks to the OpenAI platform endpoint and preserves fail-closed behavior for provider 404/no-report outcomes. The focused provider-routing tests pass locally; hosted Checks remain the authority.


## 2026-08-25 central Strix fallback contract recheck
Expand Down
45 changes: 37 additions & 8 deletions scripts/ci/strix_quick_gate.sh
Comment thread
seonghobae marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,9 @@ fi
# models (openai-direct/... or openai_direct/...). When the primary model runs
# against NVIDIA NIM, OpenRouter, or GitHub Models, its LLM_API_KEY cannot
# authenticate a direct-OpenAI fallback; this file carries the OpenAI key.
# Optional: without it, explicit direct-OpenAI models keep using LLM_API_KEY,
# which is correct whenever the primary already runs against direct OpenAI.
# When the primary already runs against direct OpenAI, its LLM_API_KEY is safe
# to reuse for direct-OpenAI fallback models. A cross-provider fallback without
# this key fails closed instead of receiving the primary provider's credential.
STRIX_OPENAI_FALLBACK_KEY_FILE="${STRIX_OPENAI_FALLBACK_KEY_FILE:-}"
if [ -n "$STRIX_OPENAI_FALLBACK_KEY_FILE" ] && { [ ! -f "$STRIX_OPENAI_FALLBACK_KEY_FILE" ] || [ -L "$STRIX_OPENAI_FALLBACK_KEY_FILE" ]; }; then
echo "ERROR: STRIX_OPENAI_FALLBACK_KEY_FILE must reference a regular file containing the API key." >&2
Expand Down Expand Up @@ -2432,6 +2433,15 @@ resolved_llm_api_base_for_model() {
return 0
fi

if is_explicit_openai_model "$model" && ! is_explicit_openai_model "$PRIMARY_MODEL"; then
# Cross-provider fallback: direct-OpenAI models must always use the
# OpenAI platform endpoint. Inheriting the primary provider's API
# base (NVIDIA NIM, OpenRouter, GitHub Models) sends an OpenAI
# model and key to a foreign host, which answers "404 page not
# found" and turns every rate-limit outage into a failed fallback.
return 0
fi
Comment on lines +2436 to +2443

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Duplicate unreachable direct-OpenAI routing guard

The cross-provider guard is_explicit_openai_model "$model" && ! is_explicit_openai_model "$PRIMARY_MODEL" is written twice in a row with identical conditions and an identical return 0. The first always returns, so the second block is unreachable dead code.

Suggested change
if is_explicit_openai_model "$model" && ! is_explicit_openai_model "$PRIMARY_MODEL"; then
# Cross-provider fallback: direct-OpenAI models must always use the
# OpenAI platform endpoint. Inheriting the primary provider's API
# base (NVIDIA NIM, OpenRouter, GitHub Models) sends an OpenAI
# model and key to a foreign host, which answers "404 page not
# found" and turns every rate-limit outage into a failed fallback.
return 0
fi
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


local api_base_file="$LLM_API_BASE_FILE"
local api_base_file_name="LLM_API_BASE_FILE"
if is_github_models_model "$model" && [ -n "${STRIX_GITHUB_MODELS_API_BASE_FILE:-}" ]; then
Expand Down Expand Up @@ -2562,11 +2572,21 @@ run_strix_once() {
# with the GitHub Models token, not the direct-OpenAI key.
child_llm_api_key="$STRIX_GITHUB_MODELS_KEY"
fi
if is_explicit_openai_model "$model" && [ -n "$STRIX_OPENAI_FALLBACK_KEY" ]; then
# Cross-provider fallback: explicit direct-OpenAI models
# authenticate with the OpenAI key, not the primary provider's
# key (NVIDIA NIM, OpenRouter, or GitHub Models).
child_llm_api_key="$STRIX_OPENAI_FALLBACK_KEY"
if is_explicit_openai_model "$model"; then
if [ "$model" != "$PRIMARY_MODEL" ] && [ -n "$STRIX_OPENAI_FALLBACK_KEY" ]; then
# Cross-provider fallback: explicit direct-OpenAI models
# authenticate with the OpenAI key, not the primary provider's
# key (NVIDIA NIM, OpenRouter, or GitHub Models). The same
# dedicated key also takes precedence for same-provider fallback.
child_llm_api_key="$STRIX_OPENAI_FALLBACK_KEY"
elif is_explicit_openai_model "$PRIMARY_MODEL"; then
# Same-provider fallback: reuse the primary direct-OpenAI key
# only when no dedicated fallback key was configured.
:
else
echo "ERROR: direct-OpenAI fallback '$model' requires STRIX_OPENAI_FALLBACK_KEY_FILE when the primary model uses another provider." >&2
return 2
fi
Comment thread
seonghobae marked this conversation as resolved.
Comment on lines +2575 to +2589

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Direct-OpenAI key and base routing stay consistent

The key-selection block (strix_quick_gate.sh) and the base block (strix_quick_gate.sh) agree across all four cases: direct-OpenAI primary reuses its key and base; cross-provider fallback with a dedicated key routes to the OpenAI default base; same-provider fallback without a dedicated key reuses the primary key; cross-provider fallback without a key fails closed with rc 2. The base gate is false when the primary is itself direct-OpenAI, so same-provider fallbacks keep the primary base, matching the added tests.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

fi
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.
fi
set -o pipefail
Expand Down Expand Up @@ -4233,6 +4253,8 @@ run_current_target_scan() {
read -r -a FALLBACK_MODELS <<<"$FALLBACK_MODELS_RAW"

fallback_tried=0
local fallback_attempts=0
local fallback_config_failures=0
for candidate_raw in "${FALLBACK_MODELS[@]}"; do
candidate="$(normalize_model "$candidate_raw")"
if [ -z "$candidate" ] || [ "$candidate" = "$PRIMARY_MODEL" ]; then
Expand All @@ -4246,6 +4268,7 @@ run_current_target_scan() {
fi

fallback_tried=1
fallback_attempts=$((fallback_attempts + 1))
if is_vertex_model "$PRIMARY_MODEL"; then
echo "Primary Vertex model unavailable; retrying with fallback '$candidate'."
else
Expand All @@ -4264,7 +4287,9 @@ run_current_target_scan() {
return 0
fi
if [ "$fallback_scan_rc" -eq 2 ]; then
return 2
fallback_config_failures=$((fallback_config_failures + 1))
echo "Skipping fallback model '$candidate' because its provider configuration is invalid; trying the next configured fallback." >&2
continue
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.
fi

local strict_fallback_provider_signal=0
Expand Down Expand Up @@ -4332,6 +4357,10 @@ run_current_target_scan() {
fi
return 1
fi
if [ "$fallback_config_failures" -eq "$fallback_attempts" ]; then
echo "ERROR: All configured fallback models failed provider configuration." >&2
return 2
fi
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.

if [ "$INFRA_ERROR_DETECTED" -eq 1 ] &&
[ "$PR_FINDINGS_DECISION" = "allow_baseline" ]; then
Expand Down
Loading
Loading