Skip to content

[3389 by Claude Opus 5.5] 0.9.0 release logic fix: OpenCode free models are stale, and the failure reason hides the real error. Check current upstream main first. - #3390

Merged
integry merged 2 commits into
mainfrom
3389/claude-opus-5-5-0-9-0-release-logic-fix-o-20261029-1836-0b0
Oct 29, 2026
Merged

integry merged 2 commits into
mainfrom
3389/claude-opus-5-5-0-9-0-release-logic-fix-o-20261029-1836-0b0

Conversation

@propr-dev

@propr-dev propr-dev Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

AI Implementation Summary

Closes #3389

Branch: 3389/claude-opus-5-5-0-9-0-release-logic-fix-o-20261029-1836-0b0
Commits: ✅ Changes committed (085291a)


AI Processing Completed

Execution Details:

Summary:
I've fixed all five points of #3389. One requested check didn't run: there's no Docker in this container, so I couldn't build the agent image or run scripts/smoke-test-images.sh. Upstream main is the same commit as this branch, so nothing there already covered this.

Why the failure reason was wrong. When OpenCode runs the way ProPR runs it (prompt attached as a file), a failed run sends two error events. The first one has your Model not found: … Did you mean: … message, then a generic "Unexpected server error" follows. ProPR kept only the last event. On other runs OpenCode sends only the generic event, and the real cause is only in its log, which OpenCode 1.19.5 doesn't write to its log file when the run fails. ProPR's wrapper was also reading logs from the wrong directory.

What changed

  1. CLI version. OpenCode is now 1.19.5 (the latest release) in Dockerfile.agent (both stages), scripts/build-images.sh, the core and shared default versions, and the tests that pin the version. opencode models and live one-line runs worked without login on all 8 free models. The image-build flag checks also passed against this binary.

  2. Free-model catalog. It now lists Big Pickle (still the default), Beam, DeepSeek V4.1 Flash, Flint 40B, GLM 5.3 Flash, MiMo V2.6 Flash, Nemotron 3.5 Lightning and Nemotron 3.5 Super VL. Nemotron 3 Ultra Free is gone: it was listed at first, failed twice with "Model is unavailable", then disappeared from the list. The pricing IDs for Beam and Flint are my best match by name and context size, not confirmed.

  3. Retired models. A new table in packages/shared/src/openCodeCompatibility.ts, modeled on the Antigravity one, maps each retired model to a current one:

    • Ling → DeepSeek V4.1 Flash
    • MiMo V2.5 → MiMo V2.6 Flash
    • Nemotron 3 Ultra → Nemotron 3.5 Super VL
    • Muse Spark → Big Pickle
    • The five older retired models are mapped too.

    On startup, agents drop retired models and a retired default moves to its replacement. Saved selections, routes, labels, aliases and /review picks resolve to the replacement. Any retired ID that still reaches a run is swapped before it is passed to OpenCode, and the health check no longer probes Ling.

  4. Failure reasons.

    • A specific error is no longer replaced by a generic one that comes after it.
    • The wrapper now passes --print-logs --log-level ERROR, and a generic error is replaced by the logged cause with the same ref.
    • The reason still goes through agentFailureLine and redaction.

    I added regressions using the exact event from the issue. Run through the real wrapper and OpenCode 1.19.5, the stored reason is now Model not found: opencode/ling-3.0-flash-fin-free. Did you mean: deepseek-v4.1-flash-free, glm-5.3-flash-free, mimo-v2.6-flash-free?. Successful runs still leave stderr empty. One side effect: a failed run's logs no longer include the last 80 lines of OpenCode's log file, but that step was reading the wrong directory and never worked.

  5. Changelog and docs. 0.9.0 has Changed bullets for the CLI and the catalog, the Retired models entry is updated, and there's a Fixed bullet for the failure reason. The OpenCode model table in the docs is updated.

