From 1a3d01aa64078fb1eba808037e2514ffb8bebd9f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 1 Jul 2026 16:39:20 +0900 Subject: [PATCH 1/3] Fix OpenCode check-run slurp filtering --- .github/workflows/opencode-review.yml | 15 ++++++++++++--- scripts/ci/test_strix_quick_gate.sh | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index b228fe8d..ed02dce8 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -4022,6 +4022,7 @@ jobs: local output_file="$1" local mode="$2" local jq_filter + local check_runs_pages_file case "$mode" in failed) @@ -4054,11 +4055,19 @@ jobs: ;; esac - gh api -X GET "repos/${GH_REPOSITORY}/commits/${HEAD_SHA}/check-runs" \ + check_runs_pages_file="$(mktemp)" + if ! gh api -X GET "repos/${GH_REPOSITORY}/commits/${HEAD_SHA}/check-runs" \ -f per_page=100 \ --paginate \ - --slurp \ - --jq "$jq_filter" >"$output_file" + --slurp >"$check_runs_pages_file"; then + rm -f "$check_runs_pages_file" + return 1 + fi + if ! jq -r "$jq_filter" "$check_runs_pages_file" >"$output_file"; then + rm -f "$check_runs_pages_file" + return 1 + fi + rm -f "$check_runs_pages_file" } current_head_manual_strix_success_status() { diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index ffea57cd..163f1c4e 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -691,6 +691,8 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'collect_current_head_commit_check_runs()' "opencode approval falls back to current-head commit check-runs when PR rollup lags" assert_file_contains "$workflow_file" 'commits/${HEAD_SHA}/check-runs' "opencode approval queries current-head commit check-runs before changing review state" assert_file_contains "$workflow_file" '--slurp' "opencode approval aggregates paginated commit check-runs before classifying them" + assert_file_contains "$workflow_file" 'jq -r "$jq_filter" "$check_runs_pages_file" >"$output_file"' "opencode approval filters slurped commit check-runs with jq instead of unsupported gh api --jq" + assert_file_not_contains "$workflow_file" '--jq "$jq_filter" >"$output_file"' "opencode approval must not combine gh api --slurp with --jq" assert_file_contains "$workflow_file" 'group_by(.name // "")' "opencode approval keeps only the latest same-name commit check-run" assert_file_contains "$workflow_file" 'map(last)' "opencode approval ignores superseded same-name commit check-runs" assert_file_contains "$workflow_file" 'collect_current_head_commit_check_runs "$commit_check_runs_file" pending' "opencode approval blocks approval on pending commit check-runs omitted from PR rollup" From deff26648ab133fa6b1bdc0f43d1a1b3be710cd1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 1 Jul 2026 16:44:22 +0900 Subject: [PATCH 2/3] Tighten OpenCode slurp regression assertion --- scripts/ci/test_strix_quick_gate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 163f1c4e..84db56c4 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -690,7 +690,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'collect_current_head_strix_workflow_runs()' "opencode approval separately accounts for jobless current-head Strix workflow runs" assert_file_contains "$workflow_file" 'collect_current_head_commit_check_runs()' "opencode approval falls back to current-head commit check-runs when PR rollup lags" assert_file_contains "$workflow_file" 'commits/${HEAD_SHA}/check-runs' "opencode approval queries current-head commit check-runs before changing review state" - assert_file_contains "$workflow_file" '--slurp' "opencode approval aggregates paginated commit check-runs before classifying them" + assert_file_contains "$workflow_file" '--slurp >"$check_runs_pages_file"' "opencode approval aggregates paginated commit check-runs before classifying them" assert_file_contains "$workflow_file" 'jq -r "$jq_filter" "$check_runs_pages_file" >"$output_file"' "opencode approval filters slurped commit check-runs with jq instead of unsupported gh api --jq" assert_file_not_contains "$workflow_file" '--jq "$jq_filter" >"$output_file"' "opencode approval must not combine gh api --slurp with --jq" assert_file_contains "$workflow_file" 'group_by(.name // "")' "opencode approval keeps only the latest same-name commit check-run" From 0cf879313104b7c55e0d4da6c636aaebddd75666 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 12:10:16 +0000 Subject: [PATCH 3/3] feat: raise OPENCODE_MODEL_ATTEMPTS to 3 with 300s backoff cap for exponential retry --- .github/workflows/opencode-review.yml | 4 ++-- scripts/ci/test_strix_quick_gate.sh | 6 +++--- tests/test_opencode_agent_contract.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index f793caf0..1d2e3a9a 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -2248,12 +2248,12 @@ jobs: NPM_CONFIG_IGNORE_SCRIPTS: "true" NO_COLOR: "1" OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-5-nano" - OPENCODE_MODEL_ATTEMPTS: "1" + OPENCODE_MODEL_ATTEMPTS: "3" OPENCODE_RUN_TIMEOUT_SECONDS: "18000" OPENCODE_EXPORT_TIMEOUT_SECONDS: "600" OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "18000" OPENCODE_BACKOFF_INITIAL_SECONDS: "30" - OPENCODE_BACKOFF_MAX_SECONDS: "30" + OPENCODE_BACKOFF_MAX_SECONDS: "300" OPENCODE_FIRST_ATTEMPT_AGENT: ci-review OPENCODE_AGENT: ci-review-fallback OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 73dc0283..8ffd6310 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -513,7 +513,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" "needs.coverage-evidence.result == 'success'" "opencode model pool only runs after coverage evidence passed" assert_file_contains "$workflow_file" "id: opencode_review_model_pool" "opencode DeepSeek V3 fallback still runs after a primary model timeout or step failure when coverage evidence passed" assert_file_contains "$workflow_file" "always()" "opencode fallback chain uses always() so failed model steps cannot skip every fallback" - assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "1"' "opencode fallback tries the catalog promptly instead of spending the entire review on one model" + assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "3"' "opencode model pool retries transiently failing requests with exponential backoff before moving to the next candidate" assert_file_contains "$workflow_file" "Run OpenCode PR Review model pool" "opencode review includes a broad catalog fallback pool" assert_file_contains "$workflow_file" "continue-on-error: true" "opencode model step timeouts do not prevent fallback review publication" assert_file_contains "$workflow_file" 'OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-5-nano"' "opencode review uses the required nano model path" @@ -616,8 +616,8 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_not_contains "$workflow_file" '[ "$changed_count" -gt 0 ] && [ "$changed_count" -le 2 ]' "opencode model-exhaustion fallback must not cap deterministic approval scope" assert_file_contains "$workflow_file" "all configured OpenCode model attempts failed to produce a usable current-head control block" "opencode model-output failures fail the check without publishing a review" assert_file_contains "$workflow_file" "no model produced a valid review control block" "opencode model-failure path documents why approval is withheld" - assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "1"' "opencode primary and fallback paths avoid multi-attempt stalls on one model" - assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "1"' "opencode catalog fallback tries each model once before moving on" + assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "3"' "opencode model pool retries each candidate with exponential backoff before exhaustion" + assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "3"' "opencode model pool uses multiple attempts so a transient failure does not immediately exhaust the review" assert_file_contains "$workflow_file" 'OPENCODE_RUN_TIMEOUT_SECONDS: "18000"' "opencode catalog fallback can run for large repositories before step timeout" assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "OpenCode %s attempt %s/%s failed" "opencode catalog fallback records per-model retry failures" assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "exponential backoff" "opencode model retry paths use exponential backoff instead of fixed sleeps" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 4d736563..bc61ee71 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -215,11 +215,11 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): assert 'APPROVAL_CHECK_WAIT_ATTEMPTS: "81"' in workflow assert 'APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "30"' in workflow assert 'OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-5-nano"' in workflow - assert 'OPENCODE_MODEL_ATTEMPTS: "1"' in workflow + assert 'OPENCODE_MODEL_ATTEMPTS: "3"' in workflow assert 'OPENCODE_RUN_TIMEOUT_SECONDS: "18000"' in workflow assert 'OPENCODE_EXPORT_TIMEOUT_SECONDS: "600"' in workflow assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "18000"' in workflow - assert 'OPENCODE_BACKOFF_MAX_SECONDS: "30"' in workflow + assert 'OPENCODE_BACKOFF_MAX_SECONDS: "300"' in workflow assert "${{ runner.temp }}/opencode-review-model-pool.md" in workflow assert 'jq -r "$jq_filter" "$check_runs_pages_file" >"$output_file"' in workflow assert not re.search(r"--slurp\s*\\\n\s*--jq", workflow)