-
Notifications
You must be signed in to change notification settings - Fork 0
fix(strix): bounded provider-outage retries #1332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1ec0be1
e61dd47
51446e8
04755fe
285d661
e6e5f85
495c02d
5d7f257
441401d
2c32065
6cb2d2f
dcbef2e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -882,6 +882,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:]]*:' | ||
|
|
||
| # 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" | ||
|
|
@@ -890,11 +901,92 @@ 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_attempt_audit_log="$RUNNER_TEMP/strix_gate_attempts.log" | ||
| : > "$strix_attempt_audit_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 | ||
| # Rebind every invocation to the remaining outer deadline. The | ||
| # original 5700/5400 split reserves 300 seconds for gate cleanup; | ||
| # retain that internal reserve plus 300 seconds for workflow | ||
| # artifact/status cleanup even when a late retry starts. | ||
| remaining_seconds=$(( strix_gate_deadline - SECONDS )) | ||
| if [ "$remaining_seconds" -le 600 ]; then | ||
| : > "$strix_run_log" | ||
| echo "Configured model and fallback models were unavailable before another bounded retry could start: only ${remaining_seconds}s remain in the outer Strix deadline." | tee "$strix_run_log" >&2 | ||
| strix_rc=1 | ||
| { | ||
| printf "=== outer-attempt=%s rc=%s ===\\n" "$strix_gate_attempt" "$strix_rc" | ||
| cat "$strix_run_log" | ||
| } >> "$strix_attempt_audit_log" | ||
| break | ||
|
Comment on lines
+925
to
+933
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Deadline-exhaustion path typed as outage When the top-of-loop deadline guard fires it overwrites Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| fi | ||
| attempt_budget_seconds=$(( remaining_seconds - 300 )) | ||
| if [ "$attempt_budget_seconds" -gt 5700 ]; then | ||
| attempt_budget_seconds=5700 | ||
| fi | ||
| attempt_process_budget_seconds=$(( attempt_budget_seconds - 300 )) | ||
| if [ "$attempt_process_budget_seconds" -gt "$process_budget_seconds" ]; then | ||
| attempt_process_budget_seconds="$process_budget_seconds" | ||
| fi | ||
| export "STRIX_TOTAL_${budget_suffix}_SECONDS=$attempt_budget_seconds" | ||
| export "STRIX_PROCESS_${budget_suffix}_SECONDS=$attempt_process_budget_seconds" | ||
|
|
||
| : > "$strix_run_log" | ||
| bash "$TRUSTED_STRIX_GATE" 2>&1 | tee "$strix_run_log" | ||
|
devin-ai-integration[bot] marked this conversation as resolved.
|
||
| strix_rc="${PIPESTATUS[0]}" | ||
| { | ||
| printf "=== outer-attempt=%s rc=%s ===\\n" "$strix_gate_attempt" "$strix_rc" | ||
| cat "$strix_run_log" | ||
| } >> "$strix_attempt_audit_log" | ||
| 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 | ||
|
Comment on lines
+964
to
+977
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Tail-scoping logic duplicated in two places The retry loop's tail-scoping and signal grep (strix.yml) duplicate the terminal classification block (strix.yml). The two are consistent today, but a future edit to one must be mirrored in the other. Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| break | ||
| fi | ||
|
seonghobae 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 | ||
|
devin-ai-integration[bot] marked this conversation as resolved.
|
||
| 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 | ||
|
seonghobae marked this conversation as resolved.
|
||
| set -e | ||
|
|
||
| if [ "$strix_rc" -eq 0 ]; then | ||
|
|
@@ -908,15 +1000,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 | ||
|
|
@@ -959,6 +1042,10 @@ jobs: | |
| cp "$RUNNER_TEMP/strix_gate_console.log" "$GITHUB_WORKSPACE/strix_runs/gate-console.log" | ||
| copied_reports=1 | ||
| fi | ||
| if [ -f "$RUNNER_TEMP/strix_gate_attempts.log" ]; then | ||
| cp "$RUNNER_TEMP/strix_gate_attempts.log" "$GITHUB_WORKSPACE/strix_runs/gate-attempts.log" | ||
| copied_reports=1 | ||
| fi | ||
| if [ -n "$(find "$GITHUB_WORKSPACE/strix_runs" -mindepth 1 -print -quit)" ]; then | ||
| copied_reports=1 | ||
| fi | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 = "ed3f7b44f9afdd6ab295426e5d0440aeca6bdfb5" | ||
| REVIEW_DISPATCH_BLOB_SHA = "0df7a17cc72a79585cec169c8299e0646f93ab02" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔍 Dispatch workflow blob SHA pin updated
Was this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
|
|
||
| def _workflow_text(path: Path) -> str: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Info: Retry deadline equals the step timeout
strix_gate_deadlineisSECONDS + 6000and the step hastimeout-minutes: 100(6000s). If a gate attempt overruns its internal budget, GitHub can kill the step at the same instant the deadline logic would fire, so the gracefulSTRIX_PROVIDER_UNAVAILABLEmessage can be lost. The outcome stays fail-closed and the loop's 600s reserve keeps normal paths clear, so this is not a bug.Was this helpful? React with 👍 or 👎 to provide feedback.