Test results

  • A run of 95 test files covering the changed code passed, as did the OpenCode agent, parser and model-definition tests (including test/modelDefinitions.test.ts). After that run I made one small fix to how logged messages are unescaped and reran the OpenCode tests, which pass.
  • Root lint and typecheck, workspace lint with --max-warnings 0, typechecks for api, core, shared and the UI, and EXPECTED_VERSION=0.9.0 RELEASE_TAG=v0.9.0 RELEASE_CANDIDATE=true npm run release:verify all pass.
  • The optional live native-log test passes against the real 1.19.5 binary.
  • test/modelAliases.test.ts always reports a pass because it ends with process.exit(0), which hides 15 failures that already exist on main. With that line removed, my new tests pass and the 15 failures are the same ones as on main.

I captured the new OpenCode model list in the agent settings at desktop and mobile width as previews. Nothing was merged, tagged, published or deployed.

Detailed Logs:

  • Conversation: 288 messages
  • Session: a8401383-b1fd-419d-aaec-19621b10c196

Log files stored at:

  • conversation: /usr/src/app/logs/executions/issue-3389-2026-10-29T19-30-37-405Z-conversation.json
  • output: /usr/src/app/logs/executions/issue-3389-2026-10-29T19-30-37-405Z-output.txt
Latest Conversation Messages
ASSISTANT: [content unavailable]

ASSISTANT: I've fixed all five points of #3389. One requested check didn't run: there's no Docker in this container, so I couldn't build the agent image or run `scripts/smoke-test-images.sh`. Upstream `main` is ...


This PR was created automatically by ProPR after processing issue #3389.


💡 Need changes?

Comment on this PR to request refinements — the AI agent monitors comments and will update the implementation based on your feedback. Keep iterating until you're satisfied!


Visual preview

OpenCode free models (desktop)

OpenCode free models (desktop)

AI Agents configuration: the OpenCode card lists the eight free models OpenCode CLI 1.19.5 serves; the retired Ling, MiMo V2.5, Muse Spark and Nemotron 3 Ultra entries are gone

Original unavailable: managed storage is unavailable or the upload has expired.

OpenCode free models (mobile)

OpenCode free models (mobile)

The same OpenCode model list at 390px width

Original unavailable: managed storage is unavailable or the upload has expired.

…there's no Docker in this container, so I couldn't build the agent image or run `scripts/smoke-test-images.sh`. Upstream `main` is the same commit as this branch, so nothing there already covered this.

Why the failure reason was wrong. When OpenCode runs the way ProPR runs it (prompt attached as a file), a failed run sends two error events. The first one has your `Model not found: … Did you mean: …` message, then a generic "Unexpected server error" follows. ProPR kept only the last event. On other runs OpenCode sends only the generic event, and the real cause is only in its log, which OpenCode 1.19.5 doesn't write to its log file when the run fails. ProPR's wrapper was also reading logs from the wrong directory.

What changed
1. CLI version. OpenCode is now 1.19.5 (the latest release) in `Dockerfile.agent` (both stages), `scripts/build-images.sh`, the core and shared default versions, and the tests that pin the version. `opencode models` and live one-line runs worked without login on all 8 free models. The image-build flag checks also passed against this binary.
2. Free-model catalog. It now lists Big Pickle (still the default), Beam, DeepSeek V4.1 Flash, Flint 40B, GLM 5.3 Flash, MiMo V2.6 Flash, Nemotron 3.5 Lightning and Nemotron 3.5 Super VL. Nemotron 3 Ultra Free is gone: it was listed at first, failed twice with "Model is unavailable", then disappeared from the list. The pricing IDs for Beam and Flint are my best match by name and context size, not confirmed.
3. Retired models. A new table in `packages/shared/src/openCodeCompatibility.ts`, modeled on the Antigravity one, maps each retired model to a current one:
   - Ling → DeepSeek V4.1 Flash
   - MiMo V2.5 → MiMo V2.6 Flash
   - Nemotron 3 Ultra → Nemotron 3.5 Super VL
   - Muse Spark → Big Pickle
   - The five older retired models are mapped too.

   On startup, agents drop retired models and a retired default moves to its replacement. Saved selections, routes, labels, aliases and `/review` picks resolve to the replacement. Any retired ID that still reaches a run is swapped before it is passed to OpenCode, and the health check no longer probes Ling.
