Skip to content

feat: implement minimum host version checks for plugin installation and updates - #355

Merged
pikann merged 3 commits into
masterfrom
feature/implement-minimum-host-version-check
Aug 4, 2026
Merged

feat: implement minimum host version checks for plugin installation and updates#355
pikann merged 3 commits into
masterfrom
feature/implement-minimum-host-version-check

Conversation

@pikann

@pikann pikann commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a minCoreVersion field to the plugin manifest so a plugin can declare the minimum Paca (host) version it requires. The host now enforces this on every path that persists a manifest and rejects incompatible installs/upgrades with a new PLUGIN_INCOMPATIBLE_HOST_VERSION error, surfaced in the marketplace UI as a friendly, localized, per-card message.

Backend

  • plugindom.PluginManifest.MinCoreVersion: optional strict X.Y.Z (or vX.Y.Z) semver string; validated in Validate().
  • New plugindom.PluginManifest.CheckMinCoreVersion(hostVersion) (version.go): strict-parses MinCoreVersion, leniently parses the running build's version (tolerates v prefix and pre-release/build suffixes), and compares major.minor.patch. A host version with no numeric core (e.g. the "dev" default for local/unreleased builds) is treated as unconstrained rather than rejected.
  • plugin_service.Service now takes a hostVersion via WithHostVersion(...) and calls CheckMinCoreVersion in both InstallPlugin and UpdatePlugin, returning apierr.CodePluginIncompatibleHostVersion (mapped to HTTP 409) when the running build is older. Wired up in bootstrap via pluginsvc.New(pluginRepo).WithHostVersion(cfg.Release.Version).
  • apierr.Error gained a Details map[string]string field and NewWithDetails(...) constructor for structured, non-localized error context. The HTTP error envelope now includes an optional error_details field (omitted when empty) alongside error_code/error. For PLUGIN_INCOMPATIBLE_HOST_VERSION this carries plugin_id, required_version, and host_version so clients can build their own localized message instead of showing the English-only error string.

Frontend

  • PluginMarketplacePanel: install/upgrade failures are now shown inline on the affected plugin card instead of failing silently. Errors are tracked per-plugin (installErrors/upgradeErrors maps) and cleared on the next attempt or on success.
  • resolveMutationErrorMessage maps known plugin error codes to translated copy; PLUGIN_INCOMPATIBLE_HOST_VERSION interpolates required_version/host_version from error_details when present, falling back to a generic translated message otherwise.
  • lib/api-error.ts: added PluginNotFound, PluginNameTaken, PluginAlreadyUpToDate, PluginDowngradeNotAllowed, PluginIncompatibleHostVersion codes, plus getApiErrorMessage/getApiErrorDetails helpers and the error_details field on ApiErrorEnvelope.
  • New translation keys under marketplace.card.errors.* added to all locales (en, es, fr, ja, ko, pt-BR, ru, vi, zh-CN).

Docs

  • backend-plugin-system.md: new "Minimum Host Version (minCoreVersion)" section documenting enforcement points and the error_details contract.
  • developer-guide.md: minCoreVersion added to the example manifest, an explainer section, a semver-bump callout, and an updated pre-release checklist item.
  • marketplace.md and overview.md: updated to reference the new field/check.

Test plan

  • go test ./... in services/api — new coverage for PluginManifest.Validate/CheckMinCoreVersion (entity_test.go), service-level install/update gating (plugin_service_test.go), apierr.NewWithDetails (codes_test.go), and error_details presence/omission in the response envelope (response_test.go).
  • vitest in apps/web — new PluginMarketplacePanel.test.tsx covering install success, the interpolated incompatible-host-version message, the generic fallback when error_details is missing, a generic message for unrecognized errors, and error clearing on retry.

@pikann pikann changed the title feat: implement minimum host version checks for plugin installation a… feat: implement minimum host version checks for plugin installation and updates Aug 4, 2026

@pullfrog pullfrog Bot left a comment

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.

ℹ️ No critical issues — one sequencing observation in the body.

Reviewed changes

Reviewed the initial diff adding minCoreVersion enforcement across the plugin lifecycle.

  • Added PluginManifest.MinCoreVersion field and strict semver validation in the domain layer (entity.go, new version.go).
  • plugin_service.go now enforces the minimum host version in InstallPlugin and UpdatePlugin, returning PLUGIN_INCOMPATIBLE_HOST_VERSION with structured details.
  • Extended apierr.Error with a Details map and surfaced it in HTTP error envelopes as error_details.
  • The web marketplace panel now maps plugin install/upgrade errors — especially incompatible-host-version — to localized per-card messages with version interpolation.
  • Added translations for all supported locales and updated plugin-system docs.

ℹ️ Incompatible-host checks run after side effects in marketplace upgrades

In UpgradeMarketplacePlugin, the new manifest is downloaded, migrations run, and the WASM runtime is reloaded before svc.UpdatePlugin enforces CheckMinCoreVersion. If the host is too old, cleanupArtifacts removes the downloaded files, but the DB migrations and runtime load have already occurred. Consider calling manifest.CheckMinCoreVersion(hostVersion) immediately after downloading and validating the manifest so the request fails fast before any side effects.

This does not block merge — the current cleanup path prevents persisted inconsistency — but moving the gate earlier removes a window where an incompatible plugin briefly mutates the instance.

Pullfrog  | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

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.

Important

The host-version gate in the marketplace-upgrade handler runs after the installer has already overwritten the currently installed plugin's artifacts.

Reviewed changes

Reviewed the delta since the prior pullfrog review (64e5fa0).

  • Renamed the strict semver parser to exported ParseSemver and added CompareSemver in the plugin domain, with coverage in entity_test.go.
  • Added CheckHostCompatibility to the domain Service interface and the concrete plugin service, centralizing PLUGIN_INCOMPATIBLE_HOST_VERSION error construction.
  • Updated InstallPlugin/UpdatePlugin to call CheckHostCompatibility; plugin_handler.go now uses the shared domain CompareSemver for upgrade ordering.
  • Moved the host-version gate in UpgradeMarketplacePlugin so it runs before migrations and WASM runtime reload, addressing the sequencing concern from the previous review.
  • Updated the HTTP error envelope to resolve *apierr.Error once and emit error_details.
  • Added handler-level mock method and tests for the standalone CheckHostCompatibility call.

⚠️ Marketplace upgrade still overwrites installed artifacts before the host-version gate

installer.Install inside UpgradeMarketplacePlugin removes and replaces backend/frontend/migration files in-place. If CheckHostCompatibility fails a few lines later, the cleanup Uninstall removes the newly downloaded files, but the old files were already deleted by Install. The plugin record remains in the database while its artifacts are gone, leaving the installed plugin broken.

Move the host-version gate so it runs before any destructive install step, or change the installer to stage downloads in a temporary directory and only swap them in once all pre-replacement checks pass.

Technical details
# Marketplace upgrade artifact safety

## Affected sites
- `services/api/internal/transport/http/handler/plugin_handler.go:383``h.installer.Install` overwrites backend/frontend/migration files before compatibility is checked.
- `services/api/internal/transport/http/handler/plugin_handler.go:404``h.svc.CheckHostCompatibility` is called after the destructive install.

## Required outcome
- An incompatible marketplace upgrade must leave the currently installed plugin's artifacts intact.
- The existing plugin must keep running after the API returns `PLUGIN_INCOMPATIBLE_HOST_VERSION`.

## Suggested approach
Fetch only the manifest tarball from the marketplace entry before downloading the full artifact set; run `CheckHostCompatibility` on that manifest, or refactor `installer.Install` to stage all artifacts in a temp directory and only replace the real directories once every pre-replacement gate has passed.

Note: 1 inline comment(s) dropped because they did not anchor to lines inside the PR diff:

  • services/api/internal/transport/http/handler/plugin_handler.go:383-404 (RIGHT) — start_line 383 (RIGHT) is not inside a diff hunk

Pullfrog  | Fix it ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

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.

✅ No new issues found.

Reviewed changes

Reviewed the delta since prior pullfrog review (6002a04).

  • Moved the host-version gate inside Installer.Install by adding a checkManifest callback that runs on the downloaded manifest immediately after parsing/validation and before any existing installed backend/frontend/migration/MCP/skills directory is removed or overwritten, fixing the prior marketplace-upgrade sequencing concern.
  • Added installer_test.go regression coverage: a rejected callback leaves existing artifacts untouched, an accepted callback lets writes proceed, and a nil callback is still allowed.
  • InstallMarketplacePlugin and UpgradeMarketplacePlugin now pass h.svc.CheckHostCompatibility to installer.Install and map installer errors through installArtifactsError, preserving *apierr.Error details such as PLUGIN_INCOMPATIBLE_HOST_VERSION instead of flattening them to CodeBadRequest.
  • Removed the now-redundant explicit CheckHostCompatibility + cleanup block in UpgradeMarketplacePlugin and the local compareSemver/parseSemver helpers; upgrade ordering now uses the exported plugindom.CompareSemver.
  • presenter.Error resolves *apierr.Error once and emits error_details; PLUGIN_INCOMPATIBLE_HOST_VERSION maps to HTTP 409 Conflict.
  • Extended tests in plugin_handler_test.go, response_test.go, and plugin_service_test.go for the callback wiring, error-detail envelope behavior, and direct CheckHostCompatibility usage.
  • Added web marketplace per-card error display keyed by plugin name/install ID, with localized interpolation of required_version/host_version from error_details and fallbacks when details are absent.

Local go test passed for the affected API packages (internal/domain/plugin, internal/apierr, internal/transport/http/presenter, internal/service/plugin, internal/platform/plugin, internal/transport/http/handler).

Pullfrog  | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pikann
pikann merged commit 876cd3a into master Aug 4, 2026
6 checks passed
@pikann
pikann deleted the feature/implement-minimum-host-version-check branch August 4, 2026 07:49
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.

1 participant