Prepare 2026-08-18 release - #2639
Conversation
WordPress 7.1 is imminent, so update the readme header for each of the ten plugins published to the plugin directory ahead of the upcoming releases. The unreleased od-* plugins have no readme.txt and are unaffected, and the "Requires at least" floor is left at 6.9. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bumping a plugin version previously required an open, dated release milestone titled "<slug> <version>". That is a lot of ceremony for a release that only carries changes which are already merged, so add two ways to bump without one: * --increment <major|minor|patch|prerelease> derives the target version from the plugin's current "Stable tag". * --set-version <version> sets an explicit version for a single plugin. In either mode the plugins are named as positional arguments, matching the calling convention of generate-pending-release-diffs.sh. The list is required rather than inferred because the appropriate increment level differs per plugin, so no single auto-detected set would be correct. Passing --all targets every plugin for the rare case where one level does apply to all of them. The "prerelease" level increments the trailing number of the prerelease component, so 1.0.0-beta5 becomes 1.0.0-beta6. This is hand-rolled rather than delegated to semver, which yields 1.0.0-beta5.0 because it treats "beta5" as a single alphanumeric identifier. Incrementing a version that has a prerelease component by major, minor, or patch is refused, since that would silently graduate a beta to a stable release. All target versions are resolved before any file is written, so that guard aborts the whole run rather than leaving some plugins bumped and others not. The milestone-based behaviour is unchanged when neither new option is passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…n-rules, web-worker-offloading Run via: ```bash npm run bump-versions -- --increment minor auto-sizes dominant-color-images speculation-rules web-worker-offloading ```
Run via: ```bash npm run bump-versions -- --increment prerelease embed-optimizer image-prioritizer ```
Fill in the empty changelog entries that bump-versions stubbed out for the seven plugins being released. The entries are written by hand because the changelog and prepare-release-notes commands derive their content from a release milestone, and these releases deliberately skip milestones. The bulk of each entry is the same across all seven plugins, since what is pending is a single cross-cutting sweep: strict types, native property types, and the raised WordPress 6.9 and PHP 7.4 minimums. Only three plugins carry anything beyond that: * Optimization Detective moved the URL Metrics storage HMAC validation into the REST endpoint callback and dropped its deprecated constants. * Speculative Loading hardened the JSON encoding of its inline scripts. * Image Prioritizer fixed TypeScript 6 type errors in the video lazy-loading script. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The script could report a plugin as having no pending changes when it actually had some. Bumping "Tested up to: 7.0" to "7.1" replaces a line with one of exactly the same length, and the working copy is reused between runs, so a previous run's rsync leaves the build file's mtime on the copy. Both rsync's default quick check and SVN's own stat cache compare size and mtime rather than content, so each concludes the file is unchanged and "svn status" comes back clean. The failure is intermittent, since it only bites once a prior run has left a matching mtime behind, and it is silent in the worst way: the affected plugin is reported under a "No changes." note, which reads as confirmation that there is nothing to release. Pass -c so rsync compares checksums, and --no-times so the files it copies get a fresh mtime that invalidates SVN's stat cache. Both are needed; -c alone still leaves SVN unable to see the change. Verified against a cold checkout and a warm one, with identical results. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## release/2026-08-18 #2639 +/- ##
===================================================
Coverage 70.35% 70.35%
===================================================
Files 91 91
Lines 7867 7867
===================================================
Hits 5535 5535
Misses 2332 2332
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
The two web-vitals bundles under build/ accounted for 35% of the whole diff, 42 KB out of 124 KB, for what amounts to a library upgrade. They are minified despite not carrying a .min.js suffix, so the existing *.min.js exclusion misses them, and each is a single line, meaning any change at all renders as a whole-file rewrite. Exclude build/*.js, which brings the diff down from 127 KB to 85 KB. The sibling build/*.asset.php is deliberately left in, since its 'version' is the compact signal that the bundled library changed: two lines showing 5.1.0 becoming 6.1.0, in place of 42 KB of unreadable bundle. That signal turned out to be worth keeping. It surfaced that web-vitals is going from 5.1.0 to 6.1.0, across a major release, which was missing from Optimization Detective's changelog entry and is now added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Excluding generated assets from the rsync kept the diff readable but blinded "svn status" to them, which is the wrong trade: status is the view that answers which files are being added, removed, and modified, and it should be complete. Worse, the exclusion also protected those files from --delete, so a stray minified asset dropped into an already-tracked directory was reported nowhere at all. Only one landing in a brand new directory showed up, and then merely as its parent directory, with no indication of what was inside. Copy everything instead, and filter "svn diff" so that generated files keep their header but have their contents replaced with a placeholder. They stay visible as changed without dragging in tens of KB of unreadable bundle: the whole report goes from 127 KB to 88 KB. The *.asset.php files are left intact, since their 'version' is the readable signal that a bundled library changed. Also expand unversioned directories in the status output, so that a batch of added files is listed file by file rather than collapsing into its parent. This supersedes the build/*.js exclusion added in the previous commit, whose carve-out for *.asset.php is preserved here by the same reasoning. Both edge cases are now covered: a stray min file in a tracked directory shows as "? stray.min.js", and a new directory lists its contents. It immediately turned up a real pending change that the exclusion had been hiding, a modified view-transitions .min.css, which on inspection is only a minifier emitting #0000 where it used to emit transparent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
🤖 Comment by Claude Opus 5
|
|
🤖 Comment by Claude Opus 5
|
|
🤖 Comment by Claude Opus 5
|
|
🤖 Comment by Claude Opus 5
|
|
🤖 Comment by Claude Opus 5
|
|
🤖 Comment by Claude Opus 5
|
|
🤖 Comment by Claude Opus 5
|
|
🤖 Comment by Claude Opus 5
|
|
🤖 Comment by Claude Opus 5
|
|
🤖 Comment by Claude Opus 5
|
web-vitals began publishing source maps in 6.0.0, and emits a sourceMappingURL comment alongside them. That reference is correct inside the package, where the map sits next to the bundle, but the webpack config copies only the bundles out of node_modules. The upgrade would therefore have shipped two files pointing at maps that do not exist, making browsers request a URL that 404s whenever devtools is open. Copy the maps too, rather than stripping the comment. Upstream publishes them deliberately for exactly this case, and Optimization Detective is a plugin whose entire purpose is measuring real user performance, so readable web-vitals frames are worth having when debugging it or an extension built on it. Both maps carry sourcesContent, so they are self-contained and need nothing else fetched. The Gutenberg plugin ships source maps for the same reason; WordPress core does not, but core is not distributed as a plugin. The cost is 49 KB on a 106 KB zip, and nothing at runtime: browsers only request a map once devtools is open, so no visitor ever pays for it. Two renames had to be handled, since web-vitals.attribution.js is copied in as web-vitals-attribution.js. Its map is now named to match, the sourceMappingURL comment in the bundle is repointed at the new name, and the map's own "file" field is repointed at the renamed bundle. Both transformers are written as factories over a file name rather than hardcoded, since any vendored bundle renamed on the way in needs the same treatment. Note that the two added .map files are visible in the pending release diff as untracked additions only because generated assets are now copied and suppressed rather than excluded. Under the previous exclusion they would have been added to the plugin without appearing anywhere in the report. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Quick way to install the above plugin builds in Bash via WP-CLI: urls=(
"https://github.com/user-attachments/files/31340635/auto-sizes.zip"
"https://github.com/user-attachments/files/31340637/dominant-color-images.zip"
"https://github.com/user-attachments/files/31340638/embed-optimizer.zip"
"https://github.com/user-attachments/files/31340639/image-prioritizer.zip"
"https://github.com/user-attachments/files/31340640/optimization-detective.zip"
"https://github.com/user-attachments/files/31340641/performance-lab.zip"
"https://github.com/user-attachments/files/31340642/speculation-rules.zip"
"https://github.com/user-attachments/files/31340643/view-transitions.zip"
"https://github.com/user-attachments/files/31340644/web-worker-offloading.zip"
"https://github.com/user-attachments/files/31340645/webp-uploads.zip"
)
for url in "${urls[@]}"; do
wp plugin install --force "$url"
done |
|
Smoke-tested the following plugins, and they are working correctly on WordPress 7.1:
|
|
I smoke tested:
I did not test Web Worker Offloading as this is being sunset. |
|
🤖 Comment by Claude Opus 5 web-vitals 5.1.0 → 6.1.0: back-compat review and runtime verificationOptimization Detective's bundled web-vitals library crosses a major version in this release, so the upgrade was reviewed against the v6 upgrade guide and then verified at runtime. No back-compat breakage was found for Optimization Detective or its extensions. Breaking changes assessedThe v6 guide lists four changes relevant to consumers. Soft Navigation reporting. This was the main concern, since a soft navigation could produce an LCP belonging to a different URL than the one a URL Metric is being built for. It turns out to be opt-in through the
Types exported as explicit types. The guide notes this does not cover the functions, and Because the jump is 5.1.0 to 6.1.0 rather than a single major, the intermediate 5.2.0 and 5.3.0 entries were swept as well. Two touch these code paths and both are safe: using Runtime verificationStatic review cannot show that metrics are still actually collected, and the reporting timing did change, so collection was exercised against a local WordPress 7.1 site running the branch builds. Both bundles were covered, since
Each stored URL Metric has exactly one element flagged |
The release notes were the last step still requiring milestones, and it failed quietly rather than loudly. Only one of the seven plugins being released has a dated milestone, so prepare-release-notes produced notes for that one alone, and create-draft-release checked only that the notes file was non-empty. A draft release would therefore have been created covering one plugin out of seven, with nothing to indicate the other six were missing. The milestone was never the source of the content: getReadmeChangelogEntry() already reads each plugin's changelog from its readme.txt, and the milestone only decided which plugins to include. So accept plugin slugs as positional arguments and skip the lookup entirely when any are given, matching what bump-versions already does. There is deliberately no option to select every plugin here, unlike bump-versions where one exists but is rarely useful. A plugin that is not being released still has a changelog entry for its current stable tag, so including it would repeat an already-published entry in the new release notes. When plugins are named explicitly, a failure on any of them is now fatal and nothing at all is written, rather than the previous per-plugin tolerance emitting a subset. Asking for specific plugins and silently getting fewer is the exact failure this commit exists to prevent. Milestone selection keeps the old behaviour, since there the set was never asserted by hand. create-draft-release forwards any slugs through, then verifies each one is present in the generated notes before creating the draft. Where no slugs are given it now lists which plugins the notes actually cover, so an omission is visible rather than implied by silence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Summary
Prepares the 2026-08-18 releases. Seven plugins are being released; the pending changes are almost entirely one cross-cutting sweep that had accumulated on
trunkwithout a release:declare( strict_types = 1 )in every PHP file, native property types, and the raised WordPress 6.9 / PHP 7.4 minimums.Performance Lab, View Transitions, and Modern Image Formats are deliberately not version-bumped. Their pending changes do not warrant a release: for Performance Lab and Modern Image Formats it is only the
Tested up to: 7.1bump, and View Transitions additionally has a rebuiltcss/view-transition-animation-wipe.min.csswhose only difference is the minifier emitting#0000where it previously emittedtransparent(the unminified source is byte-identical to what is published). TheTested up tobump reaches the directory through thebump-wordpress-tested-up-toworkflow instead, which updatesreadme.txtin bothtrunkand the stable tag without a release.The pending diff is posted as one comment per plugin below, rather than as a single comment, since the full report is 88 KB and exceeds GitHub's 65,536 character comment limit on its own. A separate comment covers the back-compat review and runtime verification of the web-vitals 5.1.0 → 6.1.0 upgrade in Optimization Detective.
Since the release date has moved on from the branch name, the plan is to merge this into
release/2026-08-18, branchrelease/<publish date>from it, and tag from there. This follows what was done when the 2026-06-19 release slipped:release/2026-06-30was cut fromrelease/2026-06-19, tagged, and the earlier branch was left untagged.Previous release preparation PR: #2619
Relevant technical choices
Minor rather than patch. The deciding factor is that all seven plugins raise their minimum requirements (
Requires at least6.6/6.8 → 6.9,Requires PHP7.2 → 7.4). Users on the dropped versions stop being offered the update, which is a compatibility break that should be visible in the version number rather than buried in a patch.declare( strict_types = 1 )reinforces this, since it is a runtime behaviour change and not a refactor: a value that was previously coerced can now raise aTypeError. The two1.0.0-beta*plugins take a prerelease bump instead, since a minor bump would graduate them to stable.Releasing without milestones. These releases carry only changes that were already merged, so no release milestones were created. Three separate commands turned out to derive their input from a dated milestone, and each needed an escape hatch:
bump-versionsgains--increment <major|minor|patch|prerelease>and--set-version <version>, which derive the target from the plugin's currentStable taginstead. Plugins are named as positional arguments, matchinggenerate-pending-release-diffs.sh. The list is required rather than inferred, because the correct increment level differs per plugin, so no single auto-detected set would be right. Incrementing a prerelease version by major/minor/patch is refused outright, and all target versions are resolved before anything is written so that guard aborts the run rather than leaving a partial bump.prepare-release-noteslikewise accepts plugin slugs positionally and skips the milestone lookup when given any. The milestone was never the source of the content —getReadmeChangelogEntry()already reads each plugin's changelog from itsreadme.txt— it only decided which plugins to include. This mattered more than it first appears: only one of the seven plugins has a dated milestone, andcreate-draft-releasechecked only that the notes file was non-empty, so a draft release would have been created covering one plugin out of seven with nothing to indicate the other six were missing.create-draft-releasenow forwards slugs through and verifies each is present in the generated notes; without slugs it lists which plugins the notes actually cover.npm run readmeis also milestone-bound.Unlike
bump-versions,prepare-release-notesdeliberately has no option to select every plugin. A plugin that is not being released still has a changelog entry for its current stable tag, so including it would repeat an already-published entry in the new release notes. Where plugins are named explicitly, a failure on any of them is fatal and nothing is written at all, since asking for specific plugins and quietly getting fewer is the failure being guarded against.npm run sincewas a no-op this cycle: there are non.e.x.tmarkers anywhere inplugins/, which is consistent with a sweep that adds types rather than APIs.Two fixes to
generate-pending-release-diffs.sh. Both matter because this is the script the release is verified against.It could report a plugin as having no pending changes when it had some.
Tested up to: 7.0→7.1is a same-length edit, and the working copies under/tmp/stable-svnare reused across runs, so a previous run'srsync -aleaves the build file's mtime on the copy while a latersvn revertrestores the content without restoring the mtime. Both rsync's default quick check and SVN's own stat cache compare size and mtime rather than content, so each concluded the file was unchanged andsvn statuscame back clean. Fixed with-cand--no-times; both are required, as-calone leaves SVN unable to see the change.Generated assets were excluded from the copy, which blinded
svn statusto them. That is the wrong trade:svn statusis the view that answers which files are being added, removed, and modified, and it should be complete. The exclusion also protected those files from--delete, so a stray minified asset dropped into an already-tracked directory was reported nowhere at all, and one landing in a new directory showed up only as its parent directory. Everything is now copied, andsvn diffis filtered so generated files keep their header but have their contents replaced with(Built file content suppressed.). The report drops from 127 KB to 88 KB whilesvn statusstays complete. Unversioned directories are also expanded in the status output so a batch of added files is listed individually.The
*.asset.phpfiles are deliberately left unsuppressed, since theirversionis the readable signal that a bundled library changed. That carve-out earned its place immediately: it surfaced that Optimization Detective's bundledweb-vitalsgoes from 5.1.0 to 6.1.0, across a major release, which was missing from its changelog entry and has been added.Source maps for the vendored web-vitals bundles. web-vitals began publishing source maps in 6.0.0 and emits a
sourceMappingURLcomment alongside them. That reference is correct inside the package, where the map sits beside the bundle, but the webpack config copies only the bundles out ofnode_modules. Left alone, this release would have shipped two files pointing at maps that do not exist, so browsers would request a URL that 404s whenever devtools is open.The maps are now copied too, rather than the comment being stripped. Upstream publishes them deliberately for exactly this case, and Optimization Detective is a plugin whose purpose is measuring real user performance, so readable web-vitals frames are worth having when debugging it or an extension built on it. Both maps carry
sourcesContent, so they are self-contained. The cost is 49 KB on a 106 KB zip and nothing at runtime, since a browser only requests a map once devtools is open. For reference, the Gutenberg plugin ships source maps; WordPress core does not, but core is not distributed as a plugin.Two renames had to be handled, since
web-vitals.attribution.jsis copied in asweb-vitals-attribution.js: its map is named to match, thesourceMappingURLin the bundle is repointed at the new name, and the map's ownfilefield is repointed at the renamed bundle. Both transformers are factories over a file name rather than hardcoded, since any vendored bundle renamed on the way in needs the same treatment.Worth noting that the two added
.mapfiles appear in the pending diff as untracked additions only because of the second script fix above. Under the previous exclusion, 223 KB of new files would have been added to the plugin without appearing anywhere in the report.Use of AI Tools
Claude Code (Opus) did the bulk of the work in this PR under close direction: analysing the pending release diff to determine the appropriate bump level per plugin, implementing the
bump-versionsandprepare-release-noteschanges, diagnosing and fixing bothgenerate-pending-release-diffs.shissues, wiring up the source maps, reviewing the web-vitals upgrade for back-compat breakage and verifying collection at runtime, and drafting the changelog entries and this description.Two of the decisions here were corrections of its work by the maintainer. The suppress-rather-than-exclude design in the second script fix replaced an earlier attempt that excluded the files outright and then added a separate file manifest to compensate, which was the wrong shape. Shipping the web-vitals source maps replaced a recommendation to strip the comment instead, which had been argued on a misstated cost: 223 KB uncompressed was quoted as though it were the download cost, when the actual zip delta is 49 KB, and it was framed as a burden on every site when no visitor ever fetches a source map.
Changelog PR attributions were verified against the GitHub API rather than inferred. All output was reviewed before committing.