Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/opencode-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: 300
env:
STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }}
GITHUB_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }}
Expand All @@ -2249,9 +2249,9 @@ jobs:
NO_COLOR: "1"
OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-5-nano"
OPENCODE_MODEL_ATTEMPTS: "1"
OPENCODE_RUN_TIMEOUT_SECONDS: "240"
OPENCODE_RUN_TIMEOUT_SECONDS: "18000"
OPENCODE_EXPORT_TIMEOUT_SECONDS: "120"
OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "360"
OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "18000"
OPENCODE_BACKOFF_INITIAL_SECONDS: "30"
OPENCODE_BACKOFF_MAX_SECONDS: "30"
OPENCODE_FIRST_ATTEMPT_AGENT: ci-review
Expand Down Expand Up @@ -2602,7 +2602,7 @@ jobs:

- name: Approve PR if OpenCode review passed
if: always()
timeout-minutes: 75
timeout-minutes: 300
env:
GH_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN || steps.opencode_app_token.outputs.token || github.token }}
CHECK_LOOKUP_GH_TOKEN: ${{ github.token }}
Expand Down Expand Up @@ -3896,7 +3896,7 @@ jobs:
} >"$prompt_file"

cd "$OPENCODE_REVIEW_WORKDIR"
if ! timeout --kill-after=30s "${OPENCODE_RUN_TIMEOUT_SECONDS:-240}s" opencode run "$(cat "$prompt_file")" \
if ! timeout --kill-after=30s "${OPENCODE_RUN_TIMEOUT_SECONDS:-18000}s" opencode run "$(cat "$prompt_file")" \
--pure \
--agent ci-review-fallback \
--model "$MODEL" \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-review-autofix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ jobs:
}
trap restore_workspace_config EXIT
cd "$TARGET_WORKSPACE"
timeout 900 opencode run "$(cat "$prompt_file")" \
timeout 18000 opencode run "$(cat "$prompt_file")" \
--pure \
--agent ci-autofix \
--model "$MODEL" \
Expand Down
8 changes: 5 additions & 3 deletions scripts/ci/pr_review_fix_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,10 @@ def change_request_is_autofixable(pr: dict[str, Any]) -> bool:
def needs_autofix(pr: dict[str, Any]) -> tuple[bool, tuple[str, ...]]:
"""Return whether current-head evidence justifies an autofix attempt."""
reasons: list[str] = []
if has_current_head_changes_requested(pr) and change_request_is_autofixable(pr):
reasons.append("current-head OpenCode requested changes")
if not (has_current_head_changes_requested(pr) and change_request_is_autofixable(pr)):
return False, ()
Comment on lines +117 to +118

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d7ea1ee — the skip message in inspect_pr() was updated to "no current-head autofixable OpenCode change request", and the matching assertion in test_fix_inspect_skip_wait_and_error_paths was updated accordingly.


