Skip to content

fix: enforce opencode >= 1.14.19 and reveal window on Wayland#2262

Merged
juliusmarminge merged 2 commits intopingdotgg:mainfrom
mwolson:fix/opencode-min-and-wayland-deadlock
Apr 21, 2026
Merged

fix: enforce opencode >= 1.14.19 and reveal window on Wayland#2262
juliusmarminge merged 2 commits intopingdotgg:mainfrom
mwolson:fix/opencode-min-and-wayland-deadlock

Conversation

@mwolson
Copy link
Copy Markdown
Contributor

@mwolson mwolson commented Apr 21, 2026

Summary

Two Linux-critical fixes that need to land together for t3code to be
usable on Wayland against a freshly-installed opencode.

  1. fix(server): gate the opencode provider probe behind a minimum
    version of 1.14.19. Older opencode versions corrupt
    ~/.config/opencode/package-lock.json on every probe via a buggy
    Npm.install health check, blocking cold start for 45-75s per probe
    once the lockfile balloons. Fixes [Bug]: Regression: OpenCode probe blocks cold start 45-75s (no timeout) #2248.
  2. fix(desktop): on Linux, reveal the window on did-finish-load as a
    fallback so the app actually shows up under Electron 40 + Wayland,
    where ready-to-show can never fire when BrowserWindow is created
    with show: false. Fixes [Bug]: Desktop window never appears on Linux/Wayland (Electron ready-to-show deadlock) #2216.

This supersedes #2217 (Wayland-only) since I hit both problems on the
same regression-test AppImage and they're cheapest to review together.

Fix 1 - opencode min-version (#2248)

Older opencode versions (< 1.14.19) have a bug in their Npm.install
health check: the declared set includes raw name@version strings
from the caller's add input alongside bare keys from package.json,
while the locked set only ever contains bare names. The sets never
match, so arborist.reify() runs on every probe. When
~/.config/opencode is a symlink into a dotfiles directory, arborist
records paths relative to the resolved target and prepends another
../ segment on each pass, corrupting the lockfile.

T3 Code's opencode provider currently spawns a local opencode server
during every health check (via connectToOpenCodeServer ->
loadOpenCodeInventory), which triggers that forkDetach'd
Npm.install and further pollutes the lockfile. This change
short-circuits the probe before the server spawn when the detected
version is below 1.14.19, returning a provider snapshot with
status: \"error\" and a message directing the user to upgrade.

  • Add MINIMUM_OPENCODE_VERSION = \"1.14.19\" gate in OpenCodeProvider.
  • Treat unparseable --version output as failing the gate.
  • Leave the external-server code path unchanged (no local spawn, no
    bug).
  • Bump the test double's default version string to a gate-passing
    value and add two tests covering the version-gate and
    unparseable-version paths.

Live regression test

  • opencode v1.4.7 installed system-wide, ~/.config/opencode
    symlinked into a dotfiles dir with a pre-polluted 31 MB
    package-lock.json.
  • Built fresh AppImage with both fixes (this branch's HEAD).
  • Launched AppImage; window appeared within ~1s; Settings -> Providers
    showed "OpenCode v1.4.7 is too old. Upgrade to v1.14.19 or newer."
  • sha256sum ~/.config/opencode/package-lock.json unchanged before
    vs. after launch; no further lockfile corruption.

Fix 2 - Wayland deadlock (#2216)

Verbatim from #2217 (same commit cherry-picked here). On
Linux/Wayland with Electron 40, a BrowserWindow created with
show: false never receives ready-to-show, because the wl_surface
has no role assigned until show() is called and the compositor never
reports the surface as ready. Add did-finish-load as a Linux-only
fallback reveal trigger so the first event from either source surfaces
the window. Other platforms keep the no-flash ready-to-show path.
Logic extracted into bindFirstRevealTrigger
(apps/desktop/src/windowReveal.ts) with focused unit tests.

Test plan

  • bun fmt
  • bun lint (0 errors)
  • bun typecheck (10/10 tasks pass)
  • bun run test for apps/server/src/provider/Layers/OpenCodeProvider.test.ts (7/7)
  • bun run test for apps/desktop/src/windowReveal.test.ts (4/4)
  • End-to-end regression on Linux/Wayland (niri) using a v1.4.7 +
    polluted-lockfile setup; see "Live regression test" above.

Note

Medium Risk
Medium risk because it changes desktop window surfacing behavior on Linux and alters OpenCode provider health-check flow to short-circuit based on CLI version parsing/comparison, which could affect provider availability detection.

Overview
Fixes two Linux-facing issues.

On desktop, window reveal is refactored to fire on the first of ready-to-show or (Linux-only) did-finish-load, avoiding an Electron/Wayland deadlock when windows are created with show: false; this logic is extracted into bindFirstRevealTrigger with unit tests.

On server, the OpenCode provider probe now enforces MINIMUM_OPENCODE_VERSION = 1.14.19: if opencode --version is unparseable or older than the minimum, the provider returns an error snapshot with an upgrade message instead of spawning/probing the server; tests were updated and extended to cover these paths.

Reviewed by Cursor Bugbot for commit 0cff00b. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Enforce opencode >= 1.14.19 and fix window reveal on Linux/Wayland

  • Adds a MINIMUM_OPENCODE_VERSION check (1.14.19) in OpenCodeProviderLive: returns an error status immediately if the CLI version is unparseable or below the minimum, instead of proceeding to probe inventory.
  • Fixes window reveal on Linux/Wayland by introducing bindFirstRevealTrigger in windowReveal.ts, which fires a reveal callback once on whichever of ready-to-show or (Linux-only) did-finish-load arrives first — guarding against ready-to-show never firing with show: false on Wayland.
  • Behavioral Change: provider checks that previously silently continued on an old or unrecognized CLI version now return an error status to the UI.

Macroscope summarized 0cff00b.

mwolson added 2 commits April 21, 2026 11:03
Older opencode versions (< 1.14.19) have a bug in their Npm.install health
check: the `declared` set is built by including raw `name@version` strings
from the caller's `add` input alongside bare keys from package.json, while
the `locked` set only ever contains bare names. The sets never match, so
`arborist.reify()` runs on every probe. When ~/.config/opencode is a
symlink into a dotfiles directory, arborist records paths relative to the
resolved target and prepends another `../` segment on each pass, corrupting
the lockfile. In the field this produced 31 MB lockfiles that blocked cold
start for 45-75 seconds per probe.

T3 Code currently spawns a local opencode server during every provider
health check (via `connectToOpenCodeServer` -> `loadOpenCodeInventory`),
which triggers that forkDetach'd Npm.install and further pollutes the
lockfile. This change short-circuits the probe before the server spawn
when the detected version is below 1.14.19, returning a provider snapshot
with `status: "error"` and a message directing the user to upgrade.

- Add `MINIMUM_OPENCODE_VERSION = "1.14.19"` gate in OpenCodeProvider.
- Treat unparseable `--version` output as failing the gate.
- Leave the external-server code path unchanged (no local spawn, no bug).
- Bump the test double's default version string to a gate-passing value
  and add two tests covering the version-gate and unparseable-version
  paths.

Fixes pingdotgg#2248
…and deadlock

On Linux/Wayland, Electron's `ready-to-show` event only fires after
`show()` is called when the BrowserWindow is created with `show: false`,
because the wl_surface has no role assigned until then and the
compositor never reports the surface as ready. The standard "wait for
ready, then show" pattern deadlocks: nothing ever calls `show()`, so the
window never appears.

Add `did-finish-load` as a Linux-only fallback trigger so the first
event from either source reveals the window. Other platforms keep the
no-flash `ready-to-show` path, since `did-finish-load` typically fires
before the first paint there. Extract the bind-once logic into
`bindFirstRevealTrigger` with focused unit tests.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e353c619-5452-47cf-89b3-cab4f5c1d9e2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 21, 2026
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Apr 21, 2026

Approvability

Verdict: Approved

Two straightforward bug fixes: a platform-specific workaround for Wayland window reveal deadlock, and a minimum version check for the opencode CLI using existing utilities. Both changes are limited in scope, well-tested, and don't introduce new features or behavioral complexity.

You can customize Macroscope's approvability policy. Learn more.

@juliusmarminge juliusmarminge merged commit 055897f into pingdotgg:main Apr 21, 2026
12 checks passed
@mwolson mwolson deleted the fix/opencode-min-and-wayland-deadlock branch April 21, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

2 participants