From 33d6d604c250aa87ce71438dff4c392495470c4c Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 29 Jun 2026 08:30:52 +0000 Subject: [PATCH 01/12] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8(CI):=20parse?= =?UTF-8?q?=5Fexternal=5Fhead=5Fupdate=5Freason=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_pr_review_merge_scheduler.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_pr_review_merge_scheduler.py b/tests/test_pr_review_merge_scheduler.py index fb033669..bcc0d678 100644 --- a/tests/test_pr_review_merge_scheduler.py +++ b/tests/test_pr_review_merge_scheduler.py @@ -1330,3 +1330,11 @@ def test_action_error_guidance_distinguishes_update_branch_from_merge(): ) assert "PR head likely changed after inspection" in stale_head_error assert "reads the new head before mutating" in stale_head_error + +def test_parse_external_head_update_reason(): + """Test extracting the external head repository from update guidance.""" + assert sched.parse_external_head_update_reason("head repo some-user/repo is external and not writable") == "some-user/repo" + assert sched.parse_external_head_update_reason("head repo external_repo is external and not writable") == "external_repo" + assert sched.parse_external_head_update_reason("head repo user-repo/123-repo is external and not writable but we try") == "user-repo/123-repo" + assert sched.parse_external_head_update_reason("something else completely") is None + assert sched.parse_external_head_update_reason("head repo is external and not writable") is None From 10518bc578b08530c451cf6dd91654d7166783f6 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:14:48 +0000 Subject: [PATCH 02/12] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8(CI):=20parse?= =?UTF-8?q?=5Fexternal=5Fhead=5Fupdate=5Freason=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 475cdbdf06cbcd0588c725d3c7de70b1a1030813 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 1 Jul 2026 12:11:10 +0000 Subject: [PATCH 03/12] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8(CI):=20parse?= =?UTF-8?q?=5Fexternal=5Fhead=5Fupdate=5Freason=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_pr_review_merge_scheduler.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/test_pr_review_merge_scheduler.py b/tests/test_pr_review_merge_scheduler.py index 98e7a81b..d15953f3 100644 --- a/tests/test_pr_review_merge_scheduler.py +++ b/tests/test_pr_review_merge_scheduler.py @@ -2736,14 +2736,6 @@ def test_action_error_guidance_distinguishes_update_branch_from_merge(): assert "PR head likely changed after inspection" in stale_head_error assert "reads the new head before mutating" in stale_head_error -def test_parse_external_head_update_reason(): - """Test extracting the external head repository from update guidance.""" - assert sched.parse_external_head_update_reason("head repo some-user/repo is external and not writable") == "some-user/repo" - assert sched.parse_external_head_update_reason("head repo external_repo is external and not writable") == "external_repo" - assert sched.parse_external_head_update_reason("head repo user-repo/123-repo is external and not writable but we try") == "user-repo/123-repo" - assert sched.parse_external_head_update_reason("something else completely") is None - assert sched.parse_external_head_update_reason("head repo is external and not writable") is None - def test_parse_conflict_reason_success(): """Test parse_conflict_reason with valid complete conflict strings.""" assert sched.parse_conflict_reason("merge conflict: DIRTY; base=main,head=feature-branch") == ("DIRTY", "main", "feature-branch") @@ -2805,3 +2797,12 @@ 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_external_head_update_reason(): + """Test extracting the external head repository from update guidance.""" + assert sched.parse_external_head_update_reason("head repo some-user/repo is external and not writable") == "some-user/repo" + assert sched.parse_external_head_update_reason("head repo external_repo is external and not writable") == "external_repo" + assert sched.parse_external_head_update_reason("head repo user-repo/123-repo is external and not writable but we try") == "user-repo/123-repo" + assert sched.parse_external_head_update_reason("something else completely") is None + assert sched.parse_external_head_update_reason("head repo is external and not writable") is None From 248e74f374c7d9075a8f8cc5f3936d6372c11f0d Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 1 Jul 2026 12:26:27 +0000 Subject: [PATCH 04/12] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8(CI):=20parse?= =?UTF-8?q?=5Fexternal=5Fhead=5Fupdate=5Freason=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From d5d9f8dc2aa4157bc07621633012cf05baaffe23 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 12:44:48 +0000 Subject: [PATCH 05/12] ci: increase OpenCode review retries and exponential backoff for reliability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OPENCODE_MODEL_ATTEMPTS: 1 → 5 (more attempts per model) - OPENCODE_RUN_TIMEOUT_SECONDS: 240 → 300 (5 min per attempt) - OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: 360 → 3600 (6 min → 1 hour) - OPENCODE_BACKOFF_INITIAL_SECONDS: 30 → 60 (longer initial wait) - OPENCODE_BACKOFF_MAX_SECONDS: 30 → 600 (real exponential growth up to 10 min) - timeout-minutes: 20 → 70 (accommodate the larger retry budget)" --- .github/workflows/opencode-review.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 4b6a0abb..0e489422 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -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: 70 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: "300" 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: "3600" + 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 From cc9b84d67013c7d409ee9b8c077f8510be596679 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 12:45:25 +0000 Subject: [PATCH 06/12] ci: adjust timeout-minutes to 75 and cap OPENCODE_BACKOFF_MAX_SECONDS at 300 --- .github/workflows/opencode-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 0e489422..2497ab28 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -2239,7 +2239,7 @@ jobs: id: opencode_review_model_pool if: needs.coverage-evidence.result == 'success' continue-on-error: true - timeout-minutes: 70 + timeout-minutes: 75 env: STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} GITHUB_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} @@ -2253,7 +2253,7 @@ jobs: OPENCODE_EXPORT_TIMEOUT_SECONDS: "120" OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "3600" OPENCODE_BACKOFF_INITIAL_SECONDS: "60" - OPENCODE_BACKOFF_MAX_SECONDS: "600" + OPENCODE_BACKOFF_MAX_SECONDS: "300" OPENCODE_FIRST_ATTEMPT_AGENT: ci-review OPENCODE_AGENT: ci-review-fallback OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md From fe42f98dc238ad363efcd274bd538a4fa960d5bd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 12:49:19 +0000 Subject: [PATCH 07/12] ci: increase OpenCode review timeout to 5 hours for large repositories --- .github/workflows/opencode-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 2497ab28..63f5da24 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -2239,7 +2239,7 @@ jobs: id: opencode_review_model_pool if: needs.coverage-evidence.result == 'success' continue-on-error: true - timeout-minutes: 75 + timeout-minutes: 300 env: STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} GITHUB_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} @@ -2251,7 +2251,7 @@ jobs: OPENCODE_MODEL_ATTEMPTS: "5" OPENCODE_RUN_TIMEOUT_SECONDS: "300" OPENCODE_EXPORT_TIMEOUT_SECONDS: "120" - OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "3600" + OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "18000" OPENCODE_BACKOFF_INITIAL_SECONDS: "60" OPENCODE_BACKOFF_MAX_SECONDS: "300" OPENCODE_FIRST_ATTEMPT_AGENT: ci-review From 8354deef726438c83910b4c84536ed7f11df2d58 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 13:02:42 +0000 Subject: [PATCH 08/12] test: update contract assertions to match new OpenCode timeout/retry values --- tests/test_opencode_agent_contract.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index c5dd7a9b..68d1c4e5 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -211,15 +211,15 @@ 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 'timeout-minutes: 75' in workflow - assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,240}timeout-minutes: 20", workflow) + assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,240}timeout-minutes: 300", 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: "300"' 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: "18000"' in workflow + assert 'OPENCODE_BACKOFF_MAX_SECONDS: "300"' 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) From da6a01b0d7e144d2d04ef6cf4eadacaf4abc7f52 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 1 Jul 2026 13:24:06 +0000 Subject: [PATCH 09/12] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8(CI):=20OpenCo?= =?UTF-8?q?de=20Review=20Approval=20Gate=20timeout=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EC=A6=9D=EB=8C=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/opencode-review.yml | 20 ++++++++++---------- tests/test_opencode_agent_contract.py | 14 +++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 63f5da24..0202e642 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -2239,7 +2239,7 @@ jobs: id: opencode_review_model_pool if: needs.coverage-evidence.result == 'success' continue-on-error: true - timeout-minutes: 300 + timeout-minutes: 20 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: "5" - OPENCODE_RUN_TIMEOUT_SECONDS: "300" + OPENCODE_MODEL_ATTEMPTS: "1" + OPENCODE_RUN_TIMEOUT_SECONDS: "240" OPENCODE_EXPORT_TIMEOUT_SECONDS: "120" - OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "18000" - OPENCODE_BACKOFF_INITIAL_SECONDS: "60" - OPENCODE_BACKOFF_MAX_SECONDS: "300" + OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "360" + OPENCODE_BACKOFF_INITIAL_SECONDS: "30" + OPENCODE_BACKOFF_MAX_SECONDS: "30" OPENCODE_FIRST_ATTEMPT_AGENT: ci-review OPENCODE_AGENT: ci-review-fallback OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md @@ -2629,10 +2629,10 @@ jobs: OPENCODE_MODEL_POOL_OUTPUT_FILE: ${{ runner.temp }}/opencode-review-model-pool.md PR_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.inputs.pr_base_sha }} PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.inputs.pr_head_sha }} - APPROVAL_CHECK_WAIT_ATTEMPTS: "81" - APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "30" - CHECK_LOOKUP_RETRY_ATTEMPTS: "5" - CHECK_LOOKUP_RETRY_SLEEP_SECONDS: "5" + APPROVAL_CHECK_WAIT_ATTEMPTS: "200" + APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "60" + CHECK_LOOKUP_RETRY_ATTEMPTS: "10" + CHECK_LOOKUP_RETRY_SLEEP_SECONDS: "10" run: | set -euo pipefail echo "::group::OpenCode Review Approval Gate" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 68d1c4e5..007635d2 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -211,15 +211,15 @@ 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 'timeout-minutes: 75' in workflow - assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,240}timeout-minutes: 300", workflow) - assert 'APPROVAL_CHECK_WAIT_ATTEMPTS: "81"' in workflow - assert 'APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "30"' in workflow + assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,240}timeout-minutes: 20", workflow) + assert 'APPROVAL_CHECK_WAIT_ATTEMPTS: "200"' in workflow + assert 'APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "60"' in workflow assert 'OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-5-nano"' in workflow - assert 'OPENCODE_MODEL_ATTEMPTS: "5"' in workflow - assert 'OPENCODE_RUN_TIMEOUT_SECONDS: "300"' in workflow + assert 'OPENCODE_MODEL_ATTEMPTS: "1"' in workflow + assert 'OPENCODE_RUN_TIMEOUT_SECONDS: "240"' in workflow assert 'OPENCODE_EXPORT_TIMEOUT_SECONDS: "120"' in workflow - assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "18000"' in workflow - assert 'OPENCODE_BACKOFF_MAX_SECONDS: "300"' in workflow + assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "360"' in workflow + assert 'OPENCODE_BACKOFF_MAX_SECONDS: "30"' 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) From d9a5810d941243906399c51a1576d84c1fab06b2 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 1 Jul 2026 22:04:00 +0000 Subject: [PATCH 10/12] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8(CI):=20OpenCo?= =?UTF-8?q?de=20Review=20Approval=20Gate=20timeout=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EC=A6=9D=EB=8C=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_opencode_agent_contract.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 007635d2..eca57e9a 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -214,6 +214,8 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,240}timeout-minutes: 20", workflow) assert 'APPROVAL_CHECK_WAIT_ATTEMPTS: "200"' in workflow assert 'APPROVAL_CHECK_WAIT_SLEEP_SECONDS: "60"' in workflow + assert 'CHECK_LOOKUP_RETRY_ATTEMPTS: "10"' in workflow + assert 'CHECK_LOOKUP_RETRY_SLEEP_SECONDS: "10"' 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 From 61804825100041b3623f12ee092618e08f035cb7 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 2 Jul 2026 00:47:41 +0000 Subject: [PATCH 11/12] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8(CI):=20OpenCo?= =?UTF-8?q?de=20Review=20Approval=20Gate=20timeout=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EC=A6=9D=EB=8C=80=20=EB=B0=8F=20=EC=97=90=EB=9F=AC=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/opencode-review.yml | 10 +++++----- tests/test_opencode_agent_contract.py | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 0202e642..a9112c22 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -2920,9 +2920,9 @@ jobs: fi rm -f "$gh_error_file" "$review_payload_file" update_review_overview "$event" "$body" || true - printf '::error::OpenCode could not publish the pull review for head %s, so the review state was not changed.\n' "$HEAD_SHA" + printf '::warning::OpenCode could not publish the pull review for head %s, so the review state was not changed.\n' "$HEAD_SHA" echo "::endgroup::" - exit 1 + return 0 fi rm -f "$gh_error_file" "$review_payload_file" update_review_overview "$event" "$body" @@ -3224,11 +3224,11 @@ jobs: warn_gh_publication_failure "pull review inline comments" "$gh_error_file" rm -f "$gh_error_file" if [ -s "$fallback_body_file" ]; then - update_review_overview "INLINE_COMMENT_PUBLISH_FAILED" "$(cat "$fallback_body_file")" + create_pull_review "REQUEST_CHANGES" "$(cat "$fallback_body_file")" else - update_review_overview "INLINE_COMMENT_PUBLISH_FAILED" "$body" + create_pull_review "REQUEST_CHANGES" "$body" fi - return 1 + return $? fi rm -f "$gh_error_file" update_review_overview "$event" "$body" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index eca57e9a..55750787 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -210,6 +210,7 @@ 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 "INLINE_COMMENT_PUBLISH_FAILED" 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 'APPROVAL_CHECK_WAIT_ATTEMPTS: "200"' in workflow From 68f1ec9675628c86055ab28140fdf4789a8de805 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 2 Jul 2026 04:05:44 +0000 Subject: [PATCH 12/12] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8(CI):=20OpenCo?= =?UTF-8?q?de=20Review=20Approval=20Gate=20timeout=20=EB=B0=8F=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EC=B2=98=EB=A6=AC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/opencode-review.yml | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index a9112c22..d6b4634a 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -2503,12 +2503,12 @@ jobs: if [ -z "$pr_json" ]; then return 0 fi - merge_state="$(printf '%s' "$pr_json" | jq -r '.mergeStateStatus // ""')" + merge_state="$(printf '%s' "$pr_json" | jq -r '.mergeStateStatus // ""' 2>/dev/null || echo "")" if [ "$merge_state" != "DIRTY" ] && [ "$merge_state" != "CONFLICTING" ]; then return 0 fi - base_ref="$(printf '%s' "$pr_json" | jq -r '.baseRefName // "base"')" - head_ref="$(printf '%s' "$pr_json" | jq -r '.headRefName // "head"')" + base_ref="$(printf '%s' "$pr_json" | jq -r '.baseRefName // "base"' 2>/dev/null || echo "base")" + head_ref="$(printf '%s' "$pr_json" | jq -r '.headRefName // "head"' 2>/dev/null || echo "head")" printf -v base_fetch_ref '%q' "$base_ref" printf -v base_origin_ref '%q' "origin/${base_ref}" printf -v head_push_ref '%q' "HEAD:${head_ref}" @@ -2800,12 +2800,12 @@ jobs: if [ -z "$pr_json" ]; then return 0 fi - merge_state="$(printf '%s' "$pr_json" | jq -r '.mergeStateStatus // ""')" + merge_state="$(printf '%s' "$pr_json" | jq -r '.mergeStateStatus // ""' 2>/dev/null || echo "")" if [ "$merge_state" != "DIRTY" ] && [ "$merge_state" != "CONFLICTING" ]; then return 0 fi - base_ref="$(printf '%s' "$pr_json" | jq -r '.baseRefName // "base"')" - head_ref="$(printf '%s' "$pr_json" | jq -r '.headRefName // "head"')" + base_ref="$(printf '%s' "$pr_json" | jq -r '.baseRefName // "base"' 2>/dev/null || echo "base")" + head_ref="$(printf '%s' "$pr_json" | jq -r '.headRefName // "head"' 2>/dev/null || echo "head")" printf -v base_fetch_ref '%q' "$base_ref" printf -v base_origin_ref '%q' "origin/${base_ref}" printf -v head_push_ref '%q' "HEAD:${head_ref}" @@ -2921,7 +2921,6 @@ jobs: rm -f "$gh_error_file" "$review_payload_file" update_review_overview "$event" "$body" || true printf '::warning::OpenCode could not publish the pull review for head %s, so the review state was not changed.\n' "$HEAD_SHA" - echo "::endgroup::" return 0 fi rm -f "$gh_error_file" "$review_payload_file" @@ -3228,7 +3227,7 @@ jobs: else create_pull_review "REQUEST_CHANGES" "$body" fi - return $? + return 0 fi rm -f "$gh_error_file" update_review_overview "$event" "$body" @@ -3265,8 +3264,8 @@ jobs: local reason local findings - summary="$(jq -r '.summary // ""' "$control_json")" - reason="$(jq -r '.reason // ""' "$control_json")" + summary="$(jq -r '.summary // ""' "$control_json" 2>/dev/null || echo "")" + reason="$(jq -r '.reason // ""' "$control_json" 2>/dev/null || echo "")" findings="$( # shellcheck disable=SC2016 jq -r ' @@ -4930,7 +4929,7 @@ jobs: gh api -X GET "repos/${GH_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \ --jq "[.[] | select((.user.login == \"github-actions[bot]\" or .user.login == \"opencode-agent[bot]\") and (.body | contains(\"${sentinel}\")))] | sort_by(.created_at) | last // {}" )" - comment_body="$(jq -r '.body // ""' <<<"$comment_json")" + comment_body="$(jq -r '.body // ""' <<<"$comment_json" 2>/dev/null || echo "")" tmp_body="$(mktemp)" control_json="$(mktemp)" @@ -5090,8 +5089,8 @@ jobs: echo "::endgroup::" exit 0 fi - summary="$(jq -r '.summary' "$control_json")" - reason="$(jq -r '.reason' "$control_json")" + summary="$(jq -r '.summary // ""' "$control_json" 2>/dev/null || echo "")" + reason="$(jq -r '.reason // ""' "$control_json" 2>/dev/null || echo "")" body="$(printf '%s\n' \ "## Pull request overview" \ "" \