Skip to content

fix: close the v7 migration gate on pre-release builds#576

Merged
galbus merged 1 commit into
mainfrom
claude/great-williamson-ea618d
Jul 19, 2026
Merged

fix: close the v7 migration gate on pre-release builds#576
galbus merged 1 commit into
mainfrom
claude/great-williamson-ea618d

Conversation

@galbus

@galbus galbus commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Problem

Updater::run() runs on every request (via Plugin::init() at plugin-include time). The v7 migration block is gated on version_compare( $version, '7.0.0', '<' ), but the shipped build is a pre-release — currently 7.0.0-beta.1. run() writes that same suffixed string back into beyondwords_version, and every pre-release suffix compares < '7.0.0', so the gate never closed.

The result, on every single page load (anonymous front-end traffic included):

  • delete_deprecated_options() — 39 delete_option()/delete_site_option() calls; WP core's delete_option() issues a SELECT autoload ... even when the option doesn't exist.
  • migrate_disabled_to_embed_none() — a WP_Query with an unindexed meta_value postmeta JOIN (WordPress.DB.SlowDBQuery pattern).
  • migrate_preselect_format() — an extra option read.

That's ~40 uncached primary-DB SELECTs plus a slow postmeta scan per request — a direct WordPress VIP performance violation (worse on large publisher DBs, and 39 network-meta lookups per request on multisite).

version_compare('7.0.0-beta.1', '7.0.0', '<') === true — verified with php -r. (The bug was originally reported against 7.0.0-dev-2.0; identical defect — any -dev/-beta/-rc suffix triggers it.)

Fix

Add a version-equality early return at the top of run():

if ( BEYONDWORDS__PLUGIN_VERSION === $version ) {
    return;
}

Once a build has booted once and recorded its own version, the next request bails before touching the database.

Chosen over the alternative one-shot beyondwords_migrated_7_0_0 flag because it's strictly better: it short-circuits the entire migration chain on the hot path (not just the v7 block), adds no new option, and self-heals every future pre-release gate. Migrations still run exactly once per version change, and all three v7 migrations are idempotent, so the rare beta→beta re-run is safe.

Also corrected three now-inaccurate comments: the file header, the run() docblock, and the migrate_preselect_format() docblock that explicitly claimed the gate "fires on every load".

No phpcs:ignore added; the pre-existing one on the meta_query is untouched (that query now runs only once per upgrade, as a migration should).

Tests

Two new UpdaterTest regression tests:

  • run_is_a_noop_once_the_recorded_version_matches_this_build — proves the gate closes (a post's legacy beyondwords_disabled flag is left untouched).
  • run_migrates_and_records_the_version_when_outdated — proves the real upgrade path still migrates and normalises the stored version.

Verification

  • phpcs (WordPress-VIP-Go standard): clean, zero errors/warnings on class-updater.php.
  • PHPUnit UpdaterTest: 8/8 pass, 29 assertions (6 pre-existing + 2 new).
  • Cypress suite not run (per repo guidance).

🤖 Generated with Claude Code

Updater::run() runs on every request via Plugin::init(). The v7 block is
gated on version_compare( $version, '7.0.0', '<' ), but the shipped build
is a pre-release (currently '7.0.0-beta.1'). run() writes that suffixed
string back into beyondwords_version, and every pre-release suffix
compares < '7.0.0', so the gate never closed: ~40 uncached DB deletes
plus a slow beyondwords_disabled postmeta JOIN ran on every page load,
anonymous front-end traffic included — a WordPress VIP performance
violation.

Add a version-equality early return at the top of run(): once a build has
recorded its own version, the next request bails before any DB work. This
short-circuits the entire migration chain (not just the v7 block), needs
no new option, and self-heals every future pre-release gate. Migrations
still run once per version change and all three v7 migrations are
idempotent, so the rare beta->beta re-run is safe.

Also correct three now-inaccurate comments (file header, run() docblock,
and the migrate_preselect_format() docblock that claimed the gate fires
on every load).

Add two UpdaterTest regression tests: run() is a no-op once the recorded
version matches this build, and still migrates + records the version when
outdated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

✅ WordPress Plugin Check Report

✅ Status: Passed

📊 Report

All checks passed! No errors or warnings found.


🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

@galbus
galbus marked this pull request as ready for review July 19, 2026 07:03
@galbus
galbus requested a review from gouravkhunger July 19, 2026 07:03
@galbus
galbus merged commit 5c24aaf into main Jul 19, 2026
12 checks passed
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.

2 participants