Skip to content

fix(plugin): accept prereleases when checking engines.opencode - #27

Merged
brentyates-swx merged 1 commit into
swxtchfrom
fix/21-prerelease-plugin-gate
Sep 22, 2026
Merged

brentyates-swx merged 1 commit into
swxtchfrom
fix/21-prerelease-plugin-gate

Conversation

@brentyates-swx

Copy link
Copy Markdown

Closes #21

The defect

checkPluginCompatibility (packages/opencode/src/plugin/shared.ts) compared the build's own version against a plugin's engines.opencode range with plain semver, under which a prerelease satisfies no ordinary range:

version range satisfies
1.18.32-swxtch.1 >=1.18.0 false
1.18.32-swxtch.1 ^1.18.0 false
1.18.31 >=1.18.0 true
1.18.32 >=1.18.0 true

Every build of this fork is versioned 1.18.32-swxtch.N, so any plugin declaring engines.opencode was rejected here — and the message named the plugin:

Plugin requires opencode >=1.18.0 but running 1.18.32-swxtch.1

That is the same misdirection as #16, where a fork version string broke a gate that assumed an upstream release string, and the message pointed away from the cause.

Why includePrerelease and not semver.coerce

#21 offered both, and they are not equivalent. The case that separates them is a prerelease of the very version a plugin requires:

version 1.19.0-beta.1, range >=1.19.0
  includePrerelease -> false   (correct: the release is not out)
  coerce            -> true    (wrong: treats the beta as the release)

Measured, not reasoned about. coerce discards prerelease ordering and is more permissive in exactly the case where strictness matters. includePrerelease removes the "prereleases match nothing" rule and leaves ordering alone, so it is what this uses.

Tests

Added to packages/opencode/test/plugin/shared.test.ts, which previously had no coverage of this function at all:

  • a prerelease build accepted against >=1.18.0, ^1.18.0, >=1.18.0 <2.0.0, 1.x
  • a release build still accepted, so the fix is not "accept everything"
  • a genuinely too-old build still rejected, with both versions in the message
  • the discriminating case: a prerelease of the required version is still rejected
  • the early returns preserved — an invalid version, major-zero, no engines, a non-record engines, a non-string range — all of which mean "cannot judge", not "compatible"

Mutation-verified: reverting to plain satisfies fails 4 of these; switching to coerce fails exactly the one written to tell the two fixes apart. Three consecutive clean runs of the file.

🤖 Generated with Claude Code

https://claude.ai/code/session_017iG7xA9wRvNyFYZSXm8inU

checkPluginCompatibility compared the build's own version against a plugin's
engines.opencode range with plain semver, under which a prerelease satisfies no
ordinary range. Measured:

    1.18.32-swxtch.1  >=1.18.0  false
    1.18.32-swxtch.1  ^1.18.0   false
    1.18.31           >=1.18.0  true
    1.18.32           >=1.18.0  true

Every build of this fork is versioned 1.18.32-swxtch.N, so ANY plugin
declaring engines.opencode was rejected here - with a message naming the
plugin rather than the version string that actually caused it. The same
misdirection as #16, which cost hours.

#21 offered two fixes and they are not equivalent. semver.coerce would turn
1.19.0-beta.1 into 1.19.0 and therefore SATISFY a plugin requiring >=1.19.0,
loading it against a build of a release that is not out yet. Measured both:
for that case coerce returns true and includePrerelease returns false.
includePrerelease removes the "prereleases match nothing" rule without
disturbing version ordering, so that is what this uses.

Tests cover both directions, including the case that distinguishes the two
fixes, plus the early returns that mean "cannot judge" rather than
"compatible". Mutation-verified: reverting to plain satisfies fails 4 of them;
switching to coerce fails exactly the one written to tell them apart.

Closes #21

Claude-Session: https://claude.ai/code/session_017iG7xA9wRvNyFYZSXm8inU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

plugin: a prerelease build version rejects every plugin declaring engines.opencode

1 participant