MHM Rentiva v4.27.4–v4.27.6: Migration Lane Architecture, License Slug Forwarding, WordPress.org Polish
A retrospective covering three patch releases shipped in succession on 2026-04-23: a critical migration architecture fix (v4.27.4), per-product license binding compatibility (v4.27.5), and WordPress.org submission readiness (v4.27.6). Backfilling these blog posts now in the wake of the v4.30.x security sprint.
This post is the record of a release that shipped before 6.0.0, which renamed the plugin's post types, taxonomies, options, meta keys and hooks. The identifiers below are the names those versions actually used, and they are kept that way on purpose. For the current names — and a conversion table — see the 6.0.7 release post.
v4.27.4 — Migration Lane Architecture Fix (CRITICAL)
The v4.27.1 (i18n locale leak) and v4.27.2 (Settings Testing pollution) data-cleanup migrations never actually ran on upgraded sites — the regression cleared during v4.27.0 prep had quietly broken the migration trigger.
Root cause
plugins_loaded migration callback used a single short-circuit:
if ( $stored_version === MHM_RENTIVA_VERSION ) {
return; // skip ALL migrations
}
But mhm_rentiva_single_site_activation() (which fires BEFORE plugins_loaded on any ZIP-replace upgrade path) had already stamped the new version into the option. By the time plugins_loaded fired, $stored === $current was true → all flag-gated cleanups got skipped forever.
Live evidence at mhmrentiva.com: Brand Name displayed as 1, currency stats rendered as 0,00 1 instead of 0,00 ₺ (because the v4.27.2 pollution migration never executed).
Fix — two-lane architecture
plugins_loaded callback split into two independent lanes:
- Lane A (schema drift, version-gated): runs only when
$stored !== $current. Idempotent across multiple loads of the same version. - Lane B (flag-gated data cleanups): runs on every request, but each migration has its own
mhm_rentiva_<flag>option that flips after successful run. Genuinely idempotent.
Both lanes have their own error containment so a failure in Lane B never blocks Lane A or vice versa.
Test coverage
tests/Migration/MigrationLaneIndependenceTest.php — 3 new tests, 8 assertions guarding the split.
Empirical verification
Chrome DevTools MCP browser tour of mhmrentiva.com after deployment: 0 duplicate notices, stats display 0,00 ₺ (U+20BA), Brand Name reads "MHM Rentiva". Manual one-time Reset to Defaults across 11 admin tabs cleared residual state from the period when migrations weren't running.
v4.27.5 — License Client Per-Product Slug Forwarding
Pairs with mhm-license-server v1.8.0+ which introduced per-product license binding. Before v4.27.5, a customer could buy a cheap MHM Currency Switcher Monthly key and use it to activate Rentiva Pro — server had no way to enforce that the license was issued for a specific product.
Change
LicenseManager::activate() and validate() request bodies now include product_slug => 'mhm-rentiva'. The server checks this against the license row's stored slug and rejects mismatches with a product_mismatch error.
Bonus fix
Additional Services admin page rendered two Documentation buttons — the global all_admin_notices docs hook + the page's own header both fired. The hook now skips the vehicle_addon screen; the page header's button is the single source.
v4.27.6 — WordPress.org Submission Polish
Plugin Check (PCP) compliance for WordPress.org plugin directory submission: 1 ERROR + 115 WARNING → 0 ERROR + 61 WARNING. The remaining warnings are all false positives caused by Plugin Check not reading the project's phpcs.xml prefix configuration.
Concrete fixes
unlink()→wp_delete_file()in the demo image importer (the only real ERROR).- Reasoned
phpcs:ignorecomments for warnings the project's ruleset already excludes (auditerror_log()calls,mhm_rentiva_prefixed hooks Plugin Check refuses to recognise, acceptedmeta_query/post__not_inperformance tradeoffs, ledger queries with core-controlled table names bound through$wpdb->prepare()). $pending_messagestemplate-scope variable →$mhm_rentiva_pending_messagesto honour Plugin Check's prefix convention.readme.txtChangelog section trimmed to fit WP.org's 5000-character limit; older releases linked to GitHub.changelog-tr.jsonbackfill for v4.27.5 + new v4.27.6 entry.
No functional change
740/740 PHPUnit still passes. PHPCS clean. The plugin behaves identically — only the Plugin Check noise was reduced.
✅ Verification across all three
| Check | v4.27.4 | v4.27.5 | v4.27.6 |
|---|---|---|---|
| PHPUnit | 740/740, 2642 assertions, 6 skipped | unchanged | unchanged |
| PHPCS | 0 / 0 | 0 / 0 | 0 / 0 |
| Plugin Check ERROR | unchanged | unchanged | 1 → 0 |
🎓 Lessons captured (memory)
feedback_migration_lane_architecture.md— when activation hooks stamp version BEFOREplugins_loaded, the version gate breaks all flag-gated migrations. Always split version-drift and data-cleanup lanes.feedback_empirical_dom_verification.md— DOM-level verification through Chrome DevTools MCP is the only way to catch runtime-DB-state bugs that 100% PHPUnit + 0 PHPCS happily ship.
🔜 Next
The v4.27.x umbrella closed here. Next major was v4.28.0 (Popular Routes) — but the security audit of the cross-product attack surface re-prioritised the roadmap into the License Security Hardening sprint (v4.30.x), shipped 2026-04-24/25.
GitHub Releases: v4.27.4 · v4.27.5 · v4.27.6