reasons.append("current-head OpenCode requested changes")
unresolved = unresolved_thread_count(pr)
if unresolved:
reasons.append(f"{unresolved} active unresolved review thread(s)")
Expand Down Expand Up @@ -209,7 +211,7 @@ def inspect_pr(

needs_fix, reasons = needs_autofix(pr)
if not needs_fix:
return "skip", ("no current-head change request or active unresolved review thread",)
return "skip", ("no current-head autofixable OpenCode change request",)

if comments is None:
comments = issue_comments(repo, number)
Expand Down
4 changes: 2 additions & 2 deletions scripts/ci/run_opencode_review_model_pool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ run_one_model_attempt() {
local opencode_export_file="$8"
local run_timeout_seconds export_timeout_seconds opencode_status session_id

run_timeout_seconds="${OPENCODE_RUN_TIMEOUT_SECONDS:-180}"
run_timeout_seconds="${OPENCODE_RUN_TIMEOUT_SECONDS:-18000}"
export_timeout_seconds="${OPENCODE_EXPORT_TIMEOUT_SECONDS:-60}"

rm -f "$opencode_json_file" "$opencode_export_file" "$candidate_output_file"
Expand Down Expand Up @@ -149,7 +149,7 @@ main() {
local opencode_json_file opencode_export_file agent retry_sleep original_run_timeout run_status

attempts="${OPENCODE_MODEL_ATTEMPTS:-3}"
original_run_timeout="${OPENCODE_RUN_TIMEOUT_SECONDS:-900}"
original_run_timeout="${OPENCODE_RUN_TIMEOUT_SECONDS:-18000}"
deadline=$((SECONDS + ${OPENCODE_TOTAL_RETRY_BUDGET_SECONDS:-18000}))
: >"$OPENCODE_OUTPUT_FILE"
cd "$OPENCODE_REVIEW_WORKDIR"
Expand Down
8 changes: 4 additions & 4 deletions scripts/ci/test_strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,8 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() {
assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "Read and follow the complete review contract" "opencode review uses a compact launcher while keeping the full review contract on disk"
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" 'OPENCODE_RUN_TIMEOUT_SECONDS: "600"' "opencode primary review has a bounded per-model timeout before trying fallback models"
assert_file_contains "$workflow_file" 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "3600"' "opencode model pool has a one-hour total retry budget"
assert_file_contains "$workflow_file" 'OPENCODE_RUN_TIMEOUT_SECONDS: "18000"' "opencode primary review has a bounded per-model timeout before trying fallback models"
assert_file_contains "$workflow_file" 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "18000"' "opencode model pool has a five-hour total retry budget"
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"
assert_file_contains "$workflow_file" "always()" "opencode fallback chain uses always() so failed model steps cannot skip every fallback"
Expand Down Expand Up @@ -578,7 +578,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: 75' "opencode approval step has a bounded wall-clock timeout"
assert_file_contains "$workflow_file" 'timeout-minutes: 300' "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"
Expand Down Expand Up @@ -618,7 +618,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() {
assert_file_contains "$workflow_file" "no model produced a valid review control block" "opencode model-failure path documents why approval is withheld"
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: "600"' "opencode catalog fallback has a bounded model review timeout before step timeout"
assert_file_contains "$workflow_file" 'OPENCODE_RUN_TIMEOUT_SECONDS: "18000"' "opencode catalog fallback has a bounded 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/openai/o3 github-models/openai/o3-mini github-models/openai/o4-mini" "opencode review includes additional OpenAI reasoning model fallbacks"
Expand Down
8 changes: 4 additions & 4 deletions tests/test_opencode_agent_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,15 +210,15 @@ 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 re.search(r"opencode-review-target:[\s\S]{0,240}timeout-minutes: 360", workflow)
assert 'timeout-minutes: 300' in 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_RUN_TIMEOUT_SECONDS: "18000"' in workflow
assert 'OPENCODE_EXPORT_TIMEOUT_SECONDS: "120"' in workflow
assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "360"' in workflow
assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "18000"' 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)
Expand Down
16 changes: 14 additions & 2 deletions tests/test_pr_review_fix_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_recent_fix_marker_is_head_scoped():


def test_needs_autofix_uses_current_head_evidence():
"""Autofix only starts from current-head review or thread evidence."""
"""Autofix starts from current-head OpenCode change requests."""
head = "a" * 40
pr = make_pr(
headRefOid=head,
Expand All @@ -62,6 +62,15 @@ def test_needs_autofix_uses_current_head_evidence():
)


def test_needs_autofix_ignores_thread_only_feedback():
"""Thread-only feedback must not start an autonomous autofix run."""
pr = make_pr(
reviewThreads={"nodes": [{"id": "thread", "isResolved": False, "isOutdated": False}]},
)

assert fix.needs_autofix(pr) == (False, ())


@pytest.mark.parametrize(
("merge_state", "body"),
[
Expand Down Expand Up @@ -381,7 +390,10 @@ def test_fix_inspect_skip_wait_and_error_paths(monkeypatch):
)

monkeypatch.setattr(fix, "needs_autofix", lambda pr: (False, ()))
assert fix.inspect_pr("owner/repo", make_pr(), args) == ("skip", ("no current-head change request or active unresolved review thread",))
assert fix.inspect_pr("owner/repo", make_pr(), args) == (
"skip",
("no current-head autofixable OpenCode change request",),
)

monkeypatch.setattr(fix, "needs_autofix", lambda pr: (True, ("reason",)))
monkeypatch.setattr(fix, "issue_comments", lambda repo, number: [{"body": f"{fix.FIX_MARKER} head_sha={'a' * 40} epoch={int(time.time())} -->"}])
Expand Down
Loading