From 09f4f7ee7c7c5a6d4e9f15a307a7d7e71967ace6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 10 Jul 2026 22:32:30 +0900 Subject: [PATCH 1/3] test: align OpenCode timeout contracts --- scripts/ci/pr_review_merge_scheduler.py | 2 +- scripts/ci/test_strix_quick_gate.sh | 31 +++++++++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/scripts/ci/pr_review_merge_scheduler.py b/scripts/ci/pr_review_merge_scheduler.py index 27a88c21b..acd1a8d08 100644 --- a/scripts/ci/pr_review_merge_scheduler.py +++ b/scripts/ci/pr_review_merge_scheduler.py @@ -109,7 +109,7 @@ """ + PULL_REQUEST_FIELDS_FRAGMENT OPEN_PRS_PAGE_SIZE = 25 -# Must exceed the opencode-review job timeout (360 min) plus typical runner-queue +# Must exceed the opencode-review job timeout (120 min) plus typical runner-queue # wait. QUEUED counts as running and the age clock starts at check creation, so a # 45-minute threshold marked every queued/long review "stale" and re-dispatched # it; each re-dispatch went to the back of the runner queue and itself went diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 8a87703ad..5ad49d275 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -386,10 +386,10 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { record_failure "opencode required workflow bootstrap must not depend on required-workflow event payload fields" fi assert_file_contains "$workflow_file" 'github.event.pull_request.base.repo.full_name || github.event.inputs.target_repository || github.repository' "opencode review scopes concurrency by target repository" - assert_file_contains "$workflow_file" "format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha)" "opencode review scopes pull_request_target concurrency by current head" - assert_file_contains "$workflow_file" "format('pr-{0}-{1}', github.event.inputs.pr_number, github.event.inputs.pr_head_sha)" "opencode review scopes manual concurrency by target PR head" + assert_file_contains "$workflow_file" "format('pr-{0}', github.event.pull_request.number)" "opencode review scopes pull_request_target concurrency to the active PR so newer heads cancel stale review runs" + assert_file_contains "$workflow_file" "format('pr-{0}', github.event.inputs.pr_number)" "opencode review scopes manual concurrency to the requested PR so newer heads cancel stale review runs" assert_file_contains "$workflow_file" 'cancel-in-progress: true' "opencode review cancels stale in-progress review attempts when a newer PR event arrives" - assert_file_contains "$workflow_file" "Checkout pull request merge ref for coverage measurement" "opencode pull_request_target coverage execution uses the trusted merge ref" + assert_file_contains "$workflow_file" "Materialize pull request merge tree for coverage measurement" "opencode pull_request_target coverage execution materializes the trusted base/head merge tree" assert_file_contains "$workflow_file" "stale OpenCode run: event head=" "opencode review side effects are skipped for stale heads" assert_file_contains "$workflow_file" "github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name" "opencode pull_request_target coverage execution is limited to same-repository PR heads using the target PR base repo" assert_file_not_contains "$workflow_file" "github.event.pull_request.head.repo.full_name == github.repository" "opencode required workflow must not compare PR head repo to the central workflow source repository" @@ -423,19 +423,19 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'install_deps <- c("Depends", "Imports", "LinkingTo")' "opencode R coverage avoids installing oversized suggested dependencies" assert_file_contains "$workflow_file" 'read.dcf("DESCRIPTION")' "opencode R coverage installs target package dependencies from DESCRIPTION" assert_file_contains "$workflow_file" "R package testthat suite" "opencode R package coverage requires package testthat evidence" - assert_file_contains "$workflow_file" "R coverage tooling install unavailable in coverage runner; deferring to required peer R CMD check evidence." "opencode R coverage defers runner package-install failures to required peer R checks" + assert_file_contains "$workflow_file" "R coverage tooling install unavailable or exceeded the runner time budget; deferring to required peer R CMD check evidence." "opencode R coverage defers runner package-install failures to required peer R checks" assert_file_contains "$workflow_file" "testthat unavailable in coverage runner; deferring to required peer R CMD check evidence." "opencode R package tests defer only when testthat cannot be installed in the coverage runner" assert_file_contains "$workflow_file" "covr package_coverage unavailable after package tests; treating missing-line report as advisory." "opencode R package coverage does not block on covr installation reproduction after tests pass" assert_file_contains "$workflow_file" "R coverage tooling packages unavailable after install" "opencode R coverage verifies covr/testthat are loadable after installation" assert_file_contains "$workflow_file" "repository: ContextualWisdomLab/.github" "opencode required workflow checks out the central source repository" assert_file_contains "$workflow_file" 'ref: ${{ steps.trusted_source.outputs.ref }}' "opencode required workflow checks out the resolved central ref" assert_file_contains "$workflow_file" "target_repository:" "opencode workflow_dispatch can target a repository whose PR does not inherit required workflows" - assert_file_contains "$workflow_file" "Checkout pull request merge ref for coverage measurement" "opencode coverage measures the PR merge ref instead of exposing secrets to PR-head execution" - assert_file_contains "$workflow_file" 'repository: ${{ github.event.pull_request.base.repo.full_name }}' "opencode pull_request_target coverage reads the trusted base repository merge ref" + assert_file_contains "$workflow_file" "Materialize pull request merge tree for coverage measurement" "opencode coverage measures the PR merge tree instead of exposing secrets to untrusted checkout actions" + assert_file_contains "$workflow_file" 'TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.inputs.target_repository || github.repository }}' "opencode pull_request_target coverage fetches exact base/head commits from the target repository" assert_file_contains "$workflow_file" "Exchange OpenCode app token for target repository review reads" "opencode review can read private target repositories through the OpenCode app token before materializing review data" assert_file_contains "$workflow_file" 'GH_TOKEN: ${{ steps.review_read_app_token.outputs.token || secrets.OPENCODE_APPROVE_TOKEN || github.token }}' "opencode materialization prefers the OpenCode app token for private target repository reads" assert_file_contains "$workflow_file" '[ "${GH_REPOSITORY:-}" != "${GITHUB_REPOSITORY:-}" ]' "opencode approval uses the app token for target-repository check lookup" - assert_file_contains "$workflow_file" "path: pr-head" "opencode coverage keeps PR-head data outside the trusted workflow root" + assert_file_contains "$workflow_file" 'COVERAGE_SOURCE_WORKDIR: ${{ github.workspace }}/pr-head' "opencode coverage keeps PR-head data outside the trusted workflow root" assert_file_contains "$workflow_file" 'COVERAGE_SOURCE_WORKDIR: ${{ github.workspace }}/pr-head' "opencode coverage measures the PR-head checkout explicitly" assert_file_not_contains "$workflow_file" "pr_head_ref:" "opencode workflow_dispatch no longer accepts an unused PR head branch input" assert_file_not_contains "$workflow_file" 'github.event.inputs.pr_head_ref' "opencode review no longer wires unused PR head branch input" @@ -533,11 +533,11 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "not a generic model-exhaustion message" "opencode review tells models to return concrete missing-evidence findings instead of progress-only output" assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "tokens_limit_reached" "opencode review detects provider context-window overflow" assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "skipping remaining attempts for this model" "opencode review skips same-model retries after context-window overflow" - assert_file_contains "$workflow_file" 'timeout-minutes: 360' "opencode review target uses the maximum GitHub-hosted runner timeout" - assert_file_contains "$workflow_file" 'timeout-minutes: 350' "opencode model pool has a deep-review runner budget while the script deadline leaves approval headroom" + assert_file_contains "$workflow_file" 'timeout-minutes: 120' "opencode review target keeps a bounded runner budget so stalled reviews release queue capacity" + assert_file_contains "$workflow_file" 'timeout-minutes: 90' "opencode model pool leaves approval-gate headroom while capping stalled model attempts" 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 has a deep but bounded per-model timeout before trying fallback models" - assert_file_contains "$workflow_file" 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "18000"' "opencode model pool exits before the job timeout so the approval gate can publish a reason" + assert_file_contains "$workflow_file" 'OPENCODE_RUN_TIMEOUT_SECONDS: "900"' "opencode primary review has a bounded per-model timeout before trying fallback models" + assert_file_contains "$workflow_file" 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "5400"' "opencode model pool exits before the job timeout so the approval gate can publish a reason" assert_file_contains "$workflow_file" 'OPENCODE_POOL_MAX_CYCLES: "1"' "opencode model pool stops after one full candidate pass instead of looping to the job timeout" 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" @@ -607,7 +607,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'load_selected_review_output()' "opencode approval step has a direct selected-output fallback when the overview comment is stale or invalid" assert_file_contains "$workflow_file" "gate result from Review Overview comment" "opencode approval step distinguishes overview-comment gate results" assert_file_contains "$workflow_file" "gate result from selected OpenCode output" "opencode approval step can recover from an invalid overview by validating the selected successful output" - assert_file_contains "$workflow_file" 'timeout-minutes: 300' "opencode approval step has a bounded wall-clock timeout" + assert_file_contains "$workflow_file" 'timeout-minutes: 75' "opencode approval step has a bounded wall-clock timeout" assert_file_contains "$workflow_file" 'APPROVAL_CHECK_WAIT_ATTEMPTS: "81"' "opencode approval waits for bounded long-running peer checks before approving" assert_file_contains "$workflow_file" 'CHECK_LOOKUP_RETRY_ATTEMPTS: "5"' "opencode approval retries transient GitHub check lookup failures before changing review state" assert_file_contains "$workflow_file" 'GitHub Checks lookup failed; retrying' "opencode approval logs transient check lookup retries" @@ -649,7 +649,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_not_contains "$workflow_file" "no model produced a valid review control block" "opencode model-failure path no longer documents a final exhausted state" 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_RUN_TIMEOUT_SECONDS: "5400"' "opencode catalog fallback has a deep but bounded per-model review timeout before step timeout" + assert_file_contains "$workflow_file" 'OPENCODE_RUN_TIMEOUT_SECONDS: "900"' "opencode catalog fallback has a bounded per-model review timeout 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" assert_file_contains "$workflow_file" "github-models/deepseek/deepseek-v3-0324 openai/gpt-5-mini openai/gpt-5 github-models/openai/o4-mini" "opencode review tries the observed high-success DeepSeek V3 path before native OpenAI and compact OpenAI reasoning fallbacks" @@ -675,7 +675,8 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'fetch --no-tags --prune --no-recurse-submodules origin "$PR_BASE_SHA" "$PR_HEAD_SHA"' "coverage evidence fetches exact base and head commits as data" assert_file_contains "$workflow_file" 'merge --no-ff --no-edit "$PR_HEAD_SHA"' "coverage evidence materializes the current pull request merge tree without action checkout" assert_file_contains "$workflow_file" "Coverage merge tree could not be materialized" "coverage evidence logs an actionable merge-tree failure reason" - assert_file_contains "$workflow_file" "--require-hashes --only-binary=:all: -r requirements-opencode-review-ci-hashes.txt" "coverage tooling installs from a hash-pinned lock" + assert_file_contains "$workflow_file" "--require-hashes" "coverage tooling installs from a hash-pinned lock" + assert_file_contains "$workflow_file" "--only-binary=:all: -r requirements-opencode-review-ci-hashes.txt" "coverage tooling installs only binary packages from the pinned lock" assert_file_contains "$workflow_file" 'ref: ${{ steps.trusted_source.outputs.ref }}' "OpenCode review checks out central trusted scripts for same-head validation" assert_file_contains "$workflow_file" 'COVERAGE_EVIDENCE_RESULT: ${{ needs.coverage-evidence.result || '\''skipped'\'' }}' "opencode approval receives the coverage-evidence job conclusion" assert_file_contains "$workflow_file" 'PR_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.inputs.pr_base_sha }}' "coverage evidence receives the PR base SHA for changed-file scoped measurement" @@ -882,7 +883,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'GH_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN || steps.review_read_app_token.outputs.token || github.token }}' "opencode manual dispatch uses the cross-repo approval token for target PR evidence lookups with app-token fallback" assert_file_contains "$workflow_file" 'repos/${GH_REPOSITORY}' "opencode review workflow uses env-backed repository context in shell commands" assert_file_contains "$workflow_file" "Run OpenCode PR Review model pool" "opencode review starts the central model pool" - assert_file_contains "$workflow_file" "github-models/deepseek/deepseek-v3-0324 github-models/openai/o4-mini github-models/openai/o3-mini" "opencode review starts with observed high-success DeepSeek V3 before faster reasoning-capable GitHub Models" + assert_file_contains "$workflow_file" "github-models/deepseek/deepseek-v3-0324 openai/gpt-5-mini openai/gpt-5 github-models/openai/o4-mini" "opencode review starts with observed high-success DeepSeek V3 before native OpenAI and compact reasoning fallbacks" assert_file_contains "$workflow_file" "github-models/deepseek/deepseek-r1-0528" "opencode review keeps a reachable DeepSeek R1 reasoning fallback model" assert_file_contains "$workflow_file" "github-models/deepseek/deepseek-v3-0324" "opencode review has a reachable DeepSeek V3 fallback model" assert_file_contains "$workflow_file" "github-models/openai/gpt-5" "opencode review still has a bounded GPT-5 fallback model" From 16e6617db06df77a729b80bfc7a304748ecc6578 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 10 Jul 2026 22:41:34 +0900 Subject: [PATCH 2/3] fix: isolate required workflow concurrency by head --- .github/workflows/close-empty-pr.yml | 4 +++- .github/workflows/opencode-review.yml | 3 ++- .github/workflows/pr-review-merge-scheduler.yml | 2 +- .github/workflows/security-scan.yml | 4 +++- scripts/ci/test_strix_quick_gate.sh | 9 +++++---- tests/test_opencode_agent_contract.py | 5 +++-- tests/test_required_workflow_queue_contract.py | 7 +++++-- 7 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/close-empty-pr.yml b/.github/workflows/close-empty-pr.yml index 956110517..16b524dc1 100644 --- a/.github/workflows/close-empty-pr.yml +++ b/.github/workflows/close-empty-pr.yml @@ -13,7 +13,9 @@ on: types: [opened, synchronize, reopened, ready_for_review, closed] concurrency: - group: close-empty-pr-${{ github.event.pull_request.base.repo.full_name || github.repository }}-${{ github.event.pull_request.number }} + group: >- + close-empty-pr-${{ github.event.pull_request.base.repo.full_name || github.repository }}-${{ + github.event.pull_request.number }}-${{ github.event.pull_request.head.sha || github.run_id }} cancel-in-progress: true permissions: diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 8dd1879c9..d97260fe2 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -36,7 +36,8 @@ concurrency: group: >- opencode-review-${{ github.event.pull_request.base.repo.full_name || github.event.inputs.target_repository || github.repository }}-${{ - github.event_name == 'pull_request_target' && format('pr-{0}', github.event.pull_request.number) || + github.event_name == 'pull_request_target' && format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha) || + github.event.inputs.pr_number && github.event.inputs.pr_head_sha && format('pr-{0}-{1}', github.event.inputs.pr_number, github.event.inputs.pr_head_sha) || github.event.inputs.pr_number && format('pr-{0}', github.event.inputs.pr_number) || github.event.inputs.pr_number || github.run_id }} cancel-in-progress: true diff --git a/.github/workflows/pr-review-merge-scheduler.yml b/.github/workflows/pr-review-merge-scheduler.yml index 99be104fe..15491fa16 100644 --- a/.github/workflows/pr-review-merge-scheduler.yml +++ b/.github/workflows/pr-review-merge-scheduler.yml @@ -118,7 +118,7 @@ on: concurrency: group: >- central-pr-review-merge-scheduler-${{ github.repository }}-${{ - github.event_name == 'pull_request_target' && format('pr-{0}', github.event.pull_request.number) || + github.event_name == 'pull_request_target' && format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha) || github.event_name == 'workflow_run' && github.event.workflow_run.pull_requests[0].number && format('pr-{0}', github.event.workflow_run.pull_requests[0].number) || github.event_name == 'workflow_call' && inputs.pr_number != '' && format('pr-{0}', inputs.pr_number) || github.event_name == 'workflow_call' && inputs.base_branch != '' && format('call-{0}', inputs.base_branch) || diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 803c3901f..b98450431 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -33,7 +33,9 @@ on: branches: [main, master, develop] concurrency: - group: security-scan-${{ github.event.pull_request.base.repo.full_name || github.repository }}-${{ github.event.pull_request.number }} + group: >- + security-scan-${{ github.event.pull_request.base.repo.full_name || github.repository }}-${{ + github.event.pull_request.number }}-${{ github.event.pull_request.head.sha || github.run_id }} cancel-in-progress: true # Scorecard Token-Permissions (alert #42): workflow-level token stays diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 5ad49d275..86932bcdb 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -386,8 +386,9 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { record_failure "opencode required workflow bootstrap must not depend on required-workflow event payload fields" fi assert_file_contains "$workflow_file" 'github.event.pull_request.base.repo.full_name || github.event.inputs.target_repository || github.repository' "opencode review scopes concurrency by target repository" - assert_file_contains "$workflow_file" "format('pr-{0}', github.event.pull_request.number)" "opencode review scopes pull_request_target concurrency to the active PR so newer heads cancel stale review runs" - assert_file_contains "$workflow_file" "format('pr-{0}', github.event.inputs.pr_number)" "opencode review scopes manual concurrency to the requested PR so newer heads cancel stale review runs" + assert_file_contains "$workflow_file" "format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha)" "opencode review scopes pull_request_target concurrency by current head" + assert_file_contains "$workflow_file" "format('pr-{0}-{1}', github.event.inputs.pr_number, github.event.inputs.pr_head_sha)" "opencode review scopes manual concurrency by target PR head" + assert_file_contains "$workflow_file" "github.event.inputs.pr_number && format('pr-{0}', github.event.inputs.pr_number)" "opencode review retains a manual PR fallback group when no head SHA is provided" assert_file_contains "$workflow_file" 'cancel-in-progress: true' "opencode review cancels stale in-progress review attempts when a newer PR event arrives" assert_file_contains "$workflow_file" "Materialize pull request merge tree for coverage measurement" "opencode pull_request_target coverage execution materializes the trusted base/head merge tree" assert_file_contains "$workflow_file" "stale OpenCode run: event head=" "opencode review side effects are skipped for stale heads" @@ -1122,7 +1123,7 @@ assert_pr_review_merge_scheduler_uses_github_actions_bot_token() { assert_file_contains "$workflow_file" 'workflows: ["Required OpenCode Review", "Strix Security Scan"]' "scheduler reruns after review or security evidence completion so approvals can trigger merge/update actions" assert_file_contains "$workflow_file" 'cron: "*/30 * * * *"' "scheduler wakes frequently enough to clear auto-merge PRs that become stale after their initial PR events" assert_file_not_contains "$workflow_file" "github.event.pull_request.number == 240" "scheduler must not hard-code repository-specific PR bypasses" - assert_file_contains "$workflow_file" "github.event_name == 'pull_request_target' && format('pr-{0}', github.event.pull_request.number)" "scheduler scopes pull_request_target concurrency to the active PR" + assert_file_contains "$workflow_file" "github.event_name == 'pull_request_target' && format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha)" "scheduler scopes pull_request_target concurrency to the active PR head" assert_file_contains "$workflow_file" "github.event_name == 'workflow_run' && github.event.workflow_run.pull_requests[0].number && format('pr-{0}', github.event.workflow_run.pull_requests[0].number)" "scheduler scopes workflow_run concurrency to the completed review PR" assert_file_contains "$workflow_file" "github.event_name == 'workflow_dispatch' && github.run_id" "scheduler keeps manual queue scans isolated per run" assert_file_contains "$workflow_file" "cancel-in-progress: \${{ github.event_name == 'pull_request_target' || github.event_name == 'workflow_dispatch' }}" "scheduler cancels stale PR/manual queue scans instead of accumulating merge/update attempts" @@ -1144,7 +1145,7 @@ assert_pr_review_merge_scheduler_uses_github_actions_bot_token() { assert_file_contains "$workflow_file" 'ref: ${{ steps.trusted_source.outputs.ref }}' "scheduler checks out the resolved central ref" assert_file_contains "$workflow_file" "contents: write" "scheduler has write permission for GitHub Actions bot branch updates" assert_file_contains "$workflow_file" "pull-requests: write" "scheduler has pull-request write permission for update-branch and auto-merge" - assert_file_contains "$workflow_file" "format('pr-{0}', github.event.pull_request.number)" "scheduler scopes required-workflow concurrency to the active pull request" + assert_file_contains "$workflow_file" "format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha)" "scheduler scopes required-workflow concurrency to the active pull request head" assert_file_contains "$scheduler_file" "update-branch" "scheduler calls the GitHub update-branch API for outdated approved PRs" assert_file_contains "$scheduler_file" "expected_head_sha={head}" "scheduler guards branch updates with the current PR head SHA" assert_file_contains "$scheduler_file" "shell=False" "scheduler subprocess wrapper forbids shell command execution" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 9de7b46f7..77967779c 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -353,8 +353,9 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): assert "opencode_review_model_pool" in workflow assert "run_opencode_review_model_pool.sh" in workflow assert "rekick_model_pool_on_exhaustion" in workflow - assert "format('pr-{0}', github.event.pull_request.number)" in workflow - assert "format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha)" not in workflow + assert "format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha)" in workflow + assert "format('pr-{0}-{1}', github.event.inputs.pr_number, github.event.inputs.pr_head_sha)" in workflow + assert "github.event.inputs.pr_number && format('pr-{0}', github.event.inputs.pr_number)" in workflow assert "OPENCODE_MODEL_CANDIDATES" in workflow model_pool_runner = Path("scripts/ci/run_opencode_review_model_pool.sh").read_text(encoding="utf-8") assert "assert_reasoning_effort_for_candidate" in model_pool_runner diff --git a/tests/test_required_workflow_queue_contract.py b/tests/test_required_workflow_queue_contract.py index 74dd1062b..0246ce1de 100644 --- a/tests/test_required_workflow_queue_contract.py +++ b/tests/test_required_workflow_queue_contract.py @@ -39,8 +39,11 @@ def test_required_pull_request_workflows_cancel_superseded_runs() -> None: assert "github.repository" in concurrency_contract assert "github.event.pull_request.number" in workflow assert "cancel-in-progress: true" in workflow - assert "github.event.pull_request.head.sha" not in concurrency_contract - assert "format('pr-{0}-{1}'" not in concurrency_contract + if filename in {"close-empty-pr.yml", "opencode-review.yml", "security-scan.yml"}: + assert "github.event.pull_request.head.sha" in concurrency_contract + else: + assert "github.event.pull_request.head.sha" not in concurrency_contract + assert "format('pr-{0}-{1}'" not in concurrency_contract def test_strix_keeps_current_head_security_evidence_logs() -> None: From 0834f7850a2cbf7d9348402c5e1f1b810feac597 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 10 Jul 2026 23:02:56 +0900 Subject: [PATCH 3/3] fix: bound OpenCode evidence preparation --- .github/workflows/opencode-review.yml | 2 +- scripts/ci/test_strix_quick_gate.sh | 15 ++++++++------- tests/test_opencode_agent_contract.py | 1 + 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index d97260fe2..f8767c7e1 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -1539,7 +1539,7 @@ jobs: npx -y "$CODEGRAPH_PACKAGE" status - name: Prepare bounded OpenCode review evidence - timeout-minutes: 40 + timeout-minutes: 12 env: GH_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN || steps.review_read_app_token.outputs.token || github.token }} GH_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.inputs.target_repository || github.repository }} diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 86932bcdb..e6221bf93 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -534,11 +534,12 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "not a generic model-exhaustion message" "opencode review tells models to return concrete missing-evidence findings instead of progress-only output" assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "tokens_limit_reached" "opencode review detects provider context-window overflow" assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "skipping remaining attempts for this model" "opencode review skips same-model retries after context-window overflow" - assert_file_contains "$workflow_file" 'timeout-minutes: 120' "opencode review target keeps a bounded runner budget so stalled reviews release queue capacity" - assert_file_contains "$workflow_file" 'timeout-minutes: 90' "opencode model pool leaves approval-gate headroom while capping stalled model attempts" + assert_file_contains "$workflow_file" 'timeout-minutes: 75' "opencode review target keeps a bounded runner budget so stalled reviews release queue capacity" + assert_file_contains "$workflow_file" 'timeout-minutes: 12' "opencode evidence preparation fails closed before it ties up the review queue" + assert_file_contains "$workflow_file" 'timeout-minutes: 25' "opencode model pool leaves approval-gate headroom while capping stalled model attempts" 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: "900"' "opencode primary review has a bounded per-model timeout before trying fallback models" - assert_file_contains "$workflow_file" 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "5400"' "opencode model pool exits before the job timeout so the approval gate can publish a reason" + assert_file_contains "$workflow_file" 'OPENCODE_RUN_TIMEOUT_SECONDS: "300"' "opencode primary review has a bounded per-model timeout before trying fallback models" + assert_file_contains "$workflow_file" 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "1200"' "opencode model pool exits before the job timeout so the approval gate can publish a reason" assert_file_contains "$workflow_file" 'OPENCODE_POOL_MAX_CYCLES: "1"' "opencode model pool stops after one full candidate pass instead of looping to the job timeout" 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" @@ -608,8 +609,8 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'load_selected_review_output()' "opencode approval step has a direct selected-output fallback when the overview comment is stale or invalid" assert_file_contains "$workflow_file" "gate result from Review Overview comment" "opencode approval step distinguishes overview-comment gate results" assert_file_contains "$workflow_file" "gate result from selected OpenCode output" "opencode approval step can recover from an invalid overview by validating the selected successful output" - assert_file_contains "$workflow_file" 'timeout-minutes: 75' "opencode approval step has a bounded wall-clock timeout" - assert_file_contains "$workflow_file" 'APPROVAL_CHECK_WAIT_ATTEMPTS: "81"' "opencode approval waits for bounded long-running peer checks before approving" + assert_file_contains "$workflow_file" 'timeout-minutes: 35' "opencode approval step has a bounded wall-clock timeout" + assert_file_contains "$workflow_file" 'APPROVAL_CHECK_WAIT_ATTEMPTS: "21"' "opencode approval waits for bounded long-running peer checks before approving" assert_file_contains "$workflow_file" 'CHECK_LOOKUP_RETRY_ATTEMPTS: "5"' "opencode approval retries transient GitHub check lookup failures before changing review state" assert_file_contains "$workflow_file" 'GitHub Checks lookup failed; retrying' "opencode approval logs transient check lookup retries" assert_file_contains "$workflow_file" 'collect_github_checks_with_retry collect_pending_github_checks "$output_file"' "opencode approval retry-wraps pending check lookup" @@ -650,7 +651,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_not_contains "$workflow_file" "no model produced a valid review control block" "opencode model-failure path no longer documents a final exhausted state" 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_RUN_TIMEOUT_SECONDS: "900"' "opencode catalog fallback has a bounded per-model review timeout before step timeout" + assert_file_contains "$workflow_file" 'OPENCODE_RUN_TIMEOUT_SECONDS: "300"' "opencode catalog fallback has a bounded per-model review timeout 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" assert_file_contains "$workflow_file" "github-models/deepseek/deepseek-v3-0324 openai/gpt-5-mini openai/gpt-5 github-models/openai/o4-mini" "opencode review tries the observed high-success DeepSeek V3 path before native OpenAI and compact OpenAI reasoning fallbacks" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 77967779c..14c25da1d 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -404,6 +404,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): assert '"## Check outcome"' not in workflow assert "publish REQUEST_CHANGES when coverage-evidence blocker states" in workflow assert re.search(r"opencode-review-target:[\s\S]*?timeout-minutes: 75", workflow) + assert re.search(r"Prepare bounded OpenCode review evidence[\s\S]{0,120}timeout-minutes: 12", workflow) assert 'timeout-minutes: 35' in workflow assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,240}timeout-minutes: 25", workflow) assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,280}continue-on-error: true", workflow)