4. Failure reasons.
   - A specific error is no longer replaced by a generic one that comes after it.
   - The wrapper now passes `--print-logs --log-level ERROR`, and a generic error is replaced by the logged cause with the same `ref`.
   - The reason still goes through `agentFailureLine` and redaction.

   I added regressions using the exact event from the issue. Run through the real wrapper and OpenCode 1.19.5, the stored reason is now `Model not found: opencode/ling-3.0-flash-fin-free. Did you mean: deepseek-v4.1-flash-free, glm-5.3-flash-free, mimo-v2.6-flash-free?`. Successful runs still leave stderr empty. One side effect: a failed run's logs no longer include the last 80 lines of OpenCode's log file, but that step was reading the wrong directory and never worked.
5. Changelog and docs. 0.9.0 has Changed bullets for the CLI and the catalog, the Retired models entry is updated, and there's a Fixed bullet for the failure reason. The OpenCode model table in the docs is updated.

Test results
- A run of 95 test files covering the changed code passed, as did the OpenCode agent, parser and model-definition tests (including `test/modelDefinitions.test.ts`). After that run I made one small fix to how logged messages are unescaped and reran the OpenCode tests, which pass.
- Root lint and typecheck, workspace lint with `--max-warnings 0`, typechecks for api, core, shared and the UI, and `EXPECTED_VERSION=0.9.0 RELEASE_TAG=v0.9.0 RELEASE_CANDIDATE=true npm run release:verify` all pass.
- The optional live native-log test passes against the real 1.19.5 binary.
- `test/modelAliases.test.ts` always reports a pass because it ends with `process.exit(0)`, which hides 15 failures that already exist on `main`. With that line removed, my new tests pass and the 15 failures are the same ones as on `main`.

I captured the new OpenCode model list in the agent settings at desktop and mobile width as previews. Nothing was merged, tagged, published or deployed.
@integry

integry commented Oct 29, 2026

Copy link
Copy Markdown
Owner

/review

@propr-dev

propr-dev Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex:gpt-6-astra

Overall Evaluation

The PR refreshes OpenCode versions and models, adds compatibility routes, and improves failure reporting. It needs a localized correction before merge: switching a saved plan away from a retired model can leave its old routing label active.

✅ Preserves useful errors — A specific JSON error survives a later generic server error, with regression coverage for both event orders.

✅ Correlates logged causes — Generic errors are replaced only by stderr causes matching their error reference; unrelated references are tested.

✅ Consistent version updates — Docker stages, build defaults, shared defaults, and core defaults agree on OpenCode 1.19.5.

This review uses static analysis of the supplied code; no commands were run. The issue specification is truncated, so unseen acceptance criteria could not be assessed. Current checks report no failures and three pending checks.

Merge blockers

Every finding below was introduced by this PR and must be resolved before merging.

F1: 🔴 Remove the original retired routing label

  • Required behavior: Changing a plan’s selected models must remove its previous routing label so implementation runs only on the newly selected models.

  • Evidence: packages/api/routes/planIssueHelpers.ts:134, getLlmLabel and handleMultiAgentImplementation

    1. An existing plan stores opencode-muse-spark-1.3-contributor-free, and its GitHub issue carries the previously generated llm-opencode-muse-spark13-free label.
    2. The user changes the plan’s implementation selection to another model through handleMultiAgentImplementation.
    3. That handler computes oldLlmLabel using getLlmLabel. The new normalization returns llm-opencode-big-pickle, rather than the label already attached to the issue.
    4. publishImplementationLabels consequently removes the nonexistent successor label and adds the new selection, leaving llm-opencode-muse-spark13-free attached.
    5. The new compatibility resolver recognizes that surviving label as Big Pickle. Since multiple model labels fan out into separate jobs, implementation includes an unintended additional OpenCode run.

    static trace: The supplied handler constructs removals exclusively from the recomputed oldLlmLabel; suspending and restoring the implement label does not correct that removal set. The supplied queued-issue repair path filters actual existing labels and does not share this defect. Added alias-resolution tests verify that the surviving retired label remains actionable, but do not cover changing selections on an already labeled issue.

  • Minimum fix: Preserve the original label identity for removal when changing selections, while generating successor labels for additions. Remove both original and successor forms where appropriate, excluding selected labels, and add a regression covering a retired label already attached to the issue.

Suggestions

These are optional follow-ups and are not sent to /fix.

S1: 🟢 Exercise the wrapper behavior

The wrapper tests inspect script text. An executable test using a stub opencode could additionally verify argument forwarding, stderr propagation, exit status, and prompt-file cleanup. This would strengthen coverage of the changed invocation without requiring Docker; it is optional because no wrapper defect is demonstrated here.

Score

The error-reporting changes and compatibility tests are useful, but the persisted-label regression requires correction. No current checks have failed; three remain pending.

Score: 6/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 52s
  • Tokens: 64,316 (63,321 in / 995 out)
  • Cost: $0.57

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix F1 S1

🤖 Review by ProPR

@integry

integry commented Oct 29, 2026

Copy link
Copy Markdown
Owner

/fix F1

@propr-dev

propr-dev Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ Applied the requested follow-up changes in commit 0493b39

Addressed 1 AI review comment (IDs: 6506712104)

Addressed finding F1

Summary of Changes

I fixed F1. When you change an issue's model selection away from a retired OpenCode model, the old label that is actually on the issue (like llm-opencode-muse-spark13-free) now gets removed, along with the newer replacement label. Before, only the replacement label was removed, so the old one stayed on the issue and started an extra Big Pickle run.

The change

  • packages/shared/src/openCodeCompatibility.ts: a new retiredOpenCodeModelLabel(id) gives back the label a retired model had before it was retired. It accepts the opencode: and opencode/ forms.
  • packages/api/routes/planIssueHelpers.ts: a new getAttachedLlmLabels(model, alias) returns every label an issue could already have for its saved model: the current label and, for a retired model, the old label too (including the agent-alias version when an alias is set). handleMultiAgentImplementation now removes all of these. Any label the user still has selected is left alone, so picking a retired model's replacement keeps the replacement label and only drops the old one.

The same defect elsewhere
syncModelLabels in packages/api/routes/planIssueConfigSync.ts, which runs when one issue's model or agent is edited, also built its removal list from the replacement label only. I fixed it the same way. The queued-issue repair path doesn't have this problem, as the review said.

Tests

  • New test/planIssueRetiredLlmLabels.test.ts runs the real handleMultiAgentImplementation, with only the GitHub client, queue and database replaced by fakes. It covers an issue that already has the retired label, both when switching to a different model and when switching to the retired model's replacement.
  • With the old removal logic put back temporarily, both handler tests fail. With the fix, they pass.
  • test/planIssueConfigSync.test.ts gets a matching test for the single-issue edit path.
  • I added the new export to the test mocks in test/planIssueConfigSync.test.ts, test/planIssueHandlers.followup.test.ts and packages/api/test/mcpEpicDispatch.test.ts. Without it the config-sync module wouldn't load in two of them, and mcpEpicDispatch would quietly call the real label lookup.
  • Passing: the new file (3), planIssueConfigSync (4), planIssueHandlers.followup (9), planIssueHelpers (4), mcpEpicDispatch (72) and planIssueImplementationTrigger (2).
  • Typechecks for shared, api and the root pass, and ESLint with --max-warnings 0 is clean on the changed api files. The shared source and root test/ files aren't covered by any lint config.

One side effect: changing away from a retired model now sends one or two extra label-removal calls to GitHub. A label that isn't on the issue comes back as "not found", which is treated as success, so nothing fails.

There's nothing visual to preview.


🤖 Implementation Details

  • Model: Claude Opus 5.5 (medium)
  • Requested By: @integry
  • Time: 21m 6s
  • Tokens: 2,099,400 (2,082,875 in / 16,525 out)
  • Cost: $1.14
  • Subscription usage: Session +5%, Weekly +1%

Undo Changes • View Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Address review blockers, or blockers and suggestions with /fix all /fix, /fix all, or /fix F3 S5
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

🤖 Generated by ProPR
Processing comment ID: 6506967925✓

… from a retired OpenCode model, the old label that is actually on the issue (like `llm-opencode-muse-spark13-free`) now gets removed, along with the newer replacement label. Before, only the replacement label was removed, so the old one stayed on the issue and started an extra Big Pickle run.

I fixed F1. When you change an issue's model selection away from a retired OpenCode model, the old label that is actually on the issue (like `llm-opencode-muse-spark13-free`) now gets removed, along with the newer replacement label. Before, only the replacement label was removed, so the old one stayed on the issue and started an extra Big Pickle run.

**The change**
- `packages/shared/src/openCodeCompatibility.ts`: a new `retiredOpenCodeModelLabel(id)` gives back the label a retired model had before it was retired. It accepts the `opencode:` and `opencode/` forms.
- `packages/api/routes/planIssueHelpers.ts`: a new `getAttachedLlmLabels(model, alias)` returns every label an issue could already have for its saved model: the current label and, for a retired model, the old label too (including the agent-alias version when an alias is set). `handleMultiAgentImplementation` now removes all of these. Any label the user still has selected is left alone, so picking a retired model's replacement keeps the replacement label and only drops the old one.

**The same defect elsewhere**
`syncModelLabels` in `packages/api/routes/planIssueConfigSync.ts`, which runs when one issue's model or agent is edited, also built its removal list from the replacement label only. I fixed it the same way. The queued-issue repair path doesn't have this problem, as the review said.

**Tests**
- New `test/planIssueRetiredLlmLabels.test.ts` runs the real `handleMultiAgentImplementation`, with only the GitHub client, queue and database replaced by fakes. It covers an issue that already has the retired label, both when switching to a different model and when switching to the retired model's replacement.
- With the old removal logic put back temporarily, both handler tests fail. With the fix, they pass.
- `test/planIssueConfigSync.test.ts` gets a matching test for the single-issue edit path.
- I added the new export to the test mocks in `test/planIssueConfigSync.test.ts`, `test/planIssueHandlers.followup.test.ts` and `packages/api/test/mcpEpicDispatch.test.ts`. Without it the config-sync module wouldn't load in two of them, and `mcpEpicDispatch` would quietly call the real label lookup.
- Passing: the new file (3), `planIssueConfigSync` (4), `planIssueHandlers.followup` (9), `planIssueHelpers` (4), `mcpEpicDispatch` (72) and `planIssueImplementationTrigger` (2).
- Typechecks for shared, api and the root pass, and ESLint with `--max-warnings 0` is clean on the changed api files. The shared source and root `test/` files aren't covered by any lint config.

One side effect: changing away from a retired model now sends one or two extra label-removal calls to GitHub. A label that isn't on the issue comes back as "not found", which is treated as success, so nothing fails.

There's nothing visual to preview.

PR: #3390
Comment by: @integry (ID: 6506967925)
Model: Claude Opus 5.5
@integry

integry commented Oct 29, 2026

Copy link
Copy Markdown
Owner

/review

@propr-dev

propr-dev Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex:gpt-6-astra

Overall Evaluation

The PR updates OpenCode’s bundled version and free-model catalog, preserves retired selections through compatibility routes, and improves failure reporting. It appears merge-ready within the supplied scope, conditional on the pending Validate Changes check.

✅ Specific failure preservation — The parser retains a specific error when a generic server error follows, and stderr recovery requires the matching error reference.

✅ Consistent compatibility routing — Retired models resolve to current models across execution, saved-selection validation, token limits, and label generation.

✅ Historical label cleanup — Configuration changes and multi-agent implementation remove historical retired-model labels, with regression coverage.

This was a static review; no commands were run. The original issue description is explicitly truncated, so acceptance requirements beyond the visible text could not be assessed.

Merge blockers

No merge blockers.

Suggestions

These are optional follow-ups and are not sent to /fix.

S2: 🟢 Test configured-agent historical labels

Add coverage for getAttachedLlmLabels with a custom OpenCode agent alias, including an alias long enough to trigger dynamic-label hashing. The implementation handles both historical catalog labels and configured-agent labels, but the new integration tests exercise only the default catalog form. This is optional coverage for an implemented branch, not a demonstrated defect.

Score

The supplied changes address the visible failure and migration paths with focused regression tests. No verified blocker was found; authoritative checks report 27 passed, none failed, and one pending.

Score: 9/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 37s
  • Tokens: 69,342 (68,694 in / 648 out)
  • Cost: $0.61

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix S2

🤖 Review by ProPR

@integry
integry merged commit c849dee into main Oct 29, 2026
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0.9.0 release logic fix: OpenCode free models are stale, and the failure reason hides the real error. Check current upstream main first.

1 participant