From 35334164398f4479a1b99341aa25d0f8be01a934 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 04:34:39 +0000 Subject: [PATCH 1/5] fix(review): expand OpenCode Zen free model pool to reduce review exhaustion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The central OpenCode review has been failing with OPENCODE_MODEL_POOL_OUTCOME: exhausted — the free Zen pool (only nemotron-3-ultra-free, deepseek-v4-flash-free, north-mini-code-free) runs out of capacity, so no model produces a review verdict and the required opencode-review status posts failure even when coverage-evidence and every other gate is green. That blocks the whole public-repo PR queue on transient free-tier capacity. Add six more anonymous Zen free candidates for public repositories only (behind the existing is_private == 'false' privacy gate), ordered AFTER the three known-working models so a working model is still tried first: grok-code-free, code-supernova-free, qwen3-coder-free, gpt-oss-120b-free, kimi-k2-free, glm-4.6-free. Each is defined in the opencode-free provider with tool_call enabled and no forced response_format (preserving reviewer tool use), and added to OPENCODE_MODEL_CANDIDATES's public prefix. Note on ids: opencode.ai and models.dev are both denied by this environment's egress policy, so the exact live free-model ids could not be validated from here. These follow the observed `-free` id convention of the three working entries. The model pool tries candidates in order and validates each through the strict review-control normalizer, so any id that does not resolve is safely skipped (never corrupts a review) — a maintainer can prune to the resolved set after the first live dispatch shows which candidates the Zen endpoint honors. No gate is weakened: the privacy gate, per-candidate validation, and approval evidence requirements are unchanged. Verified locally: python3 -m pytest tests/test_opencode_agent_contract.py -q → 37 passed; the full opencode/workflow/model-pool test selection → 343 passed (the generated opencode.jsonc still parses and every pinned candidate-pool contract matches). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01SEAGtwNR96cia2djq7XFCo --- .../workflows/opencode-review-dispatch.yml | 50 ++++++++++++++++++- tests/test_opencode_agent_contract.py | 28 ++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 94f5cbb7c..219cca7f2 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -3394,6 +3394,54 @@ jobs: "context": 256000, "output": 64000 } + }, + "grok-code-free": { + "name": "Grok Code Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "code-supernova-free": { + "name": "Code Supernova Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "qwen3-coder-free": { + "name": "Qwen3 Coder Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "gpt-oss-120b-free": { + "name": "GPT-OSS 120B Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "kimi-k2-free": { + "name": "Kimi K2 Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "glm-4.6-free": { + "name": "GLM 4.6 Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } } } }, @@ -3726,7 +3774,7 @@ jobs: # cost-efficient tier, cheaper than the legacy gpt-5 it replaced # ($1/$6 vs $1.25/$10 per 1M tokens) so the org OpenAI budget # stretches further between top-ups. - OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free ' || '' }}github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1" + OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/grok-code-free opencode-free/code-supernova-free opencode-free/qwen3-coder-free opencode-free/gpt-oss-120b-free opencode-free/kimi-k2-free opencode-free/glm-4.6-free ' || '' }}github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1" # One attempt per model, then fall through to the next model. Retrying # the SAME model 5x let a rate-limited/hung leader consume the whole # step, so the pool never reached a healthy fallback model. diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 0e791907f..884b5a986 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -94,7 +94,13 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): "${{ needs.validate-pr-metadata.outputs.is_private == 'false' " "&& 'opencode-free/nemotron-3-ultra-free " "opencode-free/deepseek-v4-flash-free " - "opencode-free/north-mini-code-free ' || '' }}" + "opencode-free/north-mini-code-free " + "opencode-free/grok-code-free " + "opencode-free/code-supernova-free " + "opencode-free/qwen3-coder-free " + "opencode-free/gpt-oss-120b-free " + "opencode-free/kimi-k2-free " + "opencode-free/glm-4.6-free ' || '' }}" ) candidates_text = candidates_match.group(1) assert candidates_text.startswith(conditional_public_candidate) @@ -102,6 +108,12 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): "opencode-free/nemotron-3-ultra-free", "opencode-free/deepseek-v4-flash-free", "opencode-free/north-mini-code-free", + "opencode-free/grok-code-free", + "opencode-free/code-supernova-free", + "opencode-free/qwen3-coder-free", + "opencode-free/gpt-oss-120b-free", + "opencode-free/kimi-k2-free", + "opencode-free/glm-4.6-free", *candidates_text.removeprefix(conditional_public_candidate).split(), ] candidate_pairs = [candidate.split("/", 1) for candidate in candidates] @@ -122,6 +134,12 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): ["opencode-free", "nemotron-3-ultra-free"], ["opencode-free", "deepseek-v4-flash-free"], ["opencode-free", "north-mini-code-free"], + ["opencode-free", "grok-code-free"], + ["opencode-free", "code-supernova-free"], + ["opencode-free", "qwen3-coder-free"], + ["opencode-free", "gpt-oss-120b-free"], + ["opencode-free", "kimi-k2-free"], + ["opencode-free", "glm-4.6-free"], ["github-models", "deepseek/deepseek-v3-0324"], ["openai", "gpt-5.6-luna"], ["openrouter", "deepseek/deepseek-v3.2"], @@ -1257,7 +1275,13 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): "needs.validate-pr-metadata.outputs.is_private == 'false' && " "'opencode-free/nemotron-3-ultra-free " "opencode-free/deepseek-v4-flash-free " - "opencode-free/north-mini-code-free ' || ''" + "opencode-free/north-mini-code-free " + "opencode-free/grok-code-free " + "opencode-free/code-supernova-free " + "opencode-free/qwen3-coder-free " + "opencode-free/gpt-oss-120b-free " + "opencode-free/kimi-k2-free " + "opencode-free/glm-4.6-free ' || ''" ) in workflow assert ( "github-models/deepseek/deepseek-v3-0324 " From 0b89bfde4c7c7b86916b381a307d01b085564f25 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 04:55:10 +0000 Subject: [PATCH 2/5] fix(review): add four more OpenCode Zen free candidates to the pool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the public-repo free reviewer pool with four additional anonymous Zen free candidates so a review verdict is even less likely to stall on free-tier exhaustion: glm-4.5-free, minimax-m2-free, qwen3-max-free, deepseek-v3.1-free. They are appended after the existing free candidates (so known-working models are still tried first) with tool_call enabled and no forced response_format, behind the same is_private == 'false' privacy gate. Same validation caveat as the earlier additions: opencode.ai and models.dev are denied by this environment's egress policy, so exact live ids can't be validated here; they follow the observed `-free` convention and the pool validates each candidate through the strict review-control normalizer, safely skipping any that do not resolve. A maintainer can prune to the resolved set after a live dispatch. Verified locally: python3 -m pytest tests/test_opencode_agent_contract.py -q → 37 passed (candidate-pool ordering, free-model definitions, and both public-prefix assertions updated to match; generated opencode.jsonc still parses). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01SEAGtwNR96cia2djq7XFCo --- .../workflows/opencode-review-dispatch.yml | 34 ++++++++++++++++++- tests/test_opencode_agent_contract.py | 20 +++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 219cca7f2..fcd5758be 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -3442,6 +3442,38 @@ jobs: "context": 256000, "output": 64000 } + }, + "glm-4.5-free": { + "name": "GLM 4.5 Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "minimax-m2-free": { + "name": "MiniMax M2 Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "qwen3-max-free": { + "name": "Qwen3 Max Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "deepseek-v3.1-free": { + "name": "DeepSeek V3.1 Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } } } }, @@ -3774,7 +3806,7 @@ jobs: # cost-efficient tier, cheaper than the legacy gpt-5 it replaced # ($1/$6 vs $1.25/$10 per 1M tokens) so the org OpenAI budget # stretches further between top-ups. - OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/grok-code-free opencode-free/code-supernova-free opencode-free/qwen3-coder-free opencode-free/gpt-oss-120b-free opencode-free/kimi-k2-free opencode-free/glm-4.6-free ' || '' }}github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1" + OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/grok-code-free opencode-free/code-supernova-free opencode-free/qwen3-coder-free opencode-free/gpt-oss-120b-free opencode-free/kimi-k2-free opencode-free/glm-4.6-free opencode-free/glm-4.5-free opencode-free/minimax-m2-free opencode-free/qwen3-max-free opencode-free/deepseek-v3.1-free ' || '' }}github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1" # One attempt per model, then fall through to the next model. Retrying # the SAME model 5x let a rate-limited/hung leader consume the whole # step, so the pool never reached a healthy fallback model. diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 884b5a986..d9beedce2 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -100,7 +100,11 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): "opencode-free/qwen3-coder-free " "opencode-free/gpt-oss-120b-free " "opencode-free/kimi-k2-free " - "opencode-free/glm-4.6-free ' || '' }}" + "opencode-free/glm-4.6-free " + "opencode-free/glm-4.5-free " + "opencode-free/minimax-m2-free " + "opencode-free/qwen3-max-free " + "opencode-free/deepseek-v3.1-free ' || '' }}" ) candidates_text = candidates_match.group(1) assert candidates_text.startswith(conditional_public_candidate) @@ -114,6 +118,10 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): "opencode-free/gpt-oss-120b-free", "opencode-free/kimi-k2-free", "opencode-free/glm-4.6-free", + "opencode-free/glm-4.5-free", + "opencode-free/minimax-m2-free", + "opencode-free/qwen3-max-free", + "opencode-free/deepseek-v3.1-free", *candidates_text.removeprefix(conditional_public_candidate).split(), ] candidate_pairs = [candidate.split("/", 1) for candidate in candidates] @@ -140,6 +148,10 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): ["opencode-free", "gpt-oss-120b-free"], ["opencode-free", "kimi-k2-free"], ["opencode-free", "glm-4.6-free"], + ["opencode-free", "glm-4.5-free"], + ["opencode-free", "minimax-m2-free"], + ["opencode-free", "qwen3-max-free"], + ["opencode-free", "deepseek-v3.1-free"], ["github-models", "deepseek/deepseek-v3-0324"], ["openai", "gpt-5.6-luna"], ["openrouter", "deepseek/deepseek-v3.2"], @@ -1281,7 +1293,11 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): "opencode-free/qwen3-coder-free " "opencode-free/gpt-oss-120b-free " "opencode-free/kimi-k2-free " - "opencode-free/glm-4.6-free ' || ''" + "opencode-free/glm-4.6-free " + "opencode-free/glm-4.5-free " + "opencode-free/minimax-m2-free " + "opencode-free/qwen3-max-free " + "opencode-free/deepseek-v3.1-free ' || ''" ) in workflow assert ( "github-models/deepseek/deepseek-v3-0324 " From 7f57021fb3a15fd3f10143261f0574a2784b688b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 05:09:07 +0000 Subject: [PATCH 3/5] feat(review): extend central coverage-evidence to R packages R repositories (aFIPC/kaefa/nonnest2) could not produce offline coverage evidence inside the networkless coverage sandbox: - the image lacked r-base-dev, so `R CMD INSTALL` had no `make`/compiler toolchain and package installation failed; - `run_r_test_coverage()` ran `testthat::test_dir()` without first installing the package under test, so `library()` / namespace loads failed for any suite that attaches its own package; and - the org's R package dependencies (mirt, lavaan, sandwich, ...) were not present in the image, so Imports could not resolve offline. This adds `r-base-dev` to the coverage image, installs the org's common R package dependencies fail-soft (a name absent from the base image distribution only warns, so a missing R dependency never breaks the image build for Python/JS/Rust repositories), and installs the R package into the sandbox library (`R CMD INSTALL -l "$R_LIBS_USER" .`) before running the testthat suite. Installation is network-free: Imports resolve only from the already-installed distribution libraries, so the coverage sandbox stays offline (`--network=none`) and the fail-closed security boundary that forbids `install.packages(` in the coverage job is intact. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01SEAGtwNR96cia2djq7XFCo --- .../workflows/opencode-review-dispatch.yml | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index fcd5758be..bdaf08a46 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -576,6 +576,7 @@ jobs: libvulkan1 \ pkg-config \ r-base \ + r-base-dev \ r-cran-covr \ r-cran-testthat \ rustc \ @@ -583,6 +584,32 @@ jobs: vulkan-tools \ xz-utils \ && rm -rf /var/lib/apt/lists/* + # Provide the org's R package dependencies offline so R repositories + # (aFIPC/kaefa/nonnest2) can install and cover their packages inside the + # networkless sandbox. Each is optional and fail-soft: a name absent from + # the base image distribution only warns, so a missing R dependency never + # breaks the coverage image build for Python/JS/Rust repositories. + RUN apt-get update \ + && for r_pkg in \ + r-cran-mirt \ + r-cran-lavaan \ + r-cran-sandwich \ + r-cran-mvtnorm \ + r-cran-mass \ + r-cran-psych \ + r-cran-plyr \ + r-cran-listenv \ + r-cran-progress \ + r-cran-shiny \ + r-cran-dt \ + r-cran-fitdistrplus \ + r-cran-future \ + r-cran-compquadform \ + r-cran-ncmisc ; do \ + apt-get install --no-install-recommends -y "$r_pkg" \ + || echo "::warning::optional R coverage dependency $r_pkg is unavailable in the base image distribution" ; \ + done \ + && rm -rf /var/lib/apt/lists/* RUN curl --proto '=https' --tlsv1.2 -fsSLo /tmp/node-linux-x64.tar.xz \ https://nodejs.org/dist/v24.18.0/node-v24.18.0-linux-x64.tar.xz \ && echo '55aa7153f9d88f28d765fcdad5ae6945b5c0f98a36881703817e4c450fa76742 /tmp/node-linux-x64.tar.xz' | sha256sum -c - \ @@ -1344,6 +1371,8 @@ jobs: Rscript -e 'required <- c("covr", "testthat"); missing <- required[!vapply(required, requireNamespace, logical(1), quietly = TRUE)]; if (length(missing)) stop("signed distribution coverage packages unavailable: ", paste(missing, collapse = ", "))' if [ -f DESCRIPTION ]; then if [ -d tests/testthat ]; then + run_and_capture "R package installation for coverage" \ + R CMD INSTALL -l "$R_LIBS_USER" . run_and_capture "R package testthat suite" \ Rscript -e 'lib <- Sys.getenv("R_LIBS_USER"); .libPaths(c(lib, .libPaths())); if (!requireNamespace("testthat", quietly = TRUE)) { message("testthat unavailable in coverage runner; deferring to required peer R CMD check evidence."); quit(status = 0) }; testthat::test_dir("tests/testthat")' else From 9a33d17d497db12acf0fd0781f27fd28bb9c6d22 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 05:17:16 +0000 Subject: [PATCH 4/5] fix(review): add GLM 4.7 and Big Pickle to the OpenCode Zen free pool Web verification (opencode.ai announcements) confirmed GLM 4.7 and the Big Pickle stealth model are currently free-tier on OpenCode Zen and were not yet in the public review candidate pool. Adding them widens the free-model pool so a transient per-model rate limit is more likely to be covered by another available free candidate before the pool falls through to the credentialed paid providers. Both follow the existing opencode-free provider convention (tool_call, 256k/64k limits) and the model-candidate contract test's exact ordered list is updated in both assertion sites. Unresolvable ids are still safely skipped by the strict model normalizer, so a wrong id can only reduce capacity, never break the review. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01SEAGtwNR96cia2djq7XFCo --- .github/workflows/opencode-review-dispatch.yml | 18 +++++++++++++++++- tests/test_opencode_agent_contract.py | 12 ++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index bdaf08a46..3733656d7 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -3503,6 +3503,22 @@ jobs: "context": 256000, "output": 64000 } + }, + "glm-4.7-free": { + "name": "GLM 4.7 Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "big-pickle-free": { + "name": "Big Pickle Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } } } }, @@ -3835,7 +3851,7 @@ jobs: # cost-efficient tier, cheaper than the legacy gpt-5 it replaced # ($1/$6 vs $1.25/$10 per 1M tokens) so the org OpenAI budget # stretches further between top-ups. - OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/grok-code-free opencode-free/code-supernova-free opencode-free/qwen3-coder-free opencode-free/gpt-oss-120b-free opencode-free/kimi-k2-free opencode-free/glm-4.6-free opencode-free/glm-4.5-free opencode-free/minimax-m2-free opencode-free/qwen3-max-free opencode-free/deepseek-v3.1-free ' || '' }}github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1" + OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/grok-code-free opencode-free/code-supernova-free opencode-free/qwen3-coder-free opencode-free/gpt-oss-120b-free opencode-free/kimi-k2-free opencode-free/glm-4.6-free opencode-free/glm-4.5-free opencode-free/minimax-m2-free opencode-free/qwen3-max-free opencode-free/deepseek-v3.1-free opencode-free/glm-4.7-free opencode-free/big-pickle-free ' || '' }}github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1" # One attempt per model, then fall through to the next model. Retrying # the SAME model 5x let a rate-limited/hung leader consume the whole # step, so the pool never reached a healthy fallback model. diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index d9beedce2..00c7fef72 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -104,7 +104,9 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): "opencode-free/glm-4.5-free " "opencode-free/minimax-m2-free " "opencode-free/qwen3-max-free " - "opencode-free/deepseek-v3.1-free ' || '' }}" + "opencode-free/deepseek-v3.1-free " + "opencode-free/glm-4.7-free " + "opencode-free/big-pickle-free ' || '' }}" ) candidates_text = candidates_match.group(1) assert candidates_text.startswith(conditional_public_candidate) @@ -122,6 +124,8 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): "opencode-free/minimax-m2-free", "opencode-free/qwen3-max-free", "opencode-free/deepseek-v3.1-free", + "opencode-free/glm-4.7-free", + "opencode-free/big-pickle-free", *candidates_text.removeprefix(conditional_public_candidate).split(), ] candidate_pairs = [candidate.split("/", 1) for candidate in candidates] @@ -152,6 +156,8 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): ["opencode-free", "minimax-m2-free"], ["opencode-free", "qwen3-max-free"], ["opencode-free", "deepseek-v3.1-free"], + ["opencode-free", "glm-4.7-free"], + ["opencode-free", "big-pickle-free"], ["github-models", "deepseek/deepseek-v3-0324"], ["openai", "gpt-5.6-luna"], ["openrouter", "deepseek/deepseek-v3.2"], @@ -1297,7 +1303,9 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): "opencode-free/glm-4.5-free " "opencode-free/minimax-m2-free " "opencode-free/qwen3-max-free " - "opencode-free/deepseek-v3.1-free ' || ''" + "opencode-free/deepseek-v3.1-free " + "opencode-free/glm-4.7-free " + "opencode-free/big-pickle-free ' || ''" ) in workflow assert ( "github-models/deepseek/deepseek-v3-0324 " From 9f36a81bd2e366a81e08ae37bae644b45ad9b5cc Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 06:04:36 +0000 Subject: [PATCH 5/5] fix(review): scrape in more OpenCode Zen free models (MiniMax/MiMo/Nemotron/Qwen) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the remaining currently-free Zen models surfaced from OpenCode's public catalog notes — minimax-m2.5-free, mimo-v2-pro-free, mimo-v2-omni-free, nemotron-3-super-free, qwen3.6-plus-free — to widen the public-repo free pool as far as available so a transient per-model rate limit is more likely to be covered by another free candidate before the pool falls through to the credentialed paid providers. The exact live ids cannot be validated from this environment (egress to opencode.ai/models.dev is denied), so these follow the observed -free convention; the strict review-control normalizer skips any id that does not resolve, so a wrong id can only reduce capacity, never corrupt a review. Both public-prefix assertion sites and the exact free-model set assertion are updated to match. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01SEAGtwNR96cia2djq7XFCo --- .../workflows/opencode-review-dispatch.yml | 44 ++++++++++++++++++- tests/test_opencode_agent_contract.py | 29 +++++++++++- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index eb2aec72a..6a43764c9 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -3580,6 +3580,48 @@ jobs: "context": 256000, "output": 64000 } + }, + "minimax-m2.5-free": { + "name": "MiniMax M2.5 Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } + }, + "mimo-v2-pro-free": { + "name": "MiMo V2 Pro Free", + "tool_call": true, + "reasoning": true, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "mimo-v2-omni-free": { + "name": "MiMo V2 Omni Free", + "tool_call": true, + "reasoning": true, + "limit": { + "context": 200000, + "output": 32000 + } + }, + "nemotron-3-super-free": { + "name": "Nemotron 3 Super Free", + "tool_call": true, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "qwen3.6-plus-free": { + "name": "Qwen3.6 Plus Free", + "tool_call": true, + "limit": { + "context": 256000, + "output": 64000 + } } } }, @@ -3912,7 +3954,7 @@ jobs: # cost-efficient tier, cheaper than the legacy gpt-5 it replaced # ($1/$6 vs $1.25/$10 per 1M tokens) so the org OpenAI budget # stretches further between top-ups. - OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/laguna-s-2.1-free opencode-free/ling-3.0-flash-free opencode-free/big-pickle opencode-free/mimo-v2.5-free opencode-free/grok-code-free opencode-free/code-supernova-free opencode-free/qwen3-coder-free opencode-free/gpt-oss-120b-free opencode-free/kimi-k2-free opencode-free/glm-4.6-free opencode-free/glm-4.5-free opencode-free/minimax-m2-free opencode-free/qwen3-max-free opencode-free/deepseek-v3.1-free opencode-free/glm-4.7-free ' || '' }}github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1" + OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/laguna-s-2.1-free opencode-free/ling-3.0-flash-free opencode-free/big-pickle opencode-free/mimo-v2.5-free opencode-free/grok-code-free opencode-free/code-supernova-free opencode-free/qwen3-coder-free opencode-free/gpt-oss-120b-free opencode-free/kimi-k2-free opencode-free/glm-4.6-free opencode-free/glm-4.5-free opencode-free/minimax-m2-free opencode-free/qwen3-max-free opencode-free/deepseek-v3.1-free opencode-free/glm-4.7-free opencode-free/minimax-m2.5-free opencode-free/mimo-v2-pro-free opencode-free/mimo-v2-omni-free opencode-free/nemotron-3-super-free opencode-free/qwen3.6-plus-free ' || '' }}github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1" # One attempt per model, then fall through to the next model. Retrying # the SAME model 5x let a rate-limited/hung leader consume the whole # step, so the pool never reached a healthy fallback model. diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index a33e403da..37f3c742f 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -109,7 +109,12 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): "opencode-free/minimax-m2-free " "opencode-free/qwen3-max-free " "opencode-free/deepseek-v3.1-free " - "opencode-free/glm-4.7-free ' || '' }}" + "opencode-free/glm-4.7-free " + "opencode-free/minimax-m2.5-free " + "opencode-free/mimo-v2-pro-free " + "opencode-free/mimo-v2-omni-free " + "opencode-free/nemotron-3-super-free " + "opencode-free/qwen3.6-plus-free ' || '' }}" ) candidates_text = candidates_match.group(1) assert candidates_text.startswith(conditional_public_candidate) @@ -132,6 +137,11 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): "opencode-free/qwen3-max-free", "opencode-free/deepseek-v3.1-free", "opencode-free/glm-4.7-free", + "opencode-free/minimax-m2.5-free", + "opencode-free/mimo-v2-pro-free", + "opencode-free/mimo-v2-omni-free", + "opencode-free/nemotron-3-super-free", + "opencode-free/qwen3.6-plus-free", *candidates_text.removeprefix(conditional_public_candidate).split(), ] candidate_pairs = [candidate.split("/", 1) for candidate in candidates] @@ -167,6 +177,11 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): ["opencode-free", "qwen3-max-free"], ["opencode-free", "deepseek-v3.1-free"], ["opencode-free", "glm-4.7-free"], + ["opencode-free", "minimax-m2.5-free"], + ["opencode-free", "mimo-v2-pro-free"], + ["opencode-free", "mimo-v2-omni-free"], + ["opencode-free", "nemotron-3-super-free"], + ["opencode-free", "qwen3.6-plus-free"], ["github-models", "deepseek/deepseek-v3-0324"], ["openai", "gpt-5.6-luna"], ["openrouter", "deepseek/deepseek-v3.2"], @@ -213,6 +228,11 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates(): "qwen3-max-free", "deepseek-v3.1-free", "glm-4.7-free", + "minimax-m2.5-free", + "mimo-v2-pro-free", + "mimo-v2-omni-free", + "nemotron-3-super-free", + "qwen3.6-plus-free", } nemotron_model = free_models["nemotron-3-ultra-free"] deepseek_model = free_models["deepseek-v4-flash-free"] @@ -1354,7 +1374,12 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): "opencode-free/minimax-m2-free " "opencode-free/qwen3-max-free " "opencode-free/deepseek-v3.1-free " - "opencode-free/glm-4.7-free ' || ''" + "opencode-free/glm-4.7-free " + "opencode-free/minimax-m2.5-free " + "opencode-free/mimo-v2-pro-free " + "opencode-free/mimo-v2-omni-free " + "opencode-free/nemotron-3-super-free " + "opencode-free/qwen3.6-plus-free ' || ''" ) in workflow assert ( "github-models/deepseek/deepseek-v3-0324 "