RFC 0024 — Plugin compatibility & versioning
Status: Implemented
Date: June 2026
Author: kasunben
Scope: packages/manifest (maxPlatformVersion + semver validation + CURRENT_MANIFEST_SCHEMA_VERSION + a checkCompatibility resolver; add semver), scripts/generate-registry.ts + scripts/install-plugins.ts + bin/sv (install/build gates), runtime startup + plugin_status + /api/admin/health (boot disable + surface), registry/plugins.json (Task 0.5.19), docs/plugin-development.md + docs/self-hosting.md; amends RFC 0006 (consumes the boot gate + admin-health surfacing), disambiguates from RFC 0007 (schemaVersion clash), relates to Task 0.5.19/0.5.20
Incorporated into plan: Yes — scheduled as roadmap Task 0.5.22; documentation-first. This RFC defines the compatibility model and where it is enforced; SRS requirement IDs, scheduling, and task allocation are deferred.
Summary
Make the manifest's dormant version fields functional. Today schemaVersion and compatibility.minPlatformVersion are declared, structurally validated, and then ignored — no semver check, no comparison against the running platform, no maxPlatformVersion. This RFC:
- adds real semver validation and a platform-version compatibility model —
minPlatformVersionhard-enforced, a newmaxPlatformVersionadvisory (warns, doesn't block); - makes
schemaVersionfunctional via a current-version constant (accept older, reject unknown-newer manifest formats); - enforces all of it through one shared resolver at four points — install, build, boot, and the registry — with tiered behaviour: install and build refuse an incompatible plugin; boot disables and surfaces it so a single bad plugin never bricks the instance.
Motivation
A plugin built for a newer platform should fail loudly at install rather than misbehave; an instance that upgrades its platform shouldn't be bricked by one plugin that now requires something newer; and the manifest format needs room to evolve without breaking every existing plugin. The fields to express all this already exist — they're just inert. RFC 0006 already called for reusing minPlatformVersion as an install-/start-time check "so a plugin built for a newer platform fails loudly," and deferred the implementation; this RFC supplies the model.
Current state (what this builds on)
- Declared, not enforced.
packages/manifest/src/schema.tshasschemaVersion: z.number().int().positive()(always1, no constant) andcompatibility: { minPlatformVersion: z.string().min(1) }(any non-empty string). A repo-wide search finds no version comparison anywhere — both fields are declarative. - No
maxPlatformVersionand nosemverlibrary in the repo. - Validation pipeline.
validateManifest(packages/manifest/src/validate.ts) is structural-only;scripts/generate-registry.tsfails the build on an invalid manifest — the natural place to add a compatibility gate. - Platform version. Lives in root
package.json(currently0.6.0), read viagetPlatformVersion()(packages/sdk/src/platform.ts), exposed throughsdk.platform.getConfig().versionand/api/admin/health. - RFC 0006 anticipated this — "reuse
compatibility.minPlatformVersionas an install-/start-time check … fail loudly," plus a startup version gate surfaced in admin health (deferred). Task 0.5.19 registry "must target compatible platform version" is a human review note today; Task 0.5.20 defines SDK semver.
The three version concepts (disambiguation)
| Concept | Versions what | Compared against |
|---|---|---|
Manifest schemaVersion | The manifest format itself | The platform's CURRENT_MANIFEST_SCHEMA_VERSION |
compatibility.minPlatformVersion / maxPlatformVersion | The platform version range the plugin supports | The running platform version (semver) |
| SDK semver (Task 0.5.20) | The plugin↔platform contract | (v1: implied by platform version) |
For v1 the platform version is the single compatibility axis a plugin declares — the SDK ships with the platform, so plugins don't separately pin an SDK range.
Note the clash with RFC 0007: that RFC reuses the name schemaVersion for a plugin's data-format version inside export bundles — a different field in a different namespace. This RFC's schemaVersion is the manifest format version. The two are intentionally distinct.
Proposed design
Semver validation
Add semver (pure JS, catalog-pinned) to packages/manifest. Tighten minPlatformVersion (and the new maxPlatformVersion) from z.string().min(1) to valid semver at validation time, and use real semver comparison (a lexicographic compare would mis-order 0.10.0 vs 0.9.0).
minPlatformVersion (hard) + maxPlatformVersion (advisory)
minPlatformVersion— a hard gate: a plugin whose minimum exceeds the running platform version is incompatible.maxPlatformVersion— a new optional field, advisory: a plugin running on a platform above its declared max still loads, but emits a warning (surfaced in Console/health, logged) — "tested up to X; running on Y." This avoids stranding working plugins or forcing a max bump every platform release.
schemaVersion (functional)
Add CURRENT_MANIFEST_SCHEMA_VERSION (= 1) in packages/manifest. Accept a manifest with schemaVersion ≤ current (older plugins keep working as the format evolves); reject a higher value with a clear error — "manifest format newer than this platform understands." (An optional MIN_SUPPORTED_SCHEMA_VERSION lets ancient formats be dropped eventually.)
The shared resolver
A pure function in packages/manifest, reused everywhere so behaviour is identical:
checkCompatibility(
manifest: SovereignManifest,
platformVersion: string,
): { compatible: boolean; reason?: string; warnings: string[] };compatible: false+reasonwhenminPlatformVersion> platform, orschemaVersion> current.warningsfor the advisorymaxPlatformVersionovershoot.- No platform dependencies; unit-tested in isolation.
Tiered enforcement
| Check point | Behaviour on incompatible |
|---|---|
Install (sv plugin add / install-plugins) | Refuse with a clear error before adding. |
Build / generate (generate-registry.ts) | Fail the build for composed in-repo plugins (the dev path). |
| Boot / startup | Disable + surface the plugin (Console/health) — never brick. |
| Registry (Task 0.5.19) | Filter so users only see plugins installable on their version. |
The boot path re-checks against the (possibly upgraded) platform version and marks the plugin incompatible-disabled — distinct from an admin-disabled plugin in plugin_status/Console — feeding RFC 0006's admin-health version gate. Advisory-max warnings surface but don't disable.
Coordination with RFC 0006 / 0007
This RFC owns the model + fields + resolver + install/build checks; RFC 0006 consumes the boot-time gate and the admin-health surfacing (cross-referenced; a light amendment). RFC 0007's export-bundle schemaVersion remains a separate concept (noted above).
UI / flows
A developer whose minPlatformVersion is too new gets a build/install error naming required vs current version. After a platform upgrade, an incompatible installed plugin appears as disabled-incompatible in Console + /api/admin/health with the reason. A plugin past its advisory max shows a non-blocking warning. The registry hides plugins that don't satisfy the viewer's platform version.
Alternatives considered
- Hand-rolled version compare. Rejected — lexicographic ordering mishandles
0.10.0vs0.9.0;semveris tiny, pure-JS, and correct. - Hard-enforced
maxPlatformVersion. Rejected (per decision) — it strands working plugins and forces a maintenance bump on every platform release; advisory is the pragmatic default. - Fail loudly everywhere on incompatibility. Rejected — at boot, one external plugin requiring a newer platform would take down the whole instance; tiered (refuse on the explicit actions, disable+surface at boot) is safer.
- Leave
schemaVersiondeclarative. Rejected — the goal includes real schema validation + backward compatibility as the manifest format evolves.
Open questions
- Incompatible-disabled representation — a
plugin_statusflag vs derived at boot from the resolver. - Max escalation — should advisory
maxever become hard at a platform major boundary? MIN_SUPPORTED_SCHEMA_VERSION— introduce now or when the format first changes?- SDK range — should plugins additionally pin an SDK version range later (deferred — platform version is the v1 axis)?
- Requirement IDs — deferred until accepted.
Adoption path
- Documentation-first (this RFC).
- When accepted & scheduled: add
semver+maxPlatformVersion+ semver validation +CURRENT_MANIFEST_SCHEMA_VERSION+ thecheckCompatibilityresolver (manifest minor bump); wire the four check points; surface boot incompatibility in Console/health (with RFC 0006); document indocs/plugin-development.md+docs/self-hosting.md(+ docs-parity for the new field).
Changelog
| Version | Date | Change |
|---|---|---|
| 0.1 | Jun 2026 | Initial draft; makes schemaVersion + compatibility functional — semver-validated minPlatformVersion (hard) + advisory maxPlatformVersion, a CURRENT_MANIFEST_SCHEMA_VERSION accept-range, one shared resolver, tiered enforcement at install/build/boot/registry; documentation-first. |
| 0.2 | Jun 2026 | Accepted; scheduled in the roadmap as Task 0.5.22. |