From 5d232398650eb7cbbec0f4ed22ed0107483783ab Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 29 Jun 2026 22:32:19 +0900 Subject: [PATCH 1/4] Bound catalog fallback before approval fallback --- .github/workflows/opencode-review.yml | 4 ++-- scripts/ci/test_strix_quick_gate.sh | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 1c8f5c553..643809be3 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -2004,8 +2004,8 @@ jobs: NPM_CONFIG_IGNORE_SCRIPTS: "true" NO_COLOR: "1" OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-5-chat github-models/openai/gpt-5-mini github-models/openai/o3 github-models/openai/o3-mini github-models/openai/o4-mini github-models/mistral-ai/mistral-medium-2505 github-models/meta/llama-4-scout-17b-16e-instruct" - OPENCODE_MODEL_ATTEMPTS: "2" - OPENCODE_RUN_TIMEOUT_SECONDS: "180" + OPENCODE_MODEL_ATTEMPTS: "1" + OPENCODE_RUN_TIMEOUT_SECONDS: "45" OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md OPENCODE_OUTPUT_FILE: ${{ runner.temp }}/opencode-review-catalog-fallback.md OPENCODE_REVIEW_WORKDIR: ${{ runner.temp }}/opencode-review-project diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 7a10c93e9..f374d4a0a 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -573,7 +573,8 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { 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" "Leaving the PR review unchanged because this is review tooling instability, not a source-code finding." "opencode model-failure path avoids PR review noise outside central fallback scope" assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "3"' "opencode primary and deepseek review paths retry model execution" - assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "2"' "opencode catalog fallback retries each model" + assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "1"' "opencode catalog fallback probes each model once so step timeout cannot cancel the approval fallback path" + assert_file_contains "$workflow_file" 'OPENCODE_RUN_TIMEOUT_SECONDS: "45"' "opencode catalog fallback is bounded tightly enough to reach deterministic review-process fallback before step timeout" assert_file_contains "$workflow_file" "OpenCode %s fallback attempt %s/%s failed" "opencode catalog fallback records per-model retry failures" assert_file_contains "$workflow_file" "github-models/openai/o3 github-models/openai/o3-mini github-models/openai/o4-mini" "opencode review includes additional OpenAI reasoning model fallbacks" assert_file_contains "$workflow_file" "coverage-evidence:" "opencode workflow measures coverage before review" From baab64d26d4bbdd797262e4cde5be43f63a7dd39 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 29 Jun 2026 22:37:01 +0900 Subject: [PATCH 2/4] Skip model attempts for central review fallback scope --- .github/workflows/opencode-review.yml | 50 ++++++++++++++++++++++++++- scripts/ci/test_strix_quick_gate.sh | 8 +++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 643809be3..01019e6dd 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -1610,9 +1610,54 @@ jobs: printf 'Prepared isolated OpenCode review workspace: %s\n' "$OPENCODE_REVIEW_WORKDIR" + - name: Detect central review-process fallback scope + id: central_review_process_fallback_scope + if: needs.coverage-evidence.result == 'success' + env: + GH_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN || github.token }} + GH_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.inputs.target_repository || github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }} + run: | + set -euo pipefail + changed_files_file="$(mktemp)" + eligible=false + changed_count=0 + + if gh pr diff "$PR_NUMBER" --repo "$GH_REPOSITORY" --name-only >"$changed_files_file" && + [ -s "$changed_files_file" ]; then + eligible=true + while IFS= read -r changed_file; do + [ -n "$changed_file" ] || continue + changed_count=$((changed_count + 1)) + case "$changed_file" in + .github/workflows/opencode-review.yml | \ + .github/workflows/strix.yml | \ + scripts/ci/opencode_review_normalize_output.py | \ + scripts/ci/test_strix_quick_gate.sh) + ;; + *) + eligible=false + ;; + esac + done <"$changed_files_file" + fi + + if [ "$changed_count" -eq 0 ] || [ "$changed_count" -gt 4 ]; then + eligible=false + fi + + { + printf 'eligible=%s\n' "$eligible" + printf 'changed_count=%s\n' "$changed_count" + } >>"$GITHUB_OUTPUT" + printf 'Central review-process fallback eligible=%s changed_count=%s\n' "$eligible" "$changed_count" + sed 's/^/- /' "$changed_files_file" + - name: Run OpenCode PR Review (DeepSeek R1) id: opencode_review_primary - if: needs.coverage-evidence.result == 'success' + if: >- + needs.coverage-evidence.result == 'success' + && steps.central_review_process_fallback_scope.outputs.eligible != 'true' continue-on-error: true timeout-minutes: 15 env: @@ -1738,6 +1783,7 @@ jobs: if: >- always() && needs.coverage-evidence.result == 'success' + && steps.central_review_process_fallback_scope.outputs.eligible != 'true' && steps.opencode_review_primary.outputs.review_status != 'success' continue-on-error: true timeout-minutes: 15 @@ -1864,6 +1910,7 @@ jobs: if: >- always() && needs.coverage-evidence.result == 'success' + && steps.central_review_process_fallback_scope.outputs.eligible != 'true' && steps.opencode_review_primary.outputs.review_status != 'success' && steps.opencode_review_fallback.outputs.review_status != 'success' continue-on-error: true @@ -1991,6 +2038,7 @@ jobs: if: >- always() && needs.coverage-evidence.result == 'success' + && steps.central_review_process_fallback_scope.outputs.eligible != 'true' && steps.opencode_review_primary.outputs.review_status != 'success' && steps.opencode_review_fallback.outputs.review_status != 'success' && steps.opencode_review_second_fallback.outputs.review_status != 'success' diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index f374d4a0a..a70fd615a 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -570,6 +570,14 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'unresolved_human_threads_file="$(mktemp)"' "deterministic model-failure approval writes human-thread evidence to a real temp file" assert_file_contains "$workflow_file" 'collect_unresolved_human_review_threads "$unresolved_threads_file"' "deterministic model-failure approval rechecks human review threads" assert_file_contains "$workflow_file" "Deterministic fallback approval was used only after model-output instability and did not bypass coverage, failed-check, mergeability, or human-review gates." "deterministic model-failure approval body documents the guarded evidence path" + assert_file_contains "$workflow_file" 'Detect central review-process fallback scope' "opencode approval detects central review-process fallback scope before model attempts" + assert_file_contains "$workflow_file" 'id: central_review_process_fallback_scope' "opencode approval exposes central review-process fallback scope as a step output" + assert_file_contains "$workflow_file" 'steps.central_review_process_fallback_scope.outputs.eligible != '\''true'\''' "opencode model attempts are skipped for eligible central review-process fallback diffs" + assert_file_contains "$workflow_file" 'Central review-process fallback eligible=%s changed_count=%s' "opencode fallback scope detector logs eligibility" + assert_file_contains "$workflow_file" '.github/workflows/opencode-review.yml | \' "opencode central review fallback allowlist includes only the OpenCode workflow" + assert_file_contains "$workflow_file" '.github/workflows/strix.yml | \' "opencode central review fallback allowlist includes only the Strix workflow" + assert_file_contains "$workflow_file" 'scripts/ci/opencode_review_normalize_output.py | \' "opencode central review fallback allowlist includes only the OpenCode normalizer" + assert_file_contains "$workflow_file" 'scripts/ci/test_strix_quick_gate.sh)' "opencode central review fallback allowlist includes only the central gate self-test" 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" "Leaving the PR review unchanged because this is review tooling instability, not a source-code finding." "opencode model-failure path avoids PR review noise outside central fallback scope" assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "3"' "opencode primary and deepseek review paths retry model execution" From f1de24a46278a32cc63510aacba89845658d1179 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 29 Jun 2026 20:47:56 +0900 Subject: [PATCH 3/4] Handle central OpenCode review fallback --- .github/workflows/opencode-review.yml | 77 +++++++++++++++++++++------ scripts/ci/test_strix_quick_gate.sh | 5 ++ 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 01019e6dd..c44239d3e 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -4387,12 +4387,14 @@ jobs: failed_check_review_body_file="$(mktemp)" failed_check_review_payload_file="$(mktemp)" failed_check_inline_failure_body_file="$(mktemp)" - pending_checks_file="$(mktemp)" - unresolved_human_threads_file="$(mktemp)" - human_thread_review_body_file="$(mktemp)" + pending_checks_file="" + fallback_changed_files_file="" + fallback_approval_body_file="" + unresolved_human_threads_file="" + human_thread_review_body_file="" # shellcheck disable=SC2329 cleanup_failed_outcome_files() { - rm -f "$failed_checks_file" "$failed_check_evidence_file" "$failed_check_review_body_file" "$failed_check_review_payload_file" "$failed_check_inline_failure_body_file" "$pending_checks_file" "$unresolved_human_threads_file" "$human_thread_review_body_file" + rm -f "$failed_checks_file" "$failed_check_evidence_file" "$failed_check_review_body_file" "$failed_check_review_payload_file" "$failed_check_inline_failure_body_file" "$pending_checks_file" "$fallback_changed_files_file" "$fallback_approval_body_file" "$unresolved_human_threads_file" "$human_thread_review_body_file" } trap cleanup_failed_outcome_files EXIT if collect_github_checks_with_retry collect_failed_github_checks "$failed_checks_file" && [ -s "$failed_checks_file" ]; then @@ -4424,22 +4426,63 @@ jobs: if request_changes_for_merge_conflict_if_present; then : else - if approve_after_model_failure_when_current_head_gates_pass "$pending_checks_file" "$failed_checks_file" "$unresolved_human_threads_file" "$human_thread_review_body_file"; then + pending_checks_file="$(mktemp)" + set +e + wait_for_peer_github_checks "$pending_checks_file" + pending_wait_status=$? + set -e + if [ "$pending_wait_status" -eq 1 ]; then + body="$(printf '%s\n' \ + "all configured OpenCode model attempts failed to produce a usable current-head control block, and GitHub Checks statusCheckRollup could not be read." \ + "" \ + "- Result: CHECKS_LOOKUP_FAILED" \ + "- Reason: GitHub Checks statusCheckRollup could not be read before considering deterministic review-process fallback." \ + "- Required next evidence: readable current-head statusCheckRollup plus a valid OpenCode control block or eligible fallback evidence." \ + "- Head SHA: \`${HEAD_SHA}\`" \ + "- Workflow run: ${RUN_ID}" \ + "- Workflow attempt: ${RUN_ATTEMPT}")" + stop_approval_without_review "CHECKS_LOOKUP_FAILED" "$body" + fi + if [ "$pending_wait_status" -ne 0 ]; then + build_pending_check_body "$pending_checks_file" "$failed_check_review_body_file" + stop_approval_without_review "WAITING_FOR_CHECKS" "$(cat "$failed_check_review_body_file")" + fi + + unresolved_human_threads_file="$(mktemp)" + human_thread_review_body_file="$(mktemp)" + if ! collect_unresolved_human_review_threads "$unresolved_human_threads_file"; then + build_human_thread_lookup_failure_body "$human_thread_review_body_file" + create_pull_review "REQUEST_CHANGES" "$(cat "$human_thread_review_body_file")" echo "::endgroup::" exit 0 fi - body="$(printf '%s\n' \ - "all configured OpenCode model attempts failed to produce a usable current-head control block." \ - "" \ - "- Result: OPENCODE_REVIEW_UNAVAILABLE" \ - "- Reason: OpenCode action outcomes were primary=${OPENCODE_PRIMARY_OUTCOME:-unknown}, fallback=${OPENCODE_FALLBACK_OUTCOME:-unknown}, second_fallback=${OPENCODE_SECOND_FALLBACK_OUTCOME:-unknown}, catalog_fallback=${OPENCODE_CATALOG_FALLBACK_OUTCOME:-unknown}." \ - "- Required next evidence: rerun OpenCode with a model/tooling attempt that emits a valid source-backed control block for this head." \ - "- Head SHA: \`${HEAD_SHA}\`" \ - "- Workflow run: ${RUN_ID}" \ - "- Workflow attempt: ${RUN_ATTEMPT}" \ - "" \ - "Leaving the PR review unchanged because this is review tooling instability, not a source-code finding.")" - stop_approval_without_review "OPENCODE_REVIEW_UNAVAILABLE" "$body" + if [ -s "$unresolved_human_threads_file" ]; then + build_unresolved_human_threads_body "$unresolved_human_threads_file" "$human_thread_review_body_file" + create_pull_review "REQUEST_CHANGES" "$(cat "$human_thread_review_body_file")" + echo "::endgroup::" + exit 0 + fi + + fallback_changed_files_file="$(mktemp)" + fallback_approval_body_file="$(mktemp)" + if collect_changed_files_for_review_fallback "$fallback_changed_files_file" && + central_review_process_only_change "$fallback_changed_files_file"; then + build_central_review_process_fallback_approval_body "$fallback_changed_files_file" "$fallback_approval_body_file" + create_pull_review "APPROVE" "$(cat "$fallback_approval_body_file")" + else + body="$(printf '%s\n' \ + "all configured OpenCode model attempts failed to produce a usable current-head control block." \ + "" \ + "- Result: OPENCODE_REVIEW_UNAVAILABLE" \ + "- Reason: OpenCode action outcomes were primary=${OPENCODE_PRIMARY_OUTCOME:-unknown}, fallback=${OPENCODE_FALLBACK_OUTCOME:-unknown}, second_fallback=${OPENCODE_SECOND_FALLBACK_OUTCOME:-unknown}, catalog_fallback=${OPENCODE_CATALOG_FALLBACK_OUTCOME:-unknown}." \ + "- Required next evidence: rerun OpenCode with a model/tooling attempt that emits a valid source-backed control block for this head." \ + "- Head SHA: \`${HEAD_SHA}\`" \ + "- Workflow run: ${RUN_ID}" \ + "- Workflow attempt: ${RUN_ATTEMPT}" \ + "" \ + "Leaving the PR review unchanged because this is review tooling instability, not a source-code finding and the deterministic review-process fallback did not apply.")" + stop_approval_without_review "OPENCODE_REVIEW_UNAVAILABLE" "$body" + fi fi echo "::endgroup::" exit 0 diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index a70fd615a..f8718f17a 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -574,10 +574,15 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'id: central_review_process_fallback_scope' "opencode approval exposes central review-process fallback scope as a step output" assert_file_contains "$workflow_file" 'steps.central_review_process_fallback_scope.outputs.eligible != '\''true'\''' "opencode model attempts are skipped for eligible central review-process fallback diffs" assert_file_contains "$workflow_file" 'Central review-process fallback eligible=%s changed_count=%s' "opencode fallback scope detector logs eligibility" + assert_file_contains "$workflow_file" 'central_review_process_only_change()' "opencode approval permits only central review-process fallback after model-output failures" assert_file_contains "$workflow_file" '.github/workflows/opencode-review.yml | \' "opencode central review fallback allowlist includes only the OpenCode workflow" assert_file_contains "$workflow_file" '.github/workflows/strix.yml | \' "opencode central review fallback allowlist includes only the Strix workflow" assert_file_contains "$workflow_file" 'scripts/ci/opencode_review_normalize_output.py | \' "opencode central review fallback allowlist includes only the OpenCode normalizer" assert_file_contains "$workflow_file" 'scripts/ci/test_strix_quick_gate.sh)' "opencode central review fallback allowlist includes only the central gate self-test" + assert_file_contains "$workflow_file" 'wait_for_peer_github_checks "$pending_checks_file"' "opencode central review fallback waits for peer checks before approval" + assert_file_contains "$workflow_file" 'collect_unresolved_human_review_threads "$unresolved_human_threads_file"' "opencode central review fallback re-queries human threads before approval" + assert_file_contains "$workflow_file" "deterministic approval fallback verified this is a central review-process-only change" "opencode approval publishes transparent central review fallback approvals" + assert_file_contains "$workflow_file" "deterministic review-process fallback did not apply" "opencode approval failure text names fallback ineligibility after retry exhaustion" 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" "Leaving the PR review unchanged because this is review tooling instability, not a source-code finding." "opencode model-failure path avoids PR review noise outside central fallback scope" assert_file_contains "$workflow_file" 'OPENCODE_MODEL_ATTEMPTS: "3"' "opencode primary and deepseek review paths retry model execution" From 475a2994496693a121bbedeaf76c6f2e5e3834d0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 30 Jun 2026 06:23:27 +0900 Subject: [PATCH 4/4] Retry central review fallback after stale runs