diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 4b6a0abba..47d77c10c 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -823,7 +823,7 @@ jobs: needs: [coverage-evidence] if: always() && (github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request_target') runs-on: ubuntu-latest - timeout-minutes: 75 + timeout-minutes: 360 permissions: actions: write checks: read @@ -2239,7 +2239,7 @@ jobs: id: opencode_review_model_pool if: needs.coverage-evidence.result == 'success' continue-on-error: true - timeout-minutes: 20 + timeout-minutes: 360 env: STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} GITHUB_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} @@ -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_RUN_TIMEOUT_SECONDS: "240" + OPENCODE_MODEL_ATTEMPTS: "5" + OPENCODE_RUN_TIMEOUT_SECONDS: "900" OPENCODE_EXPORT_TIMEOUT_SECONDS: "120" - OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "360" - OPENCODE_BACKOFF_INITIAL_SECONDS: "30" - OPENCODE_BACKOFF_MAX_SECONDS: "30" + OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "14400" + OPENCODE_BACKOFF_INITIAL_SECONDS: "60" + OPENCODE_BACKOFF_MAX_SECONDS: "600" OPENCODE_FIRST_ATTEMPT_AGENT: ci-review OPENCODE_AGENT: ci-review-fallback OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md @@ -2602,7 +2602,7 @@ jobs: - name: Approve PR if OpenCode review passed if: always() - timeout-minutes: 75 + timeout-minutes: 360 env: GH_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN || steps.opencode_app_token.outputs.token || github.token }} CHECK_LOOKUP_GH_TOKEN: ${{ github.token }} diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index c5dd7a9b5..5d9a4bc70 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -210,16 +210,16 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): assert '"## Review outcome"' in workflow assert '"## Check outcome"' not in workflow assert "publish REQUEST_CHANGES when coverage-evidence blocker states" in workflow - assert 'timeout-minutes: 75' in workflow - assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,240}timeout-minutes: 20", workflow) + assert 'timeout-minutes: 360' in workflow + assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,240}timeout-minutes: 360", workflow) 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_RUN_TIMEOUT_SECONDS: "240"' in workflow + assert 'OPENCODE_MODEL_ATTEMPTS: "5"' in workflow + assert 'OPENCODE_RUN_TIMEOUT_SECONDS: "900"' in workflow assert 'OPENCODE_EXPORT_TIMEOUT_SECONDS: "120"' in workflow - assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "360"' in workflow - assert 'OPENCODE_BACKOFF_MAX_SECONDS: "30"' in workflow + assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "14400"' in workflow + assert 'OPENCODE_BACKOFF_MAX_SECONDS: "600"' in workflow assert "${{ runner.temp }}/opencode-review-model-pool.md" in workflow assert re.search(r'check-runs" \\\n\s+-f per_page=100 \\\n\s+--paginate \\\n\s+--slurp \|\n\s+jq -r "\$jq_filter"', workflow) assert not re.search(r"--slurp\s*\\\n\s*--jq", workflow) diff --git a/tests/test_pr_review_merge_scheduler.py b/tests/test_pr_review_merge_scheduler.py index c66313517..b74f89a0b 100644 --- a/tests/test_pr_review_merge_scheduler.py +++ b/tests/test_pr_review_merge_scheduler.py @@ -2797,3 +2797,21 @@ def test_run_masks_secrets_in_args(): err_msg = str(exc_info.value) assert "ghp_abcdef1234567890abcdef1234567890abcdef" not in err_msg assert "***" in err_msg + +def test_parse_workflow_action_required_reason(): + """Test extracting ACTION_REQUIRED check names from a scheduler reason.""" + # Test valid marker extraction + reason = "Some other text workflow action required: Check 1; Check 2" + assert sched.parse_workflow_action_required_reason(reason) == "Check 1" + + # Test missing marker + reason2 = "Some text without the marker" + assert sched.parse_workflow_action_required_reason(reason2) is None + + # Test marker with no extra checks + reason3 = "workflow action required:" + assert sched.parse_workflow_action_required_reason(reason3) is None + + # Test marker with whitespace + reason4 = "workflow action required: " + assert sched.parse_workflow_action_required_reason(reason4) is None