From dbb94a31ee8fbde486ffef3f9e3d15a3296a56be Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 11 Jul 2026 15:18:08 +0900 Subject: [PATCH] fix(opencode): retry invalid control output --- .github/workflows/opencode-review.yml | 8 +++++--- scripts/ci/run_opencode_review_model_pool.sh | 18 ++++++++++-------- tests/test_opencode_agent_contract.py | 5 +++-- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 3bfe0db2c..9e195c2d1 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -2975,9 +2975,11 @@ jobs: OPENCODE_RUN_TIMEOUT_SECONDS: "180" OPENCODE_EXPORT_TIMEOUT_SECONDS: "60" OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "540" - # Stop after one catalog pass; the retry budget should fail closed - # with visible diagnostics before the 350-min job timeout. - OPENCODE_POOL_MAX_CYCLES: "1" + # Keep cycling through the high-sensitivity catalog until a model + # emits a valid control block or the bounded retry/step timeout fires. + # A single invalid no-control response is provider/tool state, not a + # source finding. + OPENCODE_POOL_CYCLE_SLEEP_SECONDS: "15" OPENCODE_BACKOFF_INITIAL_SECONDS: "5" OPENCODE_BACKOFF_MAX_SECONDS: "5" OPENCODE_FIRST_ATTEMPT_AGENT: ci-review diff --git a/scripts/ci/run_opencode_review_model_pool.sh b/scripts/ci/run_opencode_review_model_pool.sh index 44ef11a93..cd3686854 100644 --- a/scripts/ci/run_opencode_review_model_pool.sh +++ b/scripts/ci/run_opencode_review_model_pool.sh @@ -278,13 +278,15 @@ main() { if [ "$run_status" -eq 2 ]; then break fi - retry_sleep="$(backoff_sleep "$attempt")" - if [ "$deadline" -gt 0 ] && [ $((SECONDS + retry_sleep)) -gt "$deadline" ]; then - retry_sleep=$((deadline - SECONDS)) - fi - if [ "$retry_sleep" -gt 0 ]; then - printf 'Retrying OpenCode after exponential backoff of %ss.\n' "$retry_sleep" - sleep "$retry_sleep" + if [ "$attempt" -lt "$attempts" ]; then + retry_sleep="$(backoff_sleep "$attempt")" + if [ "$deadline" -gt 0 ] && [ $((SECONDS + retry_sleep)) -gt "$deadline" ]; then + retry_sleep=$((deadline - SECONDS)) + fi + if [ "$retry_sleep" -gt 0 ]; then + printf 'Retrying OpenCode after exponential backoff of %ss.\n' "$retry_sleep" + sleep "$retry_sleep" + fi fi done done @@ -295,7 +297,7 @@ main() { record_pool_exhausted exit 1 fi - printf 'OpenCode retry budget and the workflow step timeout remain the outer guards for provider stalls.\n' + printf 'OpenCode retry budget and the workflow step timeout remain the outer guards for invalid or unavailable provider output.\n' cycle_sleep="${OPENCODE_POOL_CYCLE_SLEEP_SECONDS:-60}" if [ "$deadline" -gt 0 ] && [ $((SECONDS + cycle_sleep)) -gt "$deadline" ]; then cycle_sleep=$((deadline - SECONDS)) diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index f548d828e..368275858 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -446,7 +446,8 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): assert 'OPENCODE_RUN_TIMEOUT_SECONDS: "180"' in workflow assert 'OPENCODE_EXPORT_TIMEOUT_SECONDS: "60"' in workflow assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "540"' in workflow - assert 'OPENCODE_POOL_MAX_CYCLES: "1"' in workflow + assert "OPENCODE_POOL_MAX_CYCLES" not in workflow + assert 'OPENCODE_POOL_CYCLE_SLEEP_SECONDS: "15"' in workflow assert 'OPENCODE_BACKOFF_MAX_SECONDS: "5"' in workflow assert 'OPENCODE_EXHAUSTED_REKICK_INITIAL_SLEEP_SECONDS: "15"' in workflow assert 'OPENCODE_EXHAUSTED_REKICK_MAX_SLEEP_SECONDS: "30"' in workflow @@ -462,7 +463,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): assert "OpenCode model pool has no configured model candidates." in model_pool_runner assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS:-2400' in model_pool_runner assert "completed a full model-candidate cycle without a valid control conclusion" in model_pool_runner - assert "retry budget and the workflow step timeout" in model_pool_runner + assert "workflow step timeout remain the outer guards for invalid or unavailable provider output" in model_pool_runner assert "OpenCode model pool exhausted before producing a valid control conclusion." in model_pool_runner assert 'record_review_status "exhausted"' in model_pool_runner assert "retry budget exhausted" not in model_pool_runner