Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 8 additions & 8 deletions .github/workflows/opencode-review-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4058,8 +4058,8 @@ jobs:
"apiKey": "{env:OPENAI_API_KEY}"
},
"models": {
"gpt-5.6-luna": {
"name": "OpenAI GPT-5.6 Luna (direct)",
"gpt-5.4": {
"name": "OpenAI GPT-5.4 (direct)",
"tool_call": true,
"reasoning": true,
"options": {
Expand Down Expand Up @@ -4471,17 +4471,17 @@ jobs:
# or used for product/model improvement, so private repositories
# include neither NIM nor anonymous free candidates and start at the
# existing keyed fallback list: OpenCode Zen GPT-5.6 Terra, DeepSeek
# V3, the direct GPT-5.6 Luna slot, and pinned PAID
# V3, the direct GPT-5.4 slot, and pinned PAID
# OpenRouter coder models (free-tier candidates hit the shared
# free-models-per-day cap and hung for the full candidate timeout,
# so the OpenRouter slots use cheap paid models billed against the
# org's OpenRouter credits), then the full-size GPT-4.1 long-context
# endpoint and provider-specific GPT/o3 fallbacks.
# The direct-OpenAI slot runs GPT-5.6 Luna: the newest family's
# cost-efficient tier, cheaper than the legacy gpt-5 it replaced
# ($1/$6 vs $1.25/$10 per 1M tokens) so the org OpenAI budget
# stretches further between top-ups.
OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5 nvidia-nim/nvidia/llama-3.1-nemotron-ultra-253b-v1 nvidia-nim/nvidia/nemotron-3-super-120b-a12b nvidia-nim/nvidia/nemotron-3-ultra-550b-a55b nvidia-nim/meta/llama-3.3-70b-instruct nvidia-nim/deepseek-ai/deepseek-v4-pro nvidia-nim/mistralai/codestral-22b-instruct-v0.1 opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/laguna-s-2.1-free opencode-free/ling-3.0-flash-free opencode-free/big-pickle opencode-free/mimo-v2.5-free opencode-free/hy3-free opencode-free/minimax-m3-free opencode-free/glm-5-free opencode-free/kimi-k2.5-free opencode-free/qwen3.6-plus-free ' || '' }}opencode/gpt-5.6-terra github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1"
# The direct-OpenAI slot runs GPT-5.4: gpt-5.6-luna returns 404 on
# the OpenAI API (see a724582), so the pool keeps the newest VALID
# direct-OpenAI model instead of burning a candidate on a certain
# failure.
OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5 nvidia-nim/nvidia/llama-3.1-nemotron-ultra-253b-v1 nvidia-nim/nvidia/nemotron-3-super-120b-a12b nvidia-nim/nvidia/nemotron-3-ultra-550b-a55b nvidia-nim/meta/llama-3.3-70b-instruct nvidia-nim/deepseek-ai/deepseek-v4-pro nvidia-nim/mistralai/codestral-22b-instruct-v0.1 opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/laguna-s-2.1-free opencode-free/ling-3.0-flash-free opencode-free/big-pickle opencode-free/mimo-v2.5-free opencode-free/hy3-free opencode-free/minimax-m3-free opencode-free/glm-5-free opencode-free/kimi-k2.5-free opencode-free/qwen3.6-plus-free ' || '' }}opencode/gpt-5.6-terra github-models/deepseek/deepseek-v3-0324 openai/gpt-5.4 openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1"
# One attempt per model, then fall through to the next model. Retrying
# the SAME model 5x let a rate-limited/hung leader consume the whole
# step, so the pool never reached a healthy fallback model.
Expand Down
73 changes: 62 additions & 11 deletions .github/workflows/strix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,17 @@ jobs:
export "STRIX_PROCESS_${budget_suffix}_SECONDS=$process_budget_seconds"
export "STRIX_TOTAL_${budget_suffix}_SECONDS=5700"

# Recognized signals that the LLM backend was unavailable / starved.
# Defined before the gate loop so the bounded retry decision below
# can classify outcomes without duplicating the patterns later.
backend_unavailable_signal='RateLimitError|Too many requests\. For more on scraping GitHub|exceeded your current quota|insufficient_quota|billing details|"status"[[:space:]]*:[[:space:]]*"RESOURCE_EXHAUSTED"|tokens_limit_reached|Request body too large|Max size:[[:space:]]*[0-9]+[[:space:]]+tokens|Error code:[[:space:]]*413|LLM CONNECTION FAILED|Could not establish connection to the language model|LLM warm-up failed|Configured model and fallback models were unavailable|Configured Vertex model and fallback models were unavailable|emitted provider infrastructure or failure-signal output|before provider infrastructure failure|litellm(\.exceptions)?\.NotFoundError[^[:cntrl:]]*Nvidia_nimException[^[:cntrl:]]*Error code:[[:space:]]*404|Error during penetration test: loginAsGuest failed after [0-9]+ attempts: curl exit 7: curl: \(7\) Failed to connect to 127\.0\.0\.1 port 48080'
model_behavior_error_signal='(^|[^A-Za-z0-9_])(agents|pydantic_ai|strix)(\.[A-Za-z_][A-Za-z0-9_]*)*\.ModelBehaviorError([^A-Za-z0-9_]|$)'
# Any evidence that a vulnerability was actually reported. Its presence
# forces a hard failure so real findings are NEVER downgraded. Keep the
# severity branch anchored away from identifiers so environment lines
# such as STRIX_FAIL_ON_MIN_SEVERITY do not look like findings.
reported_vulnerability_signal='Vulnerabilities[[:space:]]+[1-9]|(^|[^A-Za-z0-9_])severity[[:space:]]*:'
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.

# Capture the gate exit code plus its console output. The gate returns
# exit 1 both for genuine blocking vulnerabilities AND for
# LLM-backend-unavailable outcomes (GitHub Models "Too many requests"
Expand All @@ -871,11 +882,60 @@ jobs:
# could not complete a scan. Provider failure is typed infrastructure
# evidence, but remains non-passing because no authoritative complete
# vulnerability result exists.
#
# A typed provider outage with no reported vulnerability finding is
# retried with bounded linear backoff inside this step so transient
# provider failures do not fail the required check on the first
# attempt. Genuine findings, configuration failures, and unexpected
# exit codes never retry; the deadline keeps every path inside the
# deterministic 120-minute job budget, and all-terminal outcomes
# remain fail-closed.
strix_run_log="$RUNNER_TEMP/strix_gate_console.log"
strix_rc=0
strix_gate_attempt=1
strix_gate_deadline=$(( SECONDS + 6000 ))
set +e
bash "$TRUSTED_STRIX_GATE" 2>&1 | tee "$strix_run_log"
strix_rc="${PIPESTATUS[0]}"
while : ; do
: > "$strix_run_log"

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: Earlier retry attempts' logs are discarded

: > "$strix_run_log" (strix.yml) truncates the console log each iteration, so only the final attempt reaches the uploaded artifact. Correct for classification, but debugging a multi-attempt outage loses the intermediate console output.

Open in Devin Review

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

bash "$TRUSTED_STRIX_GATE" 2>&1 | tee "$strix_run_log"
strix_rc="${PIPESTATUS[0]}"
if [ "$strix_rc" -eq 0 ]; then
break
fi
# Only exit-code 1 scan failures can be infrastructure outcomes.
if [ "$strix_rc" -ne 1 ]; then
break
fi
# Scope this attempt's retry decision to the log tail after the
# last pipeline-continuation marker, exactly like the terminal
# classification below: an already-exempted finding before the
# marker must not mask a retryable outage after it.
strix_retry_scope_log="$strix_run_log"
if grep -Fq 'allowing pipeline continuation' "$strix_run_log"; then
strix_retry_scope_log="$RUNNER_TEMP/strix_gate_console_tail.log"
awk '/allowing pipeline continuation/{buf=""; next} {buf=buf $0 "\n"} END{printf "%s", buf}' \
"$strix_run_log" > "$strix_retry_scope_log"
fi
# A reported vulnerability is authoritative evidence: never retry
# and never risk downgrading it.
if grep -Eiq "$reported_vulnerability_signal" "$strix_retry_scope_log"; then
break
fi
# Retry only recognized provider-outage / model-behavior classes.
if ! grep -Eiq "$backend_unavailable_signal" "$strix_retry_scope_log" \
&& ! grep -Eq "$model_behavior_error_signal" "$strix_retry_scope_log"; then
break
Comment thread
coderabbitai[bot] marked this conversation as resolved.
fi
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
remaining_seconds=$(( strix_gate_deadline - SECONDS ))
if [ "$strix_gate_attempt" -ge 3 ] || [ "$remaining_seconds" -lt 600 ]; then
echo "Provider-unavailable Strix attempt ${strix_gate_attempt} reached the bounded retry limit or the remaining job time budget (${remaining_seconds}s) is too small to retry; failing closed." >&2
break
fi
Comment on lines +929 to +933

@devin-ai-integration devin-ai-integration Bot Aug 25, 2026

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: Retry can start with too little step budget

The retry gate permits a new attempt whenever remaining_seconds >= 600 (10 min), but a single attempt can run up to STRIX_TOTAL_TIMEOUT_SECONDS=5700 (~95 min, strix.yml:864). A retry started near the deadline can therefore be hard-killed mid-run by the step's timeout-minutes: 100 (strix.yml:808). The outcome stays fail-closed, so no false pass, but the 'deterministic' budget comment overstates this.

Open in Devin Review

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

backoff_seconds=$(( ${STRIX_GATE_RETRY_BACKOFF_SECONDS:-90} * strix_gate_attempt ))
echo "Strix provider outage on attempt ${strix_gate_attempt}; retrying after ${backoff_seconds}s backoff." >&2
sleep "$backoff_seconds"
strix_gate_attempt=$(( strix_gate_attempt + 1 ))
done
Comment on lines +898 to +938

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: Retry gating preserves fail-closed semantics

Retries occur only for exit code 1 with no vulnerability signal in the tail-scoped log and a matching outage/model-behavior signal; findings, exit 2, and unexpected codes break and propagate. Tail-scoping (strix.yml) mirrors the terminal classification, so an earlier exempted finding cannot mask a later outage and a real finding after the continuation marker still blocks. Attempts cap at 3 gate runs.

Open in Devin Review

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

set -e

if [ "$strix_rc" -eq 0 ]; then
Expand All @@ -889,15 +949,6 @@ jobs:
exit "$strix_rc"
fi

# Recognized signals that the LLM backend was unavailable / starved.
backend_unavailable_signal='RateLimitError|Too many requests\. For more on scraping GitHub|exceeded your current quota|insufficient_quota|billing details|"status"[[:space:]]*:[[:space:]]*"RESOURCE_EXHAUSTED"|tokens_limit_reached|Request body too large|Max size:[[:space:]]*[0-9]+[[:space:]]+tokens|Error code:[[:space:]]*413|LLM CONNECTION FAILED|Could not establish connection to the language model|LLM warm-up failed|Configured model and fallback models were unavailable|Configured Vertex model and fallback models were unavailable|emitted provider infrastructure or failure-signal output|before provider infrastructure failure|litellm(\.exceptions)?\.NotFoundError[^[:cntrl:]]*Nvidia_nimException[^[:cntrl:]]*Error code:[[:space:]]*404|Error during penetration test: loginAsGuest failed after [0-9]+ attempts: curl exit 7: curl: \(7\) Failed to connect to 127\.0\.0\.1 port 48080'
model_behavior_error_signal='(^|[^A-Za-z0-9_])(agents|pydantic_ai|strix)(\.[A-Za-z_][A-Za-z0-9_]*)*\.ModelBehaviorError([^A-Za-z0-9_]|$)'
# Any evidence that a vulnerability was actually reported. Its presence
# forces a hard failure so real findings are NEVER downgraded. Keep the
# severity branch anchored away from identifiers so environment lines
# such as STRIX_FAIL_ON_MIN_SEVERITY do not look like findings.
reported_vulnerability_signal='Vulnerabilities[[:space:]]+[1-9]|(^|[^A-Za-z0-9_])severity[[:space:]]*:'

# An earlier out-of-scope/below-threshold finding may already have
# been exempted by the trusted gate. Classify a later provider
# outage from the tail after the last continuation marker, but keep
Expand Down
8 changes: 4 additions & 4 deletions tests/test_opencode_agent_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates():
["opencode-free", "qwen3.6-plus-free"],
["opencode", "gpt-5.6-terra"],
["github-models", "deepseek/deepseek-v3-0324"],
["openai", "gpt-5.6-luna"],
["openai", "gpt-5.4"],
["openrouter", "deepseek/deepseek-v3.2"],
["openrouter", "qwen/qwen3-coder"],
["github-models", "openai/gpt-4.1"],
Expand All @@ -197,7 +197,7 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates():
["github-models", "deepseek/deepseek-r1"],
]
assert zen_models == ["gpt-5.6-terra"]
assert direct_openai_models == ["gpt-5.6-luna"]
assert direct_openai_models == ["gpt-5.4"]
assert openrouter_models == [
"deepseek/deepseek-v3.2",
"qwen/qwen3-coder",
Expand Down Expand Up @@ -1740,7 +1740,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
assert (
"opencode/gpt-5.6-terra "
"github-models/deepseek/deepseek-v3-0324 "
"openai/gpt-5.6-luna "
"openai/gpt-5.4 "
"openrouter/deepseek/deepseek-v3.2 "
"openrouter/qwen/qwen3-coder "
"github-models/openai/gpt-4.1 "
Expand Down Expand Up @@ -1887,7 +1887,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
)
assert (
"github-models/deepseek/deepseek-v3-0324 "
"openai/gpt-5.6-luna "
"openai/gpt-5.4 "
"openrouter/deepseek/deepseek-v3.2 "
"openrouter/qwen/qwen3-coder "
"github-models/openai/gpt-4.1 "
Expand Down
2 changes: 1 addition & 1 deletion tests/test_pr_review_autofix_nvidia_nim_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md")
CHANGELOG = Path("CHANGELOG.md")
REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml")
REVIEW_DISPATCH_BLOB_SHA = "dd65d90e10e5040562b501ade1a40f89572f0984"
REVIEW_DISPATCH_BLOB_SHA = "0df7a17cc72a79585cec169c8299e0646f93ab02"


def _workflow_text(path: Path) -> str:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_required_workflow_queue_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ def test_nvidia_nim_defaults_preserve_existing_fallbacks_without_secret(
assert strix.returncode == 0, strix.stderr
assert {
"provider_mode=openai_direct",
"strix_model=gpt-5.6-luna",
"strix_model=gpt-5.4",
} <= set(strix_output.read_text().splitlines())
assert (
"STRIX_MODEL: ${{ steps.gate.outputs.strix_model }}"
Expand Down
Loading
Loading