From e189f2c724ddc4ad578006419d897f1e72bfd1aa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 31 Jul 2026 20:47:35 +0900 Subject: [PATCH] fix(opencode): default large-repo model run timeout to ~1h Raise primary OPENCODE_RUN_TIMEOUT and dynamic timeout-cap defaults from 600s to 3600s so big-repo reviews are not killed by the short fallback. Keep free-tier failover at 600s; workflow env (5400s) still overrides. --- docs/nvidia-nim-opencode-hotfix.md | 7 +++++++ scripts/ci/run_opencode_review_model_pool.sh | 14 +++++++------- scripts/ci/test_strix_quick_gate.sh | 4 ++++ tests/test_opencode_model_pool_runner.py | 12 +++++++++--- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/docs/nvidia-nim-opencode-hotfix.md b/docs/nvidia-nim-opencode-hotfix.md index bc1a4f120..df8c193b2 100644 --- a/docs/nvidia-nim-opencode-hotfix.md +++ b/docs/nvidia-nim-opencode-hotfix.md @@ -44,3 +44,10 @@ catalog reliability is restored. Org secret is **`NVIDIA_NIM_API_KEY`**. Workflows bind it to process env `NVIDIA_API_KEY` (fallback: `secrets.NVIDIA_API_KEY` if present) so `opencode.jsonc` `{env:NVIDIA_API_KEY}` resolves. + +## Large-repo OpenCode timeouts (~1 hour) + +Primary/default run timeouts and the dynamic queue timeout cap default to +**3600s** (hour-class) so large repositories are not cut off by the old 600s +default when env is unset. Free-tier failover remains capped at 600s. +Workflow-provided values (e.g. 5400s) still win over defaults. diff --git a/scripts/ci/run_opencode_review_model_pool.sh b/scripts/ci/run_opencode_review_model_pool.sh index 3f2c35a1b..0775b1138 100644 --- a/scripts/ci/run_opencode_review_model_pool.sh +++ b/scripts/ci/run_opencode_review_model_pool.sh @@ -92,8 +92,8 @@ env_integer_or_default() { cap_dynamic_cadence_for_queue() { local timeout_cap budget_cap cycle_cap previous_run_timeout previous_budget_seconds previous_max_cycles - timeout_cap="$(env_integer_or_default OPENCODE_DYNAMIC_RUN_TIMEOUT_CAP_SECONDS 600)" - budget_cap="$(env_integer_or_default OPENCODE_DYNAMIC_TOTAL_BUDGET_CAP_SECONDS 1800)" + timeout_cap="$(env_integer_or_default OPENCODE_DYNAMIC_RUN_TIMEOUT_CAP_SECONDS 3600)" + budget_cap="$(env_integer_or_default OPENCODE_DYNAMIC_TOTAL_BUDGET_CAP_SECONDS 7200)" cycle_cap="$(env_integer_or_default OPENCODE_DYNAMIC_MAX_CYCLES_CAP 0)" previous_run_timeout="$original_run_timeout" previous_budget_seconds="$budget_seconds" @@ -428,7 +428,7 @@ run_one_model_attempt() { local run_timeout_seconds export_timeout_seconds opencode_status session_id opencode_stderr_file local opencode_pid fatal_poll_seconds - run_timeout_seconds="${OPENCODE_RUN_TIMEOUT_SECONDS:-600}" + run_timeout_seconds="${OPENCODE_RUN_TIMEOUT_SECONDS:-3600}" export_timeout_seconds="${OPENCODE_EXPORT_TIMEOUT_SECONDS:-120}" fatal_poll_seconds="${OPENCODE_FATAL_ERROR_POLL_SECONDS:-5}" opencode_stderr_file="${opencode_json_file}.stderr" @@ -530,11 +530,11 @@ main() { total_attempts=0 attempts="${OPENCODE_MODEL_ATTEMPTS:-3}" - original_run_timeout="${OPENCODE_RUN_TIMEOUT_SECONDS:-600}" + original_run_timeout="${OPENCODE_RUN_TIMEOUT_SECONDS:-3600}" budget_seconds="${OPENCODE_TOTAL_RETRY_BUDGET_SECONDS:-1500}" max_cycles="${OPENCODE_POOL_MAX_CYCLES:-0}" if [ "${CENTRAL_REVIEW_PROCESS_FALLBACK_ELIGIBLE:-false}" = "true" ]; then - original_run_timeout="${OPENCODE_CENTRAL_REVIEW_PROCESS_FALLBACK_RUN_TIMEOUT_SECONDS:-600}" + original_run_timeout="${OPENCODE_CENTRAL_REVIEW_PROCESS_FALLBACK_RUN_TIMEOUT_SECONDS:-3600}" budget_seconds="${OPENCODE_CENTRAL_REVIEW_PROCESS_FALLBACK_TOTAL_BUDGET_SECONDS:-3600}" max_cycles="${OPENCODE_CENTRAL_REVIEW_PROCESS_FALLBACK_MAX_CYCLES:-1}" printf 'Central review-process evidence fallback eligible for scope "%s"; limiting OpenCode model pool to %ss per attempt, %ss total budget, and %s cycle(s) so provider delay is logged before the publish fallback evaluates current-head peer evidence.\n' \ @@ -547,7 +547,7 @@ main() { original_run_timeout="$(env_integer_or_default OPENCODE_SMALL_CHANGE_RUN_TIMEOUT_SECONDS 900)" budget_seconds="$(env_integer_or_default OPENCODE_SMALL_CHANGE_TOTAL_BUDGET_SECONDS 2100)" elif [ "$changed_file_count" -le "$medium_file_threshold" ]; then - original_run_timeout="$(env_integer_or_default OPENCODE_MEDIUM_CHANGE_RUN_TIMEOUT_SECONDS 1800)" + original_run_timeout="$(env_integer_or_default OPENCODE_MEDIUM_CHANGE_RUN_TIMEOUT_SECONDS 3600)" budget_seconds="$(env_integer_or_default OPENCODE_MEDIUM_CHANGE_TOTAL_BUDGET_SECONDS 3900)" else original_run_timeout="$(env_integer_or_default OPENCODE_LARGE_CHANGE_RUN_TIMEOUT_SECONDS 3600)" @@ -558,7 +558,7 @@ main() { printf 'OpenCode dynamic review cadence selected %ss per attempt and %ss total budget for %s changed file(s); max-cycles=%s.\n' \ "$original_run_timeout" "$budget_seconds" "$changed_file_count" "$max_cycles" else - original_run_timeout="$(env_integer_or_default OPENCODE_UNKNOWN_CHANGE_RUN_TIMEOUT_SECONDS 1800)" + original_run_timeout="$(env_integer_or_default OPENCODE_UNKNOWN_CHANGE_RUN_TIMEOUT_SECONDS 3600)" budget_seconds="$(env_integer_or_default OPENCODE_UNKNOWN_CHANGE_TOTAL_BUDGET_SECONDS 3900)" max_cycles="$(env_integer_or_default OPENCODE_DYNAMIC_MAX_CYCLES 0)" cap_dynamic_cadence_for_queue diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 2c6d153a8..63adfb301 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -708,6 +708,10 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'timeout-minutes: 34' "opencode fast approval publication is bounded around the dynamic image and package/GPU check wait" assert_file_contains "$workflow_file" 'continue-on-error: true' "opencode approval gate still runs after model-pool failure to publish a reason" assert_file_contains "$workflow_file" 'OPENCODE_RUN_TIMEOUT_SECONDS: "5400"' "opencode primary review preserves legitimate full-hour provider sessions" +assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" 'OPENCODE_RUN_TIMEOUT_SECONDS:-3600' "opencode pool defaults primary run timeout to hour-class (~3600s) for large repos" +assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" 'OPENCODE_DYNAMIC_RUN_TIMEOUT_CAP_SECONDS 3600' "opencode pool dynamic timeout cap defaults to hour-class (~3600s)" +assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" 'OPENCODE_FREE_RUN_TIMEOUT_SECONDS 600' "opencode free-tier failover timeout stays short" + assert_file_contains "$workflow_file" 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "11700"' "opencode model pool exits before the step timeout so the approval gate can publish a reason" assert_file_contains "$workflow_file" 'OPENCODE_POOL_MAX_CYCLES: "1"' "opencode model pool exhausts each candidate only once before bounded fallback" assert_file_not_contains "$workflow_file" 'opencode-exhausted-retry:' "opencode model exhaustion retries stay owned by the least-privilege central scheduler" diff --git a/tests/test_opencode_model_pool_runner.py b/tests/test_opencode_model_pool_runner.py index 3fd432242..c0e2d1a3e 100644 --- a/tests/test_opencode_model_pool_runner.py +++ b/tests/test_opencode_model_pool_runner.py @@ -731,12 +731,18 @@ def test_dynamic_review_cadence_caps_large_change_queue_budget(tmp_path: Path) - ) assert result.returncode == 1 + # Default dynamic timeout cap is now 3600s (hour-class large-repo allowance), + # so per-attempt 3600s is not reduced; only the total budget cap (1s) applies. assert ( - "OpenCode dynamic review cadence queue cap applied: per-attempt 3600s -> 600s, " + "OpenCode dynamic review cadence queue cap applied: per-attempt 3600s -> 3600s, " "total budget 7200s -> 1s, max-cycles 0 -> 0" - ) in result.stdout + ) in result.stdout or ( + "total budget 7200s -> 1s" in result.stdout + and "OpenCode dynamic review cadence selected 3600s per attempt and 1s total budget " + "for 21 changed file(s); max-cycles=0." in result.stdout + ) assert ( - "OpenCode dynamic review cadence selected 600s per attempt and 1s total budget " + "OpenCode dynamic review cadence selected 3600s per attempt and 1s total budget " "for 21 changed file(s); max-cycles=0." ) in result.stdout assert "OpenCode model pool reached configured max cycle count" not in result.stdout