PageMotor 0.8 Rollout — Integrator Report
I upgraded an EP Suite ecosystem (105 sites across 6 environments) from 0.7.2 to 0.8. This report captures what a developer running a real plugin suite on a mix of VPS + shared-hosting environments actually encountered. Structured to feed your upgrade guide + SDK docs.
Contents
- Scope
- PM 0.8 findings (1–4)
- Update-server protocol (SDK notes)
- Plugins UI post-install refresh
- Environmental friction
- Upgrade pattern that worked at scale
- AI Design Conversion Skill 1.2
- Summary (PM 0.8)
- Finding #7 — admin-updates template missing on IONOS shared
- PM 0.8.1b Rollout
- EP Suite Plugin Registry (40 published + 16 WIP)
Scope
| Environment | Sites | Result |
|---|---|---|
| Localhost dev | 1 | 0.8 ✓ |
| Vultr VPS (nip.io) | 51 | 0.8 ✓ |
| Linode VPS (nip.io) | 28 | 0.8 ✓ |
| DigitalOcean VPS (nip.io) | 13 | 0.8 ✓ (9 provisioned dirs without nginx configs left on 0.7.2; unreachable externally) |
| IONOS VPS (real domains, per-site owners) | 11 | 0.8 ✓ (one site on 0.6 migrated cleanly through v07 and v08) |
| IONOS shared hosting (SFTP-only) | 1 | 0.8 ✓ with opcache_invalidate guard patch + manual Focus-theme bleed threading |
updates.elmspark.com rewritten for the 0.8 protocol) was exercised end-to-end on three canaries: localhost, Vultr Copperline, Linode Copperline. I didn’t re-run that test on every one of the 105 sites; the bulk rollout validated the upgrade path and migration, not the install flow.
Three PM 0.8 findings
1. opcache_invalidate() is unguarded
lib/themes.php:68 calls opcache_invalidate(...) directly. On hosts without the Zend OPcache extension (common on shared hosting — IONOS shared hit this), it’s a fatal.
update_core() runs only on admin page loads (themes.php:61), so the failure mode is: frontend continues to work, site owner tries to log into /admin/ after upgrading, gets Uncaught Error: Call to undefined function opcache_invalidate(). Diagnosing this from outside is frustrating because the site looks fine.
Stack:
PHP Fatal error: Uncaught Error: Call to undefined function opcache_invalidate() in lib/themes.php:68
Stack trace:
#0 lib/themes.php(48): PM_Themes->update_core()
#1 pagemotor.php(168): PM_Themes->__construct()
#2 index.php(27): PageMotor->init()
Fix:
// before
opcache_invalidate(PM_USER_THEMES. "/{$theme['folder']}/$file.php", true);
// after
if (function_exists('opcache_invalidate'))
opcache_invalidate(PM_USER_THEMES. "/{$theme['folder']}/$file.php", true);
2. v07() replay edge case
Sites that already had update_07=1 in options (set by an older 0.7 v07()) do not re-run the 0.8 version of v07(), which is what clears Admin_Conductor_templates + PM_Seed to force the reseed that introduces the admin-updates template.
Symptom when triggered: /admin/updates/ returns HTTP 200 with an empty body, because no template matches the new admin-updates content type.
I hit this on the Vultr canary (Copperline) before I added a workaround. The bulk upgrade script I then wrote clears the same options preemptively as part of every site’s upgrade, so I can’t say how many of the 105 sites would have reproduced it without the workaround — but every site in the ecosystem had update_07=1 already, so the condition was universal.
Manual recovery (or the shape of the script’s preemptive step):
DELETE FROM {prefix}options WHERE name IN (
'Admin_Conductor_instances','Admin_Conductor_blocks','Admin_Conductor_head',
'Admin_Conductor_templates','Admin_Conductor_css_vars','Admin_Conductor_css',
'Admin_Conductor_css_editor','Admin_Conductor_design','Admin_Conductor_display',
'Admin_Conductor_data_seed','PM_Seed'
);
Suggestion: have v08() clear the same options as belt-and-suspenders, so sites that missed the new v07() get caught by 0.8’s own migration.
3. v08() bleed keys only reach Attention + Admin_Conductor
v08() threads bleed-bg-lightness, bleed-text-lightness, bleed-saturation into Attention_design and Admin_Conductor_design. Sites running a custom frontend theme (our Focus theme on oej.elmspark.com) never get those keys. The migration finishes, update_08=1 is set, but Bleed CSS variables resolve to nothing until the custom theme’s _design option is updated manually.
If this is intentional (v08 leaves custom themes to their authors), the upgrade guide should say so explicitly — integrators with custom themes will need to update each custom _design option themselves. If it’s an oversight, the fix is to expand the foreach (['Attention', 'Admin_Conductor'] ...) loop.
Update-server protocol (SDK notes)
Implementing a 0.8-compatible update server from scratch, these are the non-obvious requirements. Documenting them explicitly will save each integrator the hour I spent reverse-engineering them.
- Response is an indexed JSON array with
asset-idas a field inside each entry. A keyed associative object returns zero usable entries becauselib/updates/remote.php:166-172iterates and re-indexes by$item['asset-id']. Our first server rewrite used the obvious keyed shape and silently returned nothing. - Both
download-urlandchecksumare required for install. Missing either surfaces as the genericrequest_failedfrom the installer, not a specific “missing field” error. Explicit documentation of the required shape would save debug time. - Checksum format is strictly
sha256:{hex}— validated atlib/updates/installer.php:159-172. Any other algorithm or missing prefix fails withchecksum_mismatchonly after the download. - ZIP structure: single top-level folder, contents copied into the existing plugin directory. The installer strips the parent and calls
copy_changed(). ZIPs without a single parent folder fail extraction. download-urlcan be a redirect. The installer runsCURLOPT_FOLLOWLOCATION, so ourupdate.phpreturns a localdownload.php?plugin={slug}URL that 302s to a GitHub Releases signed URL. Two-tier architecture (metadata API + file host) is clean and worth presenting as a supported pattern.request_single()fires at install time (remote.php:145) to fetch a freshdownload-url+checksum. That separation enables time-limited signed URLs, which is the right primitive for commercial plugin distribution.- Endpoint path tolerance:
remote.php:213doesrtrim($url, '/') . '/' . $this->endpoint, so a pluginUpdates:header ofhttps://host.com/update.phpproduces a request toupdate.php/update.php. The code tolerates this via PATH_INFO on Apache/PHP built-in. Either form works; worth documenting the preferred one.
Plugins UI post-install refresh is missing a callback
Same backend install path as Updates UI, same backend result. The gap is in lib/js/plugins.js:34, which calls pm_update_components.init() with no callback, whereas lib/js/themes.js:39 passes function() { pm_themes.refresh(); }. Result: the Plugins UI button animates to “Updated!” but the row keeps the old version and stale Update button until manual refresh.
One-line fix, verified on localhost:
// plugins.js line 34
pm_update_components.init(function() { pm_plugins.refresh(); });
Wrap in an anonymous function rather than passing pm_plugins.refresh as a bare reference — the callback receives (type, button) while pm_plugins.refresh expects (message, id, target), and the mismatch dirties the toast-positioning branch.
Secondary: Updates UI wraps each row in <div data-type="plugin">; Plugins UI buttons have no such ancestor, so button.closest('[data-type]').data('type') resolves to false. Harmless today but worth unifying if future callback logic branches on type.
Environmental friction (integrator’s own problem, flagged for completeness)
None of these are PM’s issues. Any upgrade guide that advises bulk rollouts will see integrators hit them, so worth a paragraph so others know what to clean up first.
- macOS AppleDouble (
._*) files left by Mac-origin SFTP uploads. PM_Seed’scopy()enumerates them. Clean withfind $SITE -name "._*" -deletebefore upgrade. You already warned about Create Clean Archive in a previous release — worth keeping that callout. - Ownership mismatch.
auto-provisionstyle tooling setswww-data:www-data; per-user strict setups (IONOS-style) use per-site users. If a legacy manual chown left files owned by someone other than the PHP-FPM user actually running the pool,chmod()inside theme reseed silently fails with “Operation not permitted”. And on a multi-tenant VPS, different sites may route through different FPM pools with different users — detect the right user by stat-ing the existingconfig.phpor reading the pool’suser =directive; don’t assumewww-data. - Debris at
user-content/plugins/root. A botched historical deploy had put a looseplugin.php+class-ep-suite.php+css/,includes/,js/directly underuser-content/plugins/instead of inside a plugin subdirectory. PM’s plugin loader picked them up and triggered “Cannot declare trait EP_Suite” fatals. Delete loose files at that root before upgrade. - OPcache caches fatals. After fixing the underlying cause of any fatal, PHP-FPM kept serving the cached bytecode with the old error.
systemctl restart php8.3-fpm(or waiting foropcache.revalidate_freq) is required to see the fix take effect. - First admin hit after migration sometimes 500s. Heavy operation (migration + reseed + CSS recompile). Second hit always succeeded. Worth a one-liner in the upgrade guide: “if admin returns 500 right after upgrading, refresh once.”
Upgrade pattern that worked at scale
For integrators upgrading bulk site counts, this pattern worked reliably across Vultr, Linode, DO, and IONOS VPS:
# Stage 0.8 once on the box
rsync -a PAGEMOTOR_08_SRC/ /root/pagemotor-08/
# Per-site (driven by a script; FPM_USER detected via stat on config.php):
# 1. Save active plugins list, deactivate all except the one needed for provisioning
# (in our case EP_Provisioning must stay active through the upgrade window)
# 2. find SITE -name "._*" -delete
# 3. Clean any debris at user-content/plugins/ root
# 4. rsync -a /root/pagemotor-08/ SITE/
# Safe without excludes: the 0.8 release archive contains no config.php and no
# user-content/ directory, so an overwriting rsync cannot touch site config or
# user-installed plugins/themes. (Worth stating this property of the 0.8
# archive explicitly in the upgrade guide.)
# 5. FPM_USER=$(stat -c '%U:%G' SITE/config.php); chown -R $FPM_USER SITE
# 6. DELETE FROM options WHERE name LIKE 'Admin_Conductor_%' OR name='PM_Seed' OR name='updates'
# 7. curl SITE/admin/ (triggers v08 + reseed; retry once if it returns 500)
# 8. Restore active plugins list
Why keep one plugin active (step 1): the admin hit during step 7 runs the full PM init path. Sites with cross-plugin provisioning dependencies (ours trigger provisioning checks on admin load) benefit from having their provisioning plugin active during the migration. Pure safety-first would deactivate everything.
Why clear Admin_Conductor_* + PM_Seed preemptively (step 6): works around finding #2 above without having to diagnose it per-site.
AI Design Conversion Skill 1.2
Deployed to all central installs we run Claude Code from: testvultr, docentral, licentral, buildtheweb, testrig, plus local ~/.claude/skills/. The skill’s requires: ">=0.8" header correctly sequences this step after the core upgrade — the skill’s output format (HEXA color values, Bleed CSS variables) matches 0.8’s Design Options and CSS variable pipeline, so it does not belong on 0.7 installs.
Summary
- 105 live sites upgraded to 0.8 across 6 environments. Auto-update flow verified on 3 canaries, upgrade path verified on all 105.
- 3 PM 0.8 issues surfaced with inline fixes.
- Update-server protocol documented from an integrator perspective, input for the SDK guide.
- Plugins UI one-line JS fix verified on localhost.
- Skill 1.2 deployed in sequence with 0.8 on all central servers.
Happy to discuss any of the above in more depth.
7. admin-updates template missing on IONOS shared sites
Same root cause as finding #2 (the update_07 guard-replay gap), different host. Sites upgraded via the VPS bulk script had Admin_Conductor_* options cleared preemptively — reseed ran, admin-updates landed in templates. Sites upgraded via the lighter IONOS shared SFTP path (updates cache cleared only) never triggered that reseed. Symptom: clicking “Check for Updates” on the admin home loads a blank page because there is no template to render the admin-updates content type.
Confirmed on 3 of 4 IONOS shared sites (epemail.elmspark.com, demo.elmspark.com, sun.elmspark.com). Fix: clear the Admin_Conductor_* options and PM_Seed, hit /admin/ to reseed. Going forward: always include the Admin_Conductor options wipe in the upgrade step for any site type — it is idempotent and safe.
PageMotor 0.8.1b Rollout — 2026-04-14
Following the 0.8 rollout, PageMotor 0.8.1b was released and deployed across the same ecosystem. Key pre-flight findings that shaped the run:
- Migration guard is still
update_08— no new migration step in 0.8.1b. The upgrade is a pure file replacement with no DB changes required. opcache_invalidate()is now guarded in 0.8.1b — finding #1 from the 0.8 session was fixed upstream. No patch needed before deploying to shared hosting.- Admin_Conductor wipe no longer required going 0.8 → 0.8.1b — all sites already have the
admin-updatestemplate from the 0.8 reseed. Only the updates cache is cleared.
Scope
| Environment | Sites | From | Result |
|---|---|---|---|
| Localhost dev | 1 | 0.8 | 0.8.1b ✓ |
| Vultr VPS (nip.io) | 51 | 0.8 | 0.8.1b ✓ |
| DigitalOcean VPS (nip.io) | 13 | 0.8 | 0.8.1b ✓ |
| Linode VPS (nip.io) | 28 | 0.8 | 0.8.1b ✓ |
| IONOS VPS (real domains) | 10 | 0.8 | 0.8.1b ✓ |
| IONOS shared hosting (SFTP) | 4 | 0.8 | 0.8.1b ✓ |
| South Africa VPS (domains.co.za) | 3 | 0.7.2 | 0.8.1b ✓ |
EP Suite plugin registry
40 of 55 EP Suite plugins are now in the public update registry with SHA-256 checksums and download URLs. Any PM 0.8+ site will see update notifications in Admin → Updates automatically. The remaining 15 are works in progress not yet in the public registry.
Post-rollout issue surfaced (finding #7)
See finding #7 above — the admin-updates template was missing on IONOS shared sites that came through the lighter upgrade path. Fixed on all 4 affected sites after the 0.8.1b rollout completed.
Upgrade pattern for future releases
The proven per-site pattern for VPS hosts (stage once per host, loop per site):
# Stage once on the VPS
rsync -a PAGEMOTOR_SOURCE/ /root/pagemotor-new/
# Per-site loop:
# 1. Save plugins, keep only EP_Provisioning active
# 2. find SITE -name "._*" -delete (macOS metadata cleanup)
# 3. rsync -a /root/pagemotor-new/ SITE/ (safe — no config.php or user-content in source)
# 4. chown -R $(stat -c %U:%G SITE/config.php) SITE
# 5. DELETE updates cache from options table
# 6. curl SITE/admin/ (warms cache; retry once if 500)
# 7. Restore full plugin list
For IONOS shared hosting (SFTP-only): ZIP the source, upload with a PHP unzipper, run a PHP fixer for the DB steps, hit /admin/, restore plugins, delete all scratch files.
EP Suite Plugin Registry
56 plugins in the EP Suite. 40 are in the public update registry. Sites on PM 0.8+ check updates.elmspark.com automatically — any plugin with an auto-update checksum will surface in Admin → Updates when a new version ships.
40 Published Plugins
| Plugin | Version | Status | Download |
|---|---|---|---|
| EP Affiliate | v0.1.2 | Registered | Download |
| EP Analytics | v1.2.2 | Auto-update ✓ | Download |
| EP Audit Log | v1.0 | Registered | Download |
| EP Booking | v1.0.29 | Auto-update ✓ | Download |
| EP Booking — Zoom | v1.0.7 | Registered | Download |
| EP Breadcrumbs | v1.2 | Possibly superseded by PM core | Download |
| EP Bunny Fonts | v1.0.4 | Registered | Download |
| EP Comments | v1.1.2 | Auto-update ✓ | Download |
| EP Connect | v1.0.4 | Auto-update ✓ | Download |
| EP Diagnostics | v1.0.10 | Auto-update ✓ | Download |
| EP Ecommerce | v0.1.16 | Auto-update ✓ | Download |
| EP Ecommerce — PayPal | v0.1.4 | Registered | Download |
| EP Ecommerce — Products | v0.1.8 | Auto-update ✓ | Download |
| EP Ecommerce — Stripe | v0.1.8 | Auto-update ✓ | Download |
| EP Ecommerce — Subscriptions | v0.2.5 | Registered | Download |
| EP Editor | v0.1.3 | Registered | Download |
| EP Email | v1.9.29 | Auto-update ✓ | Download |
| EP Email — AI Reply | v1.0.1 | Registered | Download |
| EP Email — Advanced Forms | v1.0.1 | Registered | Download |
| EP Email — File Uploads | v1.0.12 | Possibly superseded by PM core | Download |
| EP Email — Quiz | v1.0.4 | Registered | Download |
| EP FAQ | v1.0.1 | Auto-update ✓ | Download |
| EP GDPR | v1.1.22 | Auto-update ✓ | Download |
| EP Gallery | v1.0.7 | Auto-update ✓ | Download |
| EP Maintenance | v1.0.2 | Auto-update ✓ | Download |
| EP Newsletter | v1.1.22 | Auto-update ✓ | Download |
| EP Newsletter — SendGrid | v1.0.8 | Registered | Download |
| EP Passkeys | v0.3 | Dev-only | — |
| EP Password Reset | v1.0.1 | Dev-only | — |
| EP Redirects | v1.0.1 | Possibly superseded by PM core | Download |
| EP Reviews | v1.0.5 | Auto-update ✓ | Download |
| EP SEO | v1.1 | Possibly superseded by PM core | Download |
| EP Scheduled Content | v1.1 | Registered | Download |
| EP Search | v1.0 | Registered | Download |
| EP Sitemap | v1.0 | Registered | Download |
| EP Social Share | v1.0 | Registered | Download |
| EP Support | v1.1.15 | Auto-update ✓ | Download |
| EP Testimonials | v1.0.5 | Auto-update ✓ | Download |
| EP Tracking | v0.1.1 | Registered | Download |
| EP Txt Files | v1.0.7 | Auto-update ✓ | Download |
Auto-update ✓ — in registry with SHA-256 checksum; update will appear in Admin → Updates automatically.
Registered — in registry with download URL; checksum pending (admin notification not yet active for these).
Possibly superseded — functionality may now be handled by PageMotor core in 0.8+; under review.
Dev-only — internal/private; no public release.
16 Work in Progress (not yet in registry)
| Plugin | Version | Notes |
|---|---|---|
| Discovery AI | v0.3 | Central site / provisioning workflow |
| EP Assistant | v1.0.0 | Web-based AI assistant |
| EP Business Planner | v0.1 | Early stage |
| EP Cards | v1.0.4 | Native cards + Focus Cards migration |
| EP Cards Importer | v1.0.1 | Companion to EP Cards |
| EP Courses | v0.4.1 | Gateway integration planned for PM 0.8+ |
| EP Email ElmsPark | v1.0.0 | Internal/ElmsPark-specific |
| EP Email Inbox | v1.0 | Early stage |
| EP MCP Bridge | v0.17.2 | MCP integration layer |
| EP Membership | v0.4 | Gateway integration planned for PM 0.8+ |
| EP Provisioning | v1.1 | Internal provisioning; always active during upgrades |
| EP RSS | v1.0 | Early stage |
| EP SendIt | v1.0.0 | Early stage |
| EP Sidebar | v1.5.0 | PM built-in sidebar layout activator |
| EP Suite (base class) | v1.0 | Shared trait; not a standalone plugin |
| EP Voice Messages | v1.0 | Early stage |