Skip to content

fix(plugins): self-heal incompatible drivers at connect instead of blocking (#1552)#1555

Merged
datlechin merged 4 commits into
mainfrom
fix/1552-plugin-compat-graceful
Jun 2, 2026
Merged

fix(plugins): self-heal incompatible drivers at connect instead of blocking (#1552)#1555
datlechin merged 4 commits into
mainfrom
fix/1552-plugin-compat-graceful

Conversation

@datlechin
Copy link
Copy Markdown
Member

Fixes #1552.

Problem

After an app update that bumps the PluginKit ABI (v0.48.0 went 17 to 18 to turn on Swift Library Evolution), registry plugins installed against the old kit are rejected. Opening a connection shows "no compatible build is available yet", a second click shows "plugin not installed", and the only recovery is quitting and reopening. This recurs on every release that bumps the kit.

Root cause (three)

  1. The version gate (validateBundleVersions) requires an exact match and fires before the bundle loads, so a kit-17 plugin is rejected outright even though the framework is now resilient.
  2. noCompatibleBinary is treated as a permanent failure, and nothing re-runs reconciliation while the app is open, so only a relaunch recovers.
  3. The app release and the registry re-publish are decoupled, and the manifest is served from a CDN that caches for ~5 minutes, so the new binary is often not visible yet.

What changed

A. Floor gate. minimumCompatiblePluginKitVersion = 18; the gate accepts the range [floor, current] and only blocks plugins newer than the app. Additive bumps (18 to 19) no longer reject installed plugins. Today floor equals current, so behavior is unchanged until the next bump.

B. Graceful self-heal at connect. noCompatibleBinary is transient when the registry has not published the current-kit binary yet (still permanent when the registry only has a newer-kit binary, which needs an app update). ensurePluginReady re-runs reconciliation at connect and on network reachability, so the driver updates in the background and the connection proceeds. No quit and reopen. The "error then a different error on the second click" is gone because the rejected entry stays consistent.

C. Distribution. resolvedBinary selects the highest binary kit within [floor, current], so an additive bump is served by the existing binaries with no re-publish. The manifest is served through jsDelivr and purged on publish. A registry-readiness job fails the app release until every database driver has a compatible binary.

Kit-17 plugins were built before Library Evolution, so they cannot load under kit-18 and must update once. 17 to 18 is the last forced break; the floor gate stops it recurring from 18 to 19 on.

Also included

This branch is stacked on the Plugins-settings rejected-banner refactor (the two earlier commits): the unloaded-plugins banner scrolls instead of pushing the list off screen, shows each plugin's real icon, and only offers Update when a compatible build exists.

Testing

  • Build succeeds.
  • 35 unit tests pass: gate floor range, range-aware binary selection, rejected-plugin action classification.
  • scripts/check-registry-readiness.py validated against the live registry (floor 18 / current 18 ready; floor 18 / current 19 also passes off existing kit-18 binaries).

Review notes

  • The CI changes (jsDelivr purge in build-plugin.yml, the registry-readiness gate in build.yml) run only in Actions and were not verified end to end locally.
  • Switching the manifest URL to jsDelivr changes every client's manifest fetch.

@mintlify
Copy link
Copy Markdown

mintlify Bot commented Jun 2, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jun 2, 2026, 5:27 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 697644eeb6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +31 to +36
def compatible_kits(plugin):
return sorted({
binary.get("pluginKitVersion")
for binary in plugin.get("binaries", [])
if binary.get("pluginKitVersion") is not None
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require compatible binaries for each architecture

This readiness check collapses all binaries into a single set of PluginKit versions, so a plugin with only an arm64 binary in the supported range and an outdated/missing x86_64 binary still passes. The release job in .github/workflows/build.yml ships both build-arm64 and build-x86_64, and the plugin publish workflow writes architecture-specific registry entries, so after a breaking PluginKit floor bump this can allow the app release even though Intel users will still have no loadable driver binary.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 385412037a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +262 to +263
if availableKits.contains(where: { $0 >= minimumKitVersion && $0 <= currentKitVersion }) {
return .updateAvailable(registryPlugin)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check minAppVersion before offering plugin updates

When the registry entry still has an old binary in the app's PluginKit range but its plugin-level minAppVersion has moved past the running app, this returns updateAvailable and the rejected-plugin UI shows an Update Now button. That update cannot succeed because validateRegistryCompatibility rejects registryPlugin.minAppVersion before downloading, so older app versions with retained compatible binaries get a misleading/retryable update path instead of the Update TablePro path.

Useful? React with 👍 / 👎.

@datlechin datlechin merged commit 9ea6314 into main Jun 2, 2026
4 checks passed
@datlechin datlechin deleted the fix/1552-plugin-compat-graceful branch June 2, 2026 11:00
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.

Version 0.48.0 is not able to load Cloudflare D1 plugins

1 participant