The WordPress coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. development team builds WordPress! Follow this site for general updates, status reports, and the occasional code debate. There’s lots of ways to contribute:
Found a bugbugA bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority.?Create a ticket in the bug tracker.
First introduced in April as the WordPress Core Dev Environment Toolkit, the WordPress Contributor Toolkit is an experimental desktop app that guides first-time contributors through setting up a full wordpress-develop environment. GitGitGit is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency. Git is easy to learn and has a tiny footprint with lightning fast performance. Most modern plugin and theme development is being done with this version control system.
https://git-scm.com/, Node.js, and npm are still involved under the hood, but the app provides and runs them for you. You do not have to install Git, Node.js, npm, or Docker on your machine.
The first releases concentrated on getting a newcomer from a fresh computer to a running WordPress development siteDevelopment SiteYou can keep a copy of your live site in a separate environment. Maintaining a development site is a good practice that can let you make any changes and test them without affecting the live/production environment.. Version 1.0 carries that work through the contribution itself: link a TRACTracAn open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress.ticketticketCreated for both bug reports and feature development on the bug tracker. and see its details, pull requests, and patches; apply existing work and test it on the running site; create a diff or open a pull request directly from the app; and keep that first site current with the latest trunktrunkA directory in Subversion containing the latest development code in preparation for the next major release cycle. If you are running "trunk", then you are on the latest revision., fetching only what changed instead of repeating a full clone and install.
A first CoreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. contribution, from start to finish
Here is what the workflow looks like now.
1. Create the development environment
Click Create WordPress Core site and choose a folder. The app clones wordpress-develop, installs the dependencies, and runs the first build as one continuous setup. When setup finishes, click Start dev server and finish the wizard to complete the WordPress installation wizard that opens in your browser, and you are ready to start contributing.
The interface draws attention to the next action with a yellow outline, and completed actions produce an accessible confirmation.
2. Link the site to a Trac ticket
You can link a Trac ticket when creating the site or at any time afterwards. Enter its ticket number or paste its Core Trac URLURLA specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org to bring its details, related pull requests, and attached patches into the Toolkit.
The app reads the ticket’s details: summary, status and resolution, type, milestone, component, keywords, and age. These details help you understand the ticket before you start working: how long ago it was opened, whether it is still open or has already been resolved, and which part of WordPress Core it belongs to.
If you do not have a ticket yet, the app links to the good first bugs report on Trac.
Pull requests on WordPress/wordpress-develop that reference the ticket, including their open, closed, or merged state.
PatchpatchA special text file that describes changes to code, by identifying the files and lines which are added, removed, and altered. It may also be referred to as a diff. A patch can be applied to a codebase for testing. files attached directly to Trac.
The newest work is marked Latest, based on the date of the newest commit. You can preview and apply a pull request or Trac patch from the same screen.
4. Apply and test an existing patch
Before applying a patch or a PR, the Toolkit previews the files a patch will touch, warns about collisions with your edits, identifies binary files it cannot apply, and checks whether a dependency install will be needed. “Apply and rebuild” then adds the changes and runs any required install or rebuild steps.
If a patch cannot be applied, the app leaves the checkout unchanged and explains what failed so you can decide what to do next.
Applied work keeps the patch or pull request’s identity attached to it. You can make your own changes on top and later remove that layer, but the app will not let you submit somebody else’s patch as if you wrote it.
5. Edit and debug the change
The site can be opened in a detected editor, another application of your choice, or the system file manager. The build watcher now has its own control, status, and log. Starting the development server starts the watcher too, but stopping the server leaves compile-on-save running if you still need it.
The Toolkit also exposes the parts of a development environment that are useful in Core testing and development:
A direct link to wp-admin beside the site URL.
A live debug.log tab with PHPPHPThe web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher notices, warnings, deprecations, stack traces, and fatal errors.
An embedded terminal for the supported install, build, test, watch, and Grunt commands, using the Node.js runtime bundled with the app.
Adminer for inspecting the site’s SQLite database while the development server is running.
The development server can remain up while a patch is applied or trunk is updated. The app pauses and resumes the build watcher only when an operation needs exclusive access to the build or dependencies.
Open a GitHubGitHubGitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged by the repository owner. https://github.com/ pull request. The app uses GitHub’s device sign-in, creates the fork, commit, and pull request through the APIAPIAn API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways., and forgets the token when the app quits. No push credential is written to disk. The pull request form asks for a title and optional notes for reviewers: what the change does and why, how to see it working, and anything you are unsure about. The app adds the Trac ticket and your WordPress.orgWordPress.orgThe community site where WordPress code is created and shared by the users. This is where you can download the source code for WordPress core, plugins and themes as well as the central location for community conversations and organization. https://wordpress.org/ username to the description, then points you back to Trac because Core pull requests are not monitored on GitHub alone.
Attach a patch to Trac. The app saves the patch locally and opens the attachment page for the site’s linked ticket. From there, you’ll need to sign in and upload the saved file to the TRAC ticket yourself.
Hand the patch to a mentor. This path needs no GitHub or Trac account. The saved patch carries the contributor’s WordPress.org handle, event, ticket, base revision, and date so the authorship can follow the work when a mentor submits it.
The diff comes first on this screen. The destinations sit beside it, grouped by whether you or a mentor will do the sending.
One site can hold work for many tickets
Creating a site is the expensive part: cloning wordpress-develop, installing its dependencies, and running the first build. Starting another ticket should not repeat all of that.
In 1.0, every linked ticket gets its own branch inside the site. Unlinking a ticket parks its changes, and linking another swaps the files in seconds. The site shows the tickets that have work stored in it, so returning to one does not depend on remembering its number. Each ticket contains only that ticket’s changes.
Keep the environment without keeping an old copy of trunk
A site no longer has to stay on the snapshot of trunk it cloned. Update to latest trunk fetches current Core code, installs dependencies only if they changed, and rebuilds the site. Sites more than 14 days old show a warning that patches created from them may no longer apply cleanly.
The Toolkit uses its own built-in Git implementation, so Git does not need to be installed on your computer. To keep the initial download small, it creates a depth-one shallow clone. If you do have Git installed and run it inside the site folder, history-based commands will be limited: git log sees only the current snapshot, while git blame and git bisect cannot work normally.
Updating trunk does not silently rebase ticket work. The app parks the active ticket, updates the site, and returns to it on its original base. If trunk has moved since that ticket started, the Toolkit says so and explains the safe path to recreate the ticket on a fresh base.
Made for Contributor Days
Version 1.0 is available just in time for WordCamp US Contributor Day on August 16. If you are a first-time contributor planning to work with the Core team, the Test team, or another Contributor DayContributor DayContributor Days are standalone days, frequently held before or after WordCamps but they can also happen at any time. They are events where people get together to work on various areas of https://make.wordpress.org/ There are many teams that people can participate in, each with a different focus. https://make.wordpress.org/support/handbook/getting-started/getting-started-at-a-contributor-day/ team working in wordpress-develop, download the app and create your first site before Sunday if you can. The Toolkit can help you find the pull requests and patches linked to a ticket, apply them, and test the result on a running site, even if you are not writing the fix yourself. The installer is self-contained, but the initial clone and dependency installation still need an internet connection and some time.
For Contributor Day facilitators, the Toolkit is a simpler way to introduce people to Core contributions without spending the session on environment setup. Newcomers and testers can get straight to a ticket, try existing work, and contribute feedback with experienced contributors nearby.
The app has no telemetry. Bug reports, release downloads, and direct feedback are the only signals about where it helps and where contributors still get stuck.
You can share feedback through this form, open an issue in the repository, use the feedback button in the app, comment on this post, or pingPingThe act of sending a very small amount of data to an end point. Ping is used in computer science to illicit a response from a target server to test it’s connection. Ping is also a term used by Slack users to @ someone or send them a direct message (DM). Users might say something along the lines of “Ping me when the meeting starts.”@JuanMa in the #core channel on WordPress Slack.
Feedback from WordCampWordCampWordCamps are casual, locally-organized conferences covering everything related to WordPress. They're one of the places where the WordPress community comes together to teach one another what they’ve learned throughout the year and share the joy. Learn more. Contributor Days is particularly helpful, especially on whether the Toolkit enabled you to complete a contribution independently, where you still needed a facilitator, and what could have made the process easier.
Planning is underway for WordPress 7.2! This post outlines the proposed schedule along with a call for volunteers to support the release process.
Following the typical cadence, the proposed final release date for 7.2 is Wednesday, December 9, 2026. This proposed timeline remains flexible for the resulting Release Squad and adjustments can be made if necessary as they determine what timeline works best for their schedule as well as adjustments in coordination with State of the WordState of the WordThis is the annual report given by Matt Mullenweg, founder of WordPress at WordCamp US. It looks at what we’ve done, what we’re doing, and the future of WordPress. https://wordpress.tv/tag/state-of-the-word/..
Proposed Schedule
Milestone
Date
Alpha Begins
Immediately (7.2-alpha began in trunktrunkA directory in Subversion containing the latest development code in preparation for the next major release cycle. If you are running "trunk", then you are on the latest revision. on August 10th with[63167])
BetaBetaA pre-release of software that is given out to a large group of users to trial under real conditions. Beta versions have gone through alpha testing in-house and are generally fairly close in look, feel and function to the final product; however, design changes often occur as part of the process. 1
October 20-22
Beta 2
October 27-29
Beta 3
November 3-5
Beta 4
November 10-12
Release Candidaterelease candidateOne of the final stages in the version release cycle, this version signals the potential to be a final release to the public. Also see alpha (beta). 1
November 17-19
Release Candidate 2
November 24-26
Release Candidate 3
December 1-3
Dry Run
December 7-9
Final Release
December 8-10
As always, all dates are subject to change based on development progress.
Call for Volunteers
Each WordPress release depends on contributors from across the project coming together to make it a success.
As with the 6.7, 6.8, 6.9, 7.0, and 7.1 release cycles, WordPress 7.2 will continue the approach of forming a smaller, focused Release Squad based on feedback received. This streamlined structure places more emphasis on collaboration with the various Make Team Reps, who are encouraged to help coordinate efforts from within their respective teams. The goals are to reduce the overhead on the Release Squad while still ensuring each team’s contributions and priorities are represented throughout the cycle, and to reduce overlap between a Make Team RepTeam RepA Team Rep is a person who represents the Make WordPress team to the rest of the project, make sure issues are raised and addressed as needed, and coordinates cross-team efforts. and that team’s Release Squad Leads. Noteworthy Contributors will be captured from Team Reps towards the end of the release cycle.
While the end goal is to publish the final release of WordPress 7.2 at State of the Word (aka SotW), traveling to or attending SotW is not a requirement to serve on the release squad. All communication related to the release process will continue to take place in the #coreSlackSlackSlack is a Collaborative Group Chat Platform https://slack.com/. The WordPress community has its own Slack Channel at https://make.wordpress.org/chat/ channel.
If you are interested in helping lead WordPress 7.2 in one of the following roles, please comment below or reach out in the #7-2-release-leads Slack channel:
Release LeadRelease LeadThe community member ultimately responsible for the Release. – sets overall goals, makes final decisions on merging, gives final reviews where needed
Release Coordination – helps manage timelines, cross-team collaboration, and status updates
Tech Leads – oversees coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. development (including GutenbergGutenbergThe Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc.
https://wordpress.org/gutenberg/), triagetriageThe act of evaluating and sorting bug reports, in order to decide priority, severity, and other factors., and critical issues
Triage Lead – help monitor issues, shepherd patches, and guide contributors
Test Lead – coordinates testing efforts across the community and test reports
Whether you have led a release before or are looking to get involved for the first time, there are many ways to contribute. Volunteers of all backgrounds and experience levels are welcome!
If you are interested in volunteering, please leave a comment below noting your preferred area(s) by Friday, August 28th.@4thhubbard (or a designee), will review the nominations shortly after to confirm and announce the release squad as soon as possible.
Together we can make WordPress 7.2 the best one yet!
The live meeting will focus on the discussion for upcoming releases, and have an open floor section.
The various curated agenda sections below refer to additional items. If you have ticketticketCreated for both bug reports and feature development on the bug tracker. requests for help, please continue to post details in the comments section at the end of this agenda or bring them up during the dev chat.
The discussion section of the agenda is for discussing important topics affecting the upcoming release or larger initiatives that impact the CoreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. Team. To nominate a topic for discussion, please leave a comment on this agenda with a summary of the topic, any relevant links that will help people get context for the discussion, and what kind of feedback you are looking for from others participating in the discussion.
Open floor 🎙️
Any topic can be raised for discussion in the comments, as well as requests for assistance on tickets. Tickets in the milestone for the next major or maintenance release will be prioritized.
Please include details of tickets / PRs and the links in the comments, and indicate whether you intend to be available during the meeting for discussion or will be async.
A patch has been in progress since June that replaces Dashicons in the adminadmin(and super admin) bar and the sidebarSidebarA sidebar in WordPress is referred to a widget-ready area used by WordPress themes to display information that is not a part of the main content. It is not always a vertical column on the side. It can be a horizontal rectangle below or above the content area, footer, header, or any where in the theme. with WordPress icons, using the wp_get_icon() function that was introduced in WordPress 7.1. Props to @fushar, @wildworks, and @lucasmdo for moving this forward. Because the change being proposed is very visible, it deserves a wider audience than it has had so far.
WordPress coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. runs about 31,000 PHPUnit tests on every commit. These tests need maintenance just like core code: many leak state into other tests, some assert nothing, and several are coupled to networknetwork(versus site, blog) calls.
A goal for the Build/Test Tools component for the 7.2 release is to reduce build time and load across wordpress-develop and related repos. A healthier test suite will help!
This post is intended to show the pieces any contributor can claim, the same way the JSHint effort did in 2013. Same model is running today for 7.2 in PHPStan (#65817), docblocks (#65860), TypeScript (#65997), and JSDoc (#66033).
We need two kinds of help: 1) fix patches and 2) code reviews.
Why now
We can now measure the suite. Pushes to trunk now publish timing metrics to CodeVitals, and we can now flag tests that take too long as candidates to improve (see #65887).
Isolation helps find good next fixes. Running the tests in random order exposes tight coupling and shared state. Two batches of fixes are already in from WordCampWordCampWordCamps are casual, locally-organized conferences covering everything related to WordPress. They're one of the places where the WordPress community comes together to teach one another what they’ve learned throughout the year and share the joy. Learn more. US 2026 contributor dayContributor DayContributor Days are standalone days, frequently held before or after WordCamps but they can also happen at any time. They are events where people get together to work on various areas of https://make.wordpress.org/ There are many teams that people can participate in, each with a different focus. https://make.wordpress.org/support/handbook/getting-started/getting-started-at-a-contributor-day/, and 13 more pull requests are open that need your help (see #65893).
We are shaping a queue for you to jump right in: pull requests with no reviewer, and umbrella tickets with no owner yet.
How to claim a new piece
Pick an item from the tracks of work below; leave a comment on the TracTracAn open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress.ticketticketCreated for both bug reports and feature development on the bug tracker. and here on this post.
Open pull requests and help review and finalize existing PRs.
Test isolation, #65893. Run the touched directory under two random seeds and once in default order, and confirm the patchpatchA special text file that describes changes to code, by identifying the files and lines which are added, removed, and altered. It may also be referred to as a diff. A patch can be applied to a codebase for testing. restores state in tear_down() or the base testcase rather than special-casing one test. Examples: #13181adminadmin(and super admin), #13186 comment globals, #13187 user fixtures, #13191 Ajax hooksHooksIn WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same., #13193 rewrite and query, and #13205 themes.
External HTTPHTTPHTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. guard, #63083.#13407 makes a test fail if it reaches the network without being in the external-http group. See the description for a 5-step test plan.
Strict assertions, #64895.#12583, #11707 and #12613. Check each assertSame() against the function’s documented return type.
Class split, #65208. Eighteen pull requests are open; the ticket lists them. Check that the file name matches the class name, that @covers moved with the tests, and that npm run test:php -- tests/phpunit/tests/admin passes.
Here are the tracks of work
First core contribution? Start with #65819, the catch-all for small test fixes. Great spot to jump in.
At WordCampWordCampWordCamps are casual, locally-organized conferences covering everything related to WordPress. They're one of the places where the WordPress community comes together to teach one another what they’ve learned throughout the year and share the joy. Learn more. US 2026, a group of contributors gathered for an informal conversation about the relationship between the WordPress project and the PHPPHPThe web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher language and community. The discussion was held under the Chatham House Rule, so notes below are not attributed to individual participants.
A framing question opened the conversation: what are our current pain points with the language, historically and today?
Pain points and concerns
Perception and Community Relations
Many in the PHP community don’t consider WordPress developers to be “PHP developers.” Part of this is due to WordPress being seen as dragging PHP backwards because of its backwards compatibility commitments.
Many PHP language features are discussed on the internals mailing list without a WordPress perspective in the room. This is something anyone can join but that we haven’t necessarily prioritized and causes missed opportunities.
When folks raised a WASM proposal, there wasn’t interest at the time. It might be a more apt time to revisit this conversation.
Backwards compatibility and the pluginPluginA plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. ecosystem
WordPress’s long backwards compatibility window creates friction across the ecosystem:
Plugins that depend on other packages run into trouble because the broader PHP ecosystem doesn’t support versions as far back as WordPress does.
Possible responses discussed: contacting plugin authors directly, delisting incompatible plugins, and leaning on plugin developers to be more proactive. A complicating factor: most plugin authors aren’t keeping plugins up to date, and many don’t log in at all.
One forward-looking, wishlist idea that doesn’t solve backwards compat but that could potentially help with plugin security: PHP could add a language feature to load modules with flags applied, enabling a security policy for plugins. Any code required by untrusted code would itself be tainted as untrusted.
Testing and tooling
It can be hard to produce verifiable cases to test compatibility.
There’s an upcoming (not yet ready) release of PHP_CodeSniffer that fixes a lot of what we don’t currently have sniffssniffA module for PHP Code Sniffer that analyzes code for a specific problem. Multiple stiffs are combined to create a PHPCS standard. The term is named because it detects code smells, similar to how a dog would "sniff" out food. for. This would help extenders come up to standard quickly.
Code is landing in GutenbergGutenbergThe Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc.
https://wordpress.org/gutenberg/ that isn’t compatible with PHP 7.4. Differences between the automated tooling for Gutenberg versus CoreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. cause issues when that code flows into Core.
A nightly PHP Docker container to run locally would help. WordPress could put in the work to do it. Early follow has started here.
PHP version adoption
PHP 7.4 is still running on roughly 18% of websites (stats). There’s been less of a compelling reason for sites to upgrade recently. Ideas discussed:
A coordinated push from hosts to get more sites upgraded.
Making PHP 8.x meaningfully faster for WordPress, so speed becomes the compelling reason to upgrade.
Security is already a reason to push on to update since older PHP versions that WordPress supports are no longer receiving security patches.
The issue is not that folks are on old versions of WordPress itself. Stats could be pulled to see whether that has changed.
WASM
The last time a WASM proposal was raised it failed to gather enough interest for people to advance it into an RFC. At the same time, PHP receives lots of security issues, and reducing surface area could be helpful to them. WordPress is already using WASM client-side and with Playground. A more meaningful conversation with the PHP community about WASM would be worthwhile. Tied to this, it may be worth running a PHP extension with WASM, gathering operational data and experience, and sharing that back with the PHP internals to show real world use.
Context for historical involvement
We discussed why more involvement hasn’t happened without any one structural issue to point to. The main venues are PHP Internals and PHP-FIG. WordPress discussed joining FIG roughly 12 years ago but decided it didn’t want to be involved and wouldn’t get any benefit.
There have long been backchannel relationships and occasional public collaboration, but nothing intentional. Some context on why involvement has been limited:
WordPress doesn’t need much from the language and has always been conservative in adopting language features. Server performance and security are what pull WordPress in; language features are pushed harder by frameworks like Laravel.
This kind of collaboration is a constant uphill battle: highly technical, intense work requiring focus, time, and persistence across many people, and even when it makes something better for WordPress, it may still take ten years for WordPress to be able to take advantage of them due to the long-tail support for older PHP versions.
There’s an inherent mindset shift between software developers and language developers (examples raised: translations, time/date handling).
There is a reported history of patches stalling upstream.
Language features, version gating, and polyfills
A provocation was shared for this discussion: Could WordPress start gating features by PHP version aka “if you want feature X, you must run PHP X”? . Generally, WordPress uses polyfills to fill gaps, and there was discussion of collaborating with PHP on early polyfills:
When PHP 7 features were polyfilled early in a dedicated file, WordPress could implement features on top of that APIAPIAn API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. faster.
It was discussed that polyfilling new functionality could disincentivize people to update their PHP versions. It was discussed that this argument may not hold: that instead of doing what it claims, it only causes breakage for sites with older PHP veresions and the incentivization loopLoopThe Loop is PHP code used by WordPress to display posts. Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags. Any HTML or PHP code in the Loop will be processed on each post. https://codex.wordpress.org/The_Loop is broken.
The absence of polyfills led to the creation of multiple incorrect implementations in Core. Having polyfils would be a way of giving proper tools to prevent ad-hoc, insecure, and incomplete implementations in WordPress.
WordPress requires only a small number of PHP extensions (currently just jsonJSONJSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. and mysqlMySQLMySQL is a relational database management system. A database is a structured collection of data where content, configuration and other options are stored. https://www.mysql.com). Per the data from sites phoning home, PCRE is present on 100% of WordPress sites and could be made required.
The required/suggested page is out of date in a few ways and needs updating.
Incentives for hosts to update
Attendees who work at or with hosts shared what would motivate PHP upgrades:
Higher asset utilization. “Run 40% more websites on the same RAM,” with fewer people needed to monitor server security.
Security and performance generally, framed as “save by updating.”
Data from Automattic’s servers could show noticeable differences between PHP versions. There used to be PHP benchmarking across versions; WordPress.comWordPress.comAn online implementation of WordPress code that lets you immediately access a new WordPress environment to publish your content. WordPress.com is a private company owned by Automattic that hosts the largest multisite in the world. This is arguably the best place to start blogging if you have never touched WordPress before. https://wordpress.com/ is already on PHP 8.4.
New PHP features might enable new technologies, such as AI-focused capabilitiescapabilityA capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability)..
MySQL has the same dynamic: new capabilities (vector search, for example) are being built while WordPress still supports very old versions.
Plugin developers
A release email could be sent to plugin developers when their plugins aren’t compatible with a PHP version.
Opportunities for overlap with PHP Core
Performance. There are Core PHP devs that have done substantial performance work in the text processing and other low-level functions space, and the last few PHP releases have all included heavy optimizations. WordPress sees benefits in the HTMLHTMLHyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. API but can’t take full advantage the way it could if that work were upstreamed.
Upstreaming spec-compliant web standards into PHP: HTML parsing, URLURLA specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org parsing, HTTPHTTPHTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands.headerHeaderThe header of your site is typically the first thing people will experience. The masthead or header art located across the top of your page is part of the look and feel of your website. It can influence a visitor’s opinion about your content and you/ your organization’s brand. It may also look different on different screen sizes. parsing, Query arg parsing, MIME content-type inference. This is an area WordPress could help improve or sponsor work on, which would benefit WordPress and PHP at large.
WASM (see above).
Pushing hosts to update PHP (see above).
Do more to report back to the PHP internals and RFCs what our measured impacts are for different changes. Potential namespace resolution changes in PHP came up as an example as that would have real world impact on Gutenberg and new AI work (ex: the PHP AI Client).
Staffing around PHP compatibility
Staffing currently feels solid. Some history and risks:
There were rough patches, notably the PHP 8.0 update; 8.x releases have been more inclined to introduce breaking changes than past versions.
The bigger struggle is getting non-Core people (especially plugin developers) to be proactive about PHP compatibility.
WordPress makes local environments available to run tests against new PHP versions as soon as possible, and the “betaBetaA pre-release of software that is given out to a large group of users to trial under real conditions. Beta versions have gone through alpha testing in-house and are generally fairly close in look, feel and function to the final product; however, design changes often occur as part of the process.” support label for new PHP versions was dropped.
PHP 9 might be a bigger issue. If a major syntax change in PHP 9 makes it impossible to ship a WordPress release supporting both PHP 7.4 and 9, mitigations discussed: asking PHP to produce nightly builds (playground lets you run WordPress on upcoming PHP versions), running WordPress Docker images against PHP nightlies, and taking unit tests out of the Docker container for more flexibility.
Potential follow-ups
Have a more intentional conversation with the PHP community about WASM.
Pull WordPress version stats to check whether old-WP-version distribution has changed.
Explore benchmarking data to give hosts a compelling upgrade story and to do an upgrade push.
Explore upstreaming HTML API work into PHP/find ways to sponsor PHP core development on behalf of WordPress.
Explore compatibility emails to plugin developers.
Follow the PHP_CodeSniffer release that adds missing sniffs, and the namespace resolution discussion.
WordPress 7.1 extends wp_get_abilities() with a standard way to filterFilterFilters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. registered abilities.
The function now accepts an optional $args array that can filter abilities by category, namespace, or metadata. It also supports callbacks for custom per-item filtering and final result processing.
Two new WordPress filters allow plugins to influence ability retrieval across the site:
wp_get_abilities_item_include
wp_get_abilities_result
The REST APIREST APIThe REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”)
https://developer.wordpress.org/rest-api/’s abilities list controller now uses wp_get_abilities() instead of implementing categoryCategoryThe 'category' taxonomy lets you group posts / content together that share a common bond. Categories are pre-defined and broad ranging. filtering separately. It also supports filtering abilities by namespace.
Why was this change needed?
Before WordPress 7.1, there were two ways to retrieve abilities:
// Retrieve every registered ability.
$abilities = wp_get_abilities();
// Retrieve one named ability.
$ability = wp_get_ability( 'my-plugin/export-users' );
wp_get_abilities() always returned the complete registry. A caller needing a subset had to retrieve every ability and filter the result manually:
Several consumers developed their own versions of this pattern for category, namespace, and metadata checks. The REST abilities controller also performed its own category filtering after retrieving the complete registry.
This led to:
Duplicated filtering code.
Inconsistent filtering semantics between consumers.
Different behaviour between the PHPPHPThe web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher and REST APIs.
No standard extension points for ability selection.
Additional filtering passes over the registry.
WordPress 7.1 moves this work into wp_get_abilities(), providing one shared filtering pipeline for CoreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. and plugins.
An ability such as my-plugin/export-users matches, while another-plugin/export-users does not.
Namespace matching includes the namespace delimiter. Passing my-plugin does not accidentally match an ability registered under a similarly named my-plugin-extra namespace.
Filtering by metadata
The meta argument selects abilities whose metadata contains the specified key-value pairs:
Metadata comparisons are strict. The value true does not match 1, and false does not match 0.
The metadata filter checks that every requested condition exists and matches. An ability may contain additional metadata that was not included in the query.
Combining declarative filters
The category, namespace, and meta arguments can be combined:
The result callback runs after all per-item matching has completed. It is suitable for:
Sorting.
Slicing or pagination.
Reordering.
Other final result transformations.
Like item_include_callback, result_callback applies only to the current function call.
Registered abilities are normally returned in an associative array keyed by ability name. When sorting or slicing the result, preserve those keys when downstream code depends on them.
New global filters
WordPress 7.1 also introduces two filters for plugins that need to affect ability retrieval beyond a single call site.
wp_get_abilities_item_include
The wp_get_abilities_item_include filter runs for every ability that passed the declarative conditions and the caller’s item_include_callback:
$include: Whether the ability should currently be included.
$ability: The ability being evaluated.
$args: The complete arguments passed to wp_get_abilities().
Because declarative mismatches are removed before this filter runs, the filter cannot add an ability that failed category, namespace, or meta matching. It can influence the inclusion of abilities that have reached this stage, as well as their global exclusion.
wp_get_abilities_result
The wp_get_abilities_result filter receives the complete result after the caller’s result_callback:
add_filter(
'wp_get_abilities_result',
function ( array $abilities, array $args ): array {
// Apply site-wide result processing when appropriate.
return $abilities;
},
10,
2
);
The filter receives:
$abilities: The final matched array.
$args: The complete arguments passed to wp_get_abilities().
It can be used for site-wide sorting, reordering, or other final processing.
These are global filters. Plugins should use them only when the behaviour is intended to affect every relevant caller. For logic that belongs to one operation, prefer item_include_callback or result_callback.
Filtering order
The complete pipeline runs in the following order:
Match the category argument.
Match the namespace argument.
Match the meta argument.
Run item_include_callback.
Apply wp_get_abilities_item_include.
Add included abilities to the matched result.
Run result_callback on the complete result.
Apply wp_get_abilities_result.
The declarative checks, item callback, and item filter run within a single pass over the registry.
This avoids the separate array_filter() passes that consumers previously had to implement.
Discovery over REST
The REST collection endpoint delegates to wp_get_abilities() and exposes the declarative filters as query parameters:
GET /wp-json/wp-abilities/v1/abilities?namespace=my-plugin
GET /wp-json/wp-abilities/v1/abilities?category=my-plugin-content
GET /wp-json/wp-abilities/v1/abilities?meta[annotations][readonly]=true
Parameters can be combined and use the same AND logic.
?category=data-export&namespace=my-plugin
Every collection request also forces meta.show_in_rest = true internally. Supplying another metadata query can’t reveal an ability that is hidden from REST. The endpoint still requires an authenticated WordPress user, and executing a listed ability still requires its permission callback to pass.
Custom metadata needs a REST parameter schema if its query-string values should be coerced before strict comparison. Without one, 'true' will never match boolean true. The rest_abilities_collection_params filter extends the collection argument schema:
After that declaration, the REST API casts "true" to a boolean true before the value reaches the metadata-matching logic.
GET /wp-json/wp-abilities/v1/abilities?meta[my_plugin][enabled]=true
Built-in annotation types
The known readonly, destructive, and idempotent annotation values are coerced from query strings to boolean values before strict matching. Core declares the schema for these standard ability annotations.
Each accepts a boolean or null, so REST can correctly cast their query values without a pluginPluginA plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. extending the schema:
?meta[annotations][readonly]=true
Use rest_abilities_collection_params filter when making additional metadata fields queryable, especially boolean, integer, number, array, or object values that cannot be matched correctly as untyped query strings.
Backward compatibility
The $args parameter is optional:
$abilities = wp_get_abilities();
Existing calls remain valid, and the function still returns an array of WP_Ability instances keyed by ability name.
Code that manually filters the result can continue to work:
However, plugins should migrate common category, namespace, and metadata checks to the new arguments. Doing so reduces duplicated code and allows Core and other integrations to use consistent matching behaviour.
One behavioural detail deserves particular attention: the two new global filters run even when wp_get_abilities() is called without arguments.
As a result, the following call now means “retrieve abilities through the standard filtering pipeline”:
$abilities = wp_get_abilities();
It does not necessarily mean “retrieve raw registry contents,” because another plugin can alter the result through wp_get_abilities_item_include and wp_get_abilities_result.
Retrieving the raw registry
Code that specifically needs the complete, unfiltered registry can use WP_Abilities_Registry::get_all_registered():
Most application and integration code should continue using wp_get_abilities(). Direct registry access is appropriate only when raw registered state is explicitly required, such as low-level debugging or registry inspection.
Filtering does not replace authorisation
Filtering controls which abilities are returned during discovery. It does not determine whether the current user may execute an ability.
An ability’s permission_callback remains responsible for authorisation:
Developers should not assume that an ability returned by wp_get_abilities() is executable by the current user.
Similarly, excluding an ability from a filtered result is not a security boundary. Any sensitive operation must enforce its permissions when the ability is executed.
Use wp_get_abilities_item_include or wp_get_abilities_result only for behaviour intended to affect ability retrieval across callers.
Use WP_Abilities_Registry::get_all_registered() only when code explicitly requires raw, unfiltered registry data.
Together, these changes make wp_get_abilities() the shared discovery and filtering primitive for the Abilities APIAPIAn API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways., replacing duplicated filtering implementations with a consistent, extensibleExtensibleThis is the ability to add additional functionality to the code. Plugins extend the WordPress core software. pipeline.
These changes were introduced in changeset [62420] for TracTracAn open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress.ticketticketCreated for both bug reports and feature development on the bug tracker.#64990.
Props to @benjamin_zekavica for peer review, and @gziolo for review, technical guidance, and suggested improvements.
WordPress 7.1 ships client-side media processing – a capabilitycapabilityA capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability). that handles image compression, resizing, format conversion, rotation, and thumbnail generation directly in the user’s browser using WebAssembly, rather than on the server. The feature is enabled by default in supporting browsers.
This post outlines what’s changing, how it works, and what pluginPluginA plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. and theme developers need to know.
What is client-side media processing?
Traditionally, when a user uploads an image in the blockBlockBlock is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. editor, the file is sent to the server where PHPPHPThe web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher (using GD or Imagick) generates thumbnails (various image sizes for the front end), applies format conversions, handles EXIF rotation, and scales large images. This approach is limited by PHP memory constraints, server CPU availability, and the capabilitiescapabilityA capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability). of the server’s installed image library.
Client-side media processing moves this work to the browser. Images are processed using wasm-vips, a WebAssembly compilation of the high-performance libvips image processing library. The processed images – including all thumbnails – are then uploaded to the server, which stores them. After all client-side operations complete, a finalize step applies the wp_generate_attachment_metadatafilterFilterFilters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. with context 'update' so plugins see the full sub-sizes metadata. This mirrors how the server already handlesimage uploads, where sub-sizes trigger the same 'update' pass.
Key benefits
Consistent, high-quality output with modern image support. All users get the same libvips powered processing regardless of whether the server has GD or Imagick, and regardless of which version is installed.
Faster downloads for visitors. libvips produces better-compressed output than GD or Imagick (JPEGs are reduced ~15% with MozJPEG like encoding), so the generated images served to site visitors are smaller and load faster.
No more PHP memory limit failures. Large image processing that would exceed PHP’s memory limit now succeeds because it runs in the browser’s memory space.
Reduced server load. Image processing is offloaded to the user’s device, freeing server CPU and memory for other tasks.
iPhone photos just work. HEIC images can be decoded in the browser and converted to JPEG before upload, even on hosts without server-side HEIC support. Note: HEIC decode relies on platform codecs and is supported in Chromium browsers (Chrome, Edge, Brave) on macOS and on Windows with HEVC support, and in Safari on macOS. The full WASM pipeline (everything beyond HEIC) is Chromium-only – see Browser compatibility and fallback below.
AVIF without server-side AVIF support. Hosts whose PHP image editor doesn’t support AVIF can still accept AVIF uploads when client-side processing is active. The MIME-type check is bypassed for client-decoded uploads – see the security FAQ below for details.
Animated GIFs become efficient video. Opaque animated GIFs can be converted in the browser to a companion MP4/WebM video that plays exactly like the original GIF, dramatically cutting the bytes visitors download, with no loss of the autoplay-loopLoopThe Loop is PHP code used by WordPress to display posts. Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags. Any HTML or PHP code in the Loop will be processed on each post. https://codex.wordpress.org/The_Loop GIF feel.
More resilient uploads. Sub-size uploads are independent requests, so a networknetwork(versus site, blog) hiccup mid-upload doesn’t lose the entire batch. Failed requests are retried automatically with exponential backoff, so transient network errors recover without user intervention. Uploads are paused if you go offline and resume when you come back online.
What’s included
Browser-based image processing – Compression, resizing, cropping, format conversion (JPEG, PNG, WebP, AVIF, GIF), EXIF rotation, and progressive/interlaced encoding via WebAssembly in a Web Worker.
Thumbnail generation in the browser – All registered image sub-sizes are generated client-side and uploaded individually via a new sideload REST APIREST APIThe REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”)
https://developer.wordpress.org/rest-api/ endpoint. Sizes that share dimensions with built-in sizes (e.g. Twenty Eleven’s large matches medium_large) are deduplicated to a single physical file registered under all matching size names.
HEIC/HEIF support – iPhone photos (image/heic, image/heif) are decoded in the browser and uploaded as web-ready JPEG. The original HEIC is kept as a companion file (source_image) and removed when the attachment is deleted.
AVIF end-to-end uploads – AVIF can be decoded client-side, no longer requiring server-side AVIF support. High-bit-depth AVIF sources (10- or 12-bit, common for HDR photos) keep their bit depth in generated sub-sizes.
Gain Map HDR support – UltraHDR JPEGs embed a gain map alongside a standard SDR base image: a new, backwards-compatible way of adding HDR data to SDR images that is supported by Google (UltraHDR), Apple (Adaptive HDR), and Adobe (Camera Raw, Lightroom, and Photoshop). These files are detected on upload and preserved end-to-end: the original uploads unmodified, and every generated sub-size keeps its gain map, so thumbnails stay HDR. Format conversion via image_editor_output_format is intentionally skipped for these files, since converting to a different codec would strip the gain map.
Automatic format conversion – The existing image_editor_output_format filter is respected client-side, enabling automatic conversion (e.g., JPEG to WebP) during processing.
Animated GIF to video conversion – Opaque animated GIFs are converted in the browser to an MP4 (or WebM) using the native WebCodecs APIs and the mediabunny library. The GIF stays a single image/gif attachment; the converted video and a first-frame poster are sideloaded as companion files (media_details.animated_video / animated_video_poster). In the editor the block is optionally switched to a “GIF” variation of the Video block that autoplays, loops, and is muted – playing just like the original GIF – and the front end renders a native <video>. The swap is fully reversible via the block transform menu, transparent GIFs are left as images, and browsers without WebCodecs video encoding (e.g. Firefox) upload the original GIF unchanged.
A cross-origin-isolated editor – To run the WASM pipeline, the editor needs SharedArrayBuffer, which browsers only expose to cross-origin-isolated documents. WordPress enables this with Document-Isolation-Policy: isolate-and-credentialless on block editor screens for Chromium 137+. Beyond media processing, this means SharedArrayBuffer and high-resolution timers are now available to any code running in the editor, so plugins can build their own multithreaded or WASM-backed features there. Because DIP is per-document, it provides this isolation without imposing the page-wide constraints of COOP/COEP. See Cross-origin isolation impact below for what extenders should watch for.
Server-side hook compatibility – wp_generate_attachment_metadata fires the same way as for a server-side upload: once with context 'create' during the initial upload and again with 'update' after POST /wp/v2/media/{id}/finalize runs. Plugins that hook into it (watermarking, CDN sync, etc.) continue to work, the same way they already handle the deferred-subsize pass on big-image uploads.
Upload progress feedback – A snackbar in the editor tracks batch upload progress, with a spinner while uploads run and a brief checkmark on completion. It works on both the client-side and server-side upload paths, and announces start and completion via wp.a11y.speak() for screen reader users.
Smart fallback – Browsers that don’t support the required features automatically fall back to server-side processing with no user-facing change.
Image quality filters honored – The standard wp_editor_set_quality and jpeg_quality filters flow through to client-side sub-size generation via a size-aware image_quality field in the upload response, so existing quality-tuning code works unchanged.
Server-side import of external images – The Image block’s “Upload to Media Library” action and the pre-publish “External media” panel now send the image URLURLA specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org to the server, which downloads and sideloads it – avoiding browser CORS failures entirely (the old client-side fetch also could not work in the cross-origin-isolated editor).
Technical overview
@wordpress/upload-media – Manages the upload queue, concurrency (max 5 uploads, max 2 image processing operations), and orchestrates the pipeline.
@wordpress/vips – Wraps wasm-vips in a Web Worker for non-blocking image processing. The WASM bundle is loaded lazily on first use and bundles vips.wasm and vips-heif.wasm (the latter is needed for AVIF decoding).
@wordpress/media-utils – Handles HTTPHTTPHTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. transport to the WordPress REST API.
@wordpress/video-conversion – Wraps the mediabunny library in a Web Worker (mirroring the @wordpress/vips pattern) to convert animated GIFs to MP4/WebM off the main thread, gated on WebCodecs ImageDecoder/VideoEncoder availability and run with a concurrency limit of 1.
On the PHP side:
wp_is_client_side_media_processing_enabled() – Feature gate, filterable via wp_client_side_media_processing_enabled.
Cross-origin isolation – wp_start_cross_origin_isolation_output_buffer() sends Document-Isolation-Policy on load-post.php, load-post-new.php, load-site-editor.php, and load-widgets.php for Chromium 137+. Only active when client side media is enabled.
REST API extensions – New generate_sub_sizes and convert_format parameters, sideload endpoint (POST /wp/v2/media/{id}/sideload), finalize endpoint (POST /wp/v2/media/{id}/finalize), replace_file flag for HEIC companion uploads, and new response fields (exif_orientation, missing_image_sizes, filename, filesize).
Server-side hooksHooksIn WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. still fire
A common concern: if client-side processing bypasses server-side image generation, do plugins that hook into wp_generate_attachment_metadata stop working? No – the filter fires the same way it does during a server-side upload, just with the work shifted around. WordPress fires it once with context 'create' during the initial upload (before the sub-sizes are created), and again with 'update' after the finalize endpoint runs (once all client-side sub-size sideloads are complete). Plugins for watermarking, CDN sync, custom metadata processing, and similar use cases continue to work without modification – write them idempotently so they handle both passes correctly. This double-fire pattern matches how WordPress already handles big-image uploads on the server, where sub-size generation is deferred and triggers a second 'update' pass.
If finalize fails, the error is logged but the upload still succeeds – the call is best-effort so a plugin failure can’t block the user’s upload.
Existing filters still work
Client-side processing reads settings from the server and respects:
big_image_size_threshold – Maximum image dimension before scaling.
image_editor_output_format – Automatic format conversion.
wp_editor_set_quality (and jpeg_quality for JPEG output) – Encode quality, resolved per registered size.
There is noclient_side_supported_mime_types filter; the supported set (image/jpeg, image/png, image/gif, image/webp, image/avif) is fixed at CLIENT_SIDE_SUPPORTED_MIME_TYPES.
Controlling image quality
Client-side encoding honors the same PHP filters that control server-side quality: wp_editor_set_quality and, for JPEG output, jpeg_quality. The server resolves the filters per registered size and reports the result in the upload response’s size-aware image_quality field, which the client applies during sub-size resize and transcode. The same code that tunes server-side quality works unchanged:
When the server doesn’t report the field, the client falls back to a default of 0.82.
Cross-origin isolation impact
When client-side media is active, WordPress sends Document-Isolation-Policy: isolate-and-credentialless on block editor screens for Chromium 137+. Since DIP is per-document, it doesn’t impose the page-wide constraints of COEP/COOP. Notable behavior:
External scripts loaded across origins automatically get a crossorigin="anonymous" attribute via the server-side wp_add_crossorigin_attributes() output buffer and a client-side MutationObserver. <img> is excluded so external image previews aren’t affected.
DIP is skipped on adminadmin(and super admin) pages with an action other than edit, which keeps third-party page builders that rely on same-origin iframeiframeiFrame is an acronym for an inline frame. An iFrame is used inside a webpage to load another HTML document and render it. This HTML document may also contain JavaScript and/or CSS which is loaded at the time when iframe tag is parsed by the user’s browser. access functional.
External images are imported server-side. “Upload to Media Library” and the pre-publish “External media” panel POST the image URL to the media endpoint (a new url parameter) and the server downloads and sideloads it. Plugins importing remote media should do the same rather than fetch()ing image bytes in the browser – a cross-origin fetch is subject to CORS and fails in a credentialless isolated document.
Content Security Policy (CSP)
If your plugin sets a Content Security Policy, ensure the worker-src directive includes blob::
Content-Security-Policy: worker-src 'self' blob:;
Without this, the WASM processing worker cannot be created and processing falls back to server-side.
Server specific hooks don’t fire
Because a server side editor is not used, wp_image_editors, image_memory_limit and image_make_intermediate_size never fire. A complete accounting of media hooks before and after this change is available in the handbook.
What theme developers need to know
Client-side media processing is transparent to themes. Existing filters (big_image_size_threshold, image_editor_output_format, etc.) continue to work without modification. Image sizes registered via add_image_size() are automatically generated client-side, and sizes that share dimensions with built-in sizes are deduplicated to a single physical file.
Browser compatibility and fallback
Client-side processing depends on Document-Isolation-Policy to enable SharedArrayBuffer, which is currently only available in Chromium-based browsers.
Browser
Minimum Version
Status
Chrome
137+
Full support via Document-Isolation-Policy
Edge
137+
Full support via Document-Isolation-Policy
Firefox
–
*Not supported (no Document-Isolation-Policy) – falls back to server-side
Safari
–
*Not supported (no Document-Isolation-Policy) – falls back to server-side. In-browser HEIC decode still works, since it does not require Document-Isolation-Policy.
Chrome and Edge have supported Document-Isolation-Policy since version 137 (released in mid-2025). As of this post’s publication, current stable Chrome and Edge are well past that, so the overwhelming majority of Chromium users already meet the requirement. Chrome on Android supports the feature from version 146. Document-Isolation-Policy is not yet tracked on caniuse; the most reliable place to check current and future browser support is the Chrome Platform Status entry.
On unsupported browsers WordPress falls back to server-side processing automatically. Users see no difference in behavior. A plugin is available to enable the client-side media feature in Firefox/Safari using COEP/COOP headers. These are not used by default because they create compatibility issues with embeds and other third party resources.
Feature detection and limitations
Beyond browser support, the client checks several runtime conditions before activating the WASM pipeline. Failing any check causes a transparent fallback to server-side processing – there is no user-facing change.
Check
Threshold
Why
Device memory
> 2 GB
WASM image processing can OOM on very low-memory devices.
CPU cores
≥ 2
WASM image processing benefits from at least one coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. for the worker plus one for the UIUIUser interface thread.
The ~13 MB worker download is gated to faster connections; 3g is allowed.
CSP blob: workers
must succeed
The worker is created from a blob URL; strict worker-src policies block it.
Frequently asked questions
Isn’t this just a bandwidth optimization?
Not exactly. The client uploads the original plus every sub-size, so total bytes over the wire actually go up during uploads compared to the server-side path (which receives only the original). Bandwidth is saved when serving the images since encoding is better and modern images are always supported. For uploads the real win is server CPU and memory relief: hosts no longer pay the GD/Imagick cost of generating sub-sizes on upload, which is one of the most common causes of PHP timeouts and memory-limit failures on shared hosting. See “Key benefits” above.
Doesn’t the “never trust the client” rule apply here?
Client-side processing is a performance optimization, not a trust boundary. The server still validates every uploaded file – MIME type, dimensions, capability checks, sanitization – and runs the same wp_generate_attachment_metadata filter chain. If the browser can’t or won’t process the file, WordPress falls back to server-side processing transparently.
What happens if the browser can’t process the image?
Server-side processing runs as before. The fallback is automatic and transparent to the user – no UI change, no error. The exact gating (browser features, device memory, CPU cores, network class, CSP) is described in “Feature detection and limitations” above.
Will my plugin’s wp_generate_attachment_metadata hooks still run?
Yes. The filter fires the same way as during a server-side upload: once with context 'create' during the initial upload, and again with 'update' after the finalize endpoint runs (once all client-side sub-size sideloads complete). Watermarking, CDN sync, custom metadata processing, and similar plugins should keep working without modification, but should be checked to make sure they handle both passes. See “Server-side hooks still fire” above.
Does this change the format my users upload?
Only if image_editor_output_format says so – the existing filter is honored client-side. There are two new behaviors. HEIC inputs are converted to JPEG before upload and the original is kept as a companion file. And opaque animated GIFs are converted to a companion video (see below). AVIF inputs upload as AVIF, even on hosts whose server-side image editor lacks AVIF support. HDR images using gain maps upload unmodified, so their HDR gain maps survive – including in every generated sub-size.
What happens to animated GIFs?
Opaque animated GIFs are converted in the browser to a companion MP4/WebM video, and the editor offers a transform to convert the block to a “GIF” variation of the Video block that autoplays, loops, and is muted – so it behaves exactly like the original GIF while downloading far less data. Important details for extenders:
The attachment is still a GIF. It stays a single image/gif attachment in the media library; the video and a first-frame poster are companion files recorded in media_details.animated_video / animated_video_poster, removed automatically when the GIF is deleted.
The front end is a real video block. The swap is a block switch in the editor, not a render-time filter, so the published markup is a native <video autoplay loop muted playsinline poster> – nothing GIF-specific to filter.
It’s reversible, transparent GIFs are left as images, and only standalone Image blocks are converted (GIFs inside a Gallery, Media & Text, or Cover are untouched).
Browser support. Conversion needs WebCodecs video encoding (ImageDecoder + VideoEncoder). Browsers without it – notably Firefox – upload the original GIF unchanged, with no error. There is no separate opt-out filter; disabling client-side media processing also disables GIF conversion.
Why aren’t Firefox and Safari supported?
They don’t ship Document-Isolation-Policy, which is what enables SharedArrayBuffer (required for the WASM pipeline). Users on those browsers get the existing server-side path – no regressionregressionA software bug that breaks or degrades something that previously worked. Regressions are often treated as critical bugs or blockers. Recent regressions may be given higher priorities. A "3.6 regression" would be a bug in 3.6 that worked as intended in 3.5.. The HEIC canvas fallback still works in Safari for HEIC inputs. A plugin is available (wordpress.orgWordPress.orgThe community site where WordPress code is created and shared by the users. This is where you can download the source code for WordPress core, plugins and themes as well as the central location for community conversations and organization. https://wordpress.org/ version coming soon, available now on GitHub) to enable the client-side media feature in Firefox/Safari using COEP/COOP headers.
Testing and feedback
We encourage plugin and theme developers to test client-side media processing with their products. In particular:
Verify that uploads work with your plugin’s custom image sizes and format settings – including sizes that share dimensions with built-in sizes.
Test HEIC uploads if you target sites with iPhone-using authors.
Test AVIF uploads on hosts whose image editor lacks AVIF support.
Test gain-mapped HDR photos (UltraHDR JPEGs) if your plugin transforms images – sub-sizes remain UltraHDR JPEGs with their gain maps, and format conversion is intentionally skipped for them.
Test animated GIF uploads – confirm the block converts to a looping muted video, the round-trip back to a GIF works, and any plugin that post-processes attachments handles the companion video/poster correctly.
Check that cross-origin isolation doesn’t break any external resources or embeds your plugin loads in the editor.
Test with wp_client_side_media_processing_enabled returning false to ensure your fallback path works.
WordPress has no first-class way to store a credential. This proposal outlines a Secrets APIAPIAn API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. for 7.2, along with WP-CLIWP-CLIWP-CLI is the Command Line Interface for WordPress, used to do administrative and development tasks in a programmatic way. The project page is http://wp-cli.org/https://make.wordpress.org/cli/ support for it, and explains why the accompanying UIUIUser interface should wait for 7.3.
Why it matters
Every pluginPluginA plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. that needs an API key today writes it to the options table in plaintext. That is the only available path, so this isn’t a criticism of plugin authors. The consequence is that credentials end up in the same table as the site tagline, and therefore in every database dump, every backup, every staging clone, and every wp option get in a shared terminal.
Plugins have been solving this independently for years, each with its own key derivation, its own cipher choice, and its own failure modes. Site Kit encrypts its Google credentials. WooCommerce gateways handle payment keys. SMTP plugins hold mail credentials. Each one is a separate audit surface, and none of them can be reviewed once on behalf of the ecosystem.
This was survivable when the average site held a Mailchimp key and a reCAPTCHA secret. AI service integrations have made credentials both more numerous and far more expensive to lose: a leaked model-provider key is a metered spend liability. The blast radius increased while the storage mechanism stayed the same.
A second gap is that site owners and hosts increasingly need to answer basic operational questions — which credentials exist on this site, what code is using them, when were they last rotated. None of those are answerable today, because there is no object to ask about. A credential is just a row in a key-value store.
CoreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. has no concept of a secret; there’s nothing for either problem to attach to.
No new UI in 7.2. The storage and retrieval semantics are the part that must be right the first time, because every plugin, every CLICLICommand Line Interface. Terminal (Bash) in Mac, Command Prompt in Windows, or WP-CLI for WordPress. command, and every future adminadmin(and super admin) screen inherits them. A settings screen can be designed properly in 7.3 once there’s a stable API to build against and real usage to design from. The hooksHooksIn WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. and accessors an admin screen would need are in scope now; the screen itself is not.
WP-CLI is in scope. It gives the API a first-party consumer inside the same release, which is the most reliable way to discover whether the surface is actually usable before it freezes. It also closes a real leak of its own: commands that accept credentials as arguments expose them in shell history and in the process list on shared hosts. Reading and writing through the API fixes that in the same change.
A feature pluginFeature PluginA plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins ships before the core patchpatchA special text file that describes changes to code, by identifying the files and lines which are added, removed, and altered. It may also be referred to as a diff. A patch can be applied to a codebase for testing.. Rather than propose an API and ask people to evaluate it from a design document, I’ll publish the implementation as a plugin first. This will let contributors run the API instead of reading about it, expose the awkward parts of the surface while they’re still inexpensive to change, and give sites on 7.0 and 7.1 something usable immediately rather than waiting for December. The core patch then follows with an API surface identical to the plugin’s; nothing anyone builds against it needs rewriting.
The plugin will prove out the API before we consolidate things into core.
Proposed API
A small function set, a value object, and always-on encryption.
wp_set_secret( 'my-plugin/api-key', $key_from_form );
$secret = wp_get_secret( 'my-plugin/api-key' );
if ( is_wp_error( $secret ) ) {
// Exists, but unusable. Key material changed. Tell the user.
} elseif ( null === $secret ) {
// Doesn't exist. Show the connect flow.
} else {
$client = new API_Client( $secret->reveal() );
}
Supporting surface:
Secrets are namespaced by convention, plugin-slug/secret-name, so a future admin screen can group by owner and cross-namespace access has something to check against.
WP_Secret masks itself in logs, var_dump(), and error output. Getting the raw string requires an explicit ->reveal(), which makes every point of use greppable.
Default storage is the options API — ciphertext blobs, autoload=no, excluded from options.php and the REST settings endpoint. The same relationship the object cache has to options: the default substrate, replaceable by a drop-in.
Two capabilitiescapabilityA capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability)., manage_secrets and manage_network_secrets.
A change hook fires on write, carrying actor, timestamp, and old and new fingerprints. Fingerprints are readable; values are not.
Design decisions most likely to draw objections follow, with rationale.
Encryption is not optional
Secrets are encrypted at rest under a master-key envelope: a per-secret data key, wrapped by a master key. There is no plaintext mode and no constant to disable encryption.
The obvious objection is data loss — a site rotates its salts or migrates hosts, key material changes, and the secrets no longer decrypt. Plaintext options never do that. With an envelope, individual secrets are encrypted under a random master key, and the site key only wraps that master key. Rotation re-wraps a single value (the master key) without touching stored secrets. A dedicated constant is the preferred key source, with a zero-config fallback derived from existing salts for sites that can’t set one. When key material really is gone, the failure is bounded and visible: Site Health reports undecryptable secrets, and the recovery path is to re-enter them. Nothing is silently corrupted.
Site Kit is the empirical case. It derives from LOGGED_IN_KEY and LOGGED_IN_SALT by default, recommends a dedicated constant, and documents that salt rotation breaks stored credentials — and it has shipped that way at enormous scale. The ecosystem’s response was to document the approach, not to reject it.
Rationale: an API that can be configured to store plaintext is an options API with a misleading name. Making encryption unconditional is what lets a plugin author make a claim to their users about how the key is stored. The alternative — encryption off unless a site opts in — protects the small number of sites that rotate salts by leaving everyone else in plaintext forever. Sites that would opt in are largely the sites already capable of running a drop-in. Sites that would not are exactly the ones the API exists for.
Availability of cryptographic APIs is not a concern: libsodium has been bundled with PHPPHPThe web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher since 7.2, and core additionally ships sodium_compat for the cases where the extension has been disabled at build time. The primitives are present on every supported configuration without a new dependency.
Retrieval returns one of three things
wp_get_secret() returns a WP_Secret on success, null when the secret does not exist, and WP_Error when it exists but could not be retrieved — wrong key, unavailable keyring, failed decryption.
Rationale: collapsing “absent” and “broken” into a single false-y value is how you get a site that erroneously re-runs its onboarding flow, or disables an integration, when the real problem is key management. Separating them is also what lets a site distinguish a missing credential from an unauthorized change to key material.
No filterFilterFilters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. on the retrieval path
There is no filter applied to a secret on its way out of storage. No endpoint, filter, or capabilitycapabilityA capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability). returns a secret’s value other than the API call itself.
Rationale: I expect the most pushback here — a filter on secret retrieval is, by construction, a hook that receives every credential on the site in plaintext. Any plugin could register it, making it a documented interception point. A filter that can intercept a credential is a filter that can steal one. The flexibility WordPress normally gets from filters is provided instead through explicit, replaceable providers, described next.
Two extension points, independently replaceable
A drop-in, secrets.php, exposes storage and keyring as separate extension points: where ciphertext is stored (Vault, Parameter Store, a host API) and what wraps the master key (a KMS, an HSM). Neither is ever handed a plaintext secret, and neither can turn encryption off.
Rationale: these are different concerns and sites have different constraints. Replacing only the keyring while keeping default storage is a reasonable configuration, and so is the inverse. Coupling them into one swap would force an all-or-nothing decision that most sites can’t make, and would push people back to storing credentials in options.
Two version slots, not unbounded history
CURRENT and PREVIOUS, modeled as string constants on a final class WP_Secret_Version rather than a native enum. Overwriting a secret keeps the old value recoverable, so a mistyped key is fixable. Requests already in flight won’t fail mid-rotation. Retiring the previous slot is an explicit operator action — no timers, no cron.
Rationale: named version history would keep every credential a site has ever held recoverable from a backup indefinitely. Two slots covers the real use case. Retirement is operator-driven because core has no way to know when a third-party integration has finished draining the old value; a timer would just guess wrong on a schedule. Native enums require PHP 8.1, and while 8.3 or better is the recommended version, core’s minimum remains 7.4 — so string constants on a final class are the portable equivalent.
MultisitemultisiteUsed to describe a WordPress installation with a network of multiple blogs, grouped by sites. This installation type has shared users tables, and creates separate database tables for each blog (wp_posts becomes wp_0_posts). See also network, blog, site is in v0
Networknetwork(versus site, blog)-level secrets are supported. Salts are network-wide, so distinct option rows would give logical separation and no cryptographic separation; instead a network root key derives per-site subkeys via sodium_crypto_kdf_derive_from_key(). Site secrets and network secrets are separate functions with separate capabilities, and there is no implicit fallback from one to the other.
Rationale: adding this later would mean introducing a second key hierarchy alongside the first, and migrating between them. The useful consequence of doing it now: rotating the key on a 500-site network re-wraps one value rather than 500.
Import, don’t migrate
No automatic sweep of the options table. wp_import_option_as_secret() lets a plugin author move one known option deliberately, on their own explicit upgrade schedule — and flags the imported secret for rotation rather than merely reporting success.
Rationale: core cannot reliably tell which options are credentials, and guessing would break sites in ways close to undebuggable. The rotation flag matters just as much: a credential that has been sitting in wp_options is already in backups, replicas, and object caches, and encrypting it now does not change that. Rotating is what actually fixes it.
Other decisions worth stating
Strings in, strings out. The API does not serialize. Nothing stops a plugin from json_encode()-ing something and storing the result, but core neither encourages it nor unpacks it, so a secret can never expand into an object graph on read.
Fail closed. If an external store or key backend is unreachable, reads and writes return an error. There is no fallback to local storage or local key wrapping.
Nothing plaintext enters the object cache. On shared hosting a persistent object cache is shared infrastructure. WP_Secret refuses serialization outright.
No export. Values are write-only. Migrations and staging pushes mean re-entry at the destination, so staging never holds production credentials. Fingerprints let an admin confirm the re-entered value matches.
What this does and does not do
The API is a hardening interface. If a secret lives in the WordPress database and a function exists to decrypt it, then any code running as WordPress can obtain that secret.
In scope: database dumps and exfiltration, backups sitting in cloud storage, SQL injection reading wp_options, careless disclosure surfaces — options.php, export files, support screenshots, screen shares, debug logs — and a compromised read-only replica.
Out of scope: code execution in the WordPress process. Nothing defends against that. Code running inside WordPress can call wp_get_secret() for the same reason it can read environment variables or wp-config.php — the secret has to be usable, so anything that is WordPress can use it. Registering a secret against a plugin slug does not prevent a different plugin from asking for it; slugs aren’t authenticated, and even a perfect allowlist wouldn’t stop code that reads the revealed value out of memory after a legitimate call. This proposal makes no claim of per-plugin isolation, and masked values are hygiene against shoulder-surfing rather than a privilege boundary.
What changes is the value of a stolen database, which is the overwhelmingly more common breach.
What it does provide:
Credentials are not sitting in plaintext in a table that gets dumped, cloned, and shared.
Access has one chokepoint, which makes it loggable and auditable for the first time.
Rotation and retirement are defined operations rather than an UPDATE and a hope.
The storage extension point lets a host or site owner move secrets out of WordPress — which is the only configuration where real per-caller enforcement becomes possible.
A proof-of-concept plugin I shipped roughly six months ago after a discussion about encrypting TOTP tokens for the Two Factor plugin, implementing the master-key envelope, the provider model, and a WP-CLI surface. The wp_-prefixed function names here are proposed rather than shipped.
Now through mid-September: feedback on this proposal, particularly the questions below. Feature plugin published for testing.
Late September: patch on TracTracAn open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress., with the API surface matching the plugin.
Before BetaBetaA pre-release of software that is given out to a large group of users to trial under real conditions. Beta versions have gone through alpha testing in-house and are generally fairly close in look, feel and function to the final product; however, design changes often occur as part of the process. 1: committed, or explicitly deferred to 7.3 rather than rushed in.
I’ve volunteered for technical contributions to 7.2 and intend to do this work rather than propose it and hand it off.
Feedback wanted
Objections now, while the surface is still cheap to change. Specifically:
The no-filter decision on retrieval. Providers are meant to cover what a filter would normally give you — is that substitution sufficient for the cases you’d actually need to hook?
Two version slots. Is CURRENT/PREVIOUS sufficient, or is there a rotation pattern that genuinely needs more?
Import, not migrate. Plugin authors: does wp_import_option_as_secret() fit how you would actually move an existing key, or does it need a different shape?
WP-CLI surface. Which commands most need this, and in what order?
Hosts. If you run a real secret store or a real key backend and the drop-in surface is missing something you’d need, that’s the most useful feedback available — and much easier to add before anything ships than after.
Any feedback is welcome — please share your thoughts in the comments below.
Props to @jeffpaul, who talked me into building the original proof of concept six months ago, and @whyisjake for shepherding this proposal.
We propose merging Knowledge, a new wp_knowledgecustom post typeCustom Post TypeWordPress can hold and display many different types of content. A single item of such a content is generally called a post, although post is also a specific post type. Custom Post Types gives your site the ability to have templated posts, to simplify the concept., into WordPress coreCoreCore is the set of software required to run WordPress. The Core Development Team builds WordPress. for the 7.1 release, with Guidelines as the first feature built on it.
Knowledge is a general primitive for storing author-facing and agent-facing site knowledge as standard WordPress content: a post type with a type taxonomyTaxonomyA taxonomy is a way to group things together. In WordPress, some common taxonomies are category, link, tag, or post format. https://codex.wordpress.org/Taxonomies#Default_Taxonomies., the existing roles and capabilitiescapabilityA capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability)., native revisionsRevisionsThe WordPress revisions system stores a record of each saved draft or published update. The revision system allows you to see what changes were made in each revision by dragging a slider (or using the Next/Previous buttons). The display indicates what has changed in each revision., and REST access. Guidelines uses it to give site owners a first-class place to capture the standards that shape how content is written and edited, such as voice, tone, image preferences, and per-blockBlockBlock is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. rules.
The implementation is operational in the GutenbergGutenbergThe Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc.
https://wordpress.org/gutenberg/pluginPluginA plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. and has been exercised by production integrations. It builds on the Guidelines experiment, proposed in February and landed in Gutenberg 22.7 in March, then shaped through community feedback into a consolidated design and stabilized for core.
Purpose & goals
Most sites already have content standards, but they live outside WordPress in documents, wikis, and institutional knowledge. Guidelines gives them a canonical home inside WordPress, available where they matter: during writing and editing.
A single store of standards serves everyone who works on a site: writers and editors applying them by hand, plugins reading them, and AI assistants drawing on them too. Each of these needs the same thing, persistent and structured knowledge about the site, and today there is no shared place to keep it. Without a common primitive, every plugin ships its own storage, its own permissions model, and its own REST surface. That is exactly the kind of fragmentation WordPress core has historically prevented, the same way wp_template, wp_block, and nav_menu_item prevented parallel solutions in their domains. Core owns the primitive. The community decides what to build on it.
The name follows that intent. “Guideline” describes prescriptive records well but fits memories and working notes much less naturally, while “Knowledge” covers both procedural content (how work should be done) and declarative content (what is known). Knowledge names the namespace. Individual records are referred to by their concrete type, a guideline, a memory, a note. The user-facing feature in WP Adminadmin(and super admin) remains Guidelines, the same way the attachment post type surfaces as Media at /wp/v2/media.
The goals, concretely:
Provide a canonical storage primitive for author-facing and agent-facing site knowledge
Ship Guidelines as the first feature built on it, demonstrating the primitive in core
Replace fragmented plugin-specific storage, capabilitycapabilityA capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability)., and REST models with one shared foundation
Non-goals
This merge ships storage and access, not intelligence: no AI provider, no model, no retrieval algorithm, and no autonomous memory system. The specifics below are intentionally out of scope. They remain open topics, just not blockers:
No decay, consolidation, or retrieval mechanism in core. Consuming tools accommodate staleness above the primitive.
Further built-in types are deferred and can be registered by plugins in the meantime:
skill – a procedure that can load and apply a guideline, is planned for 7.2 pending settled loading and discovery semantics (ai#430)
plan – task-scoped working state for a multi-step task, pending a side-effect and lifecycle model
artifact – a reference to a versioned work product distinct from the freeform text covered by note, explored separately
Load applicability of scopes (when a scope’s guidance applies beyond the universal site scope) is left for its own discussion.
Session state and cross-site user preferences live above the primitive.
Encryption at rest is orthogonal hardening that can land later without changing the data model.
What we propose to merge in 7.1
The wp_knowledge custom post type with native revision support
The wp_knowledge_type taxonomy and the wp_knowledge_types registration filterFilterFilters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output.
The built-in types guideline, memory, and note (defined below)
The *_knowledge_item capability namespace and the access model described below
The generic /wp/v2/knowledge REST routes for working with knowledge records like other post types
The Guidelines Settings page: per-scope guideline records, a filterable scope registry as the source of truth for the UIUIUser interface, and a read-only registry route at /wp/v2/knowledge/guideline-scopes
The knowledge management ability, registered through the Abilities APIAPIAn API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways., is expected to follow in a later release.
Built-in types
Each type is defined by what the record represents and how it is applied:
guideline – a standard, pure text that is the source of truth, such as voice, tone, image guidance, or per-block rules. A guideline does nothing on its own. It is there to be applied, either directly by an ability that pulls the text in or through a skill that loads it. The site-wide standards managed on the Settings page carry this type.
memory – durable context explicitly saved or approved for future use, such as user preferences, stable facts, and profile context. Records are private and author-owned. The explicit save-or-approve rule is deliberate: core ships a storage primitive, not a memory architecture. Decay, consolidation, and retrieval remain things to build on top.
note – private freeform working text, such as sticky notes, drafts, and notes synced from external tools. A record saved without a type falls back to note.
Plugins register their own types through the same filter. A plugin might add a glossary type to keep domain terminology consistent across writers, editors, and any agent that reads it:
Core relies only on the semantics of the built-in slugs it ships. Plugin types are free to define their own behavior.
Guideline scopes
Guideline scopes define the sections shown in Settings → Guidelines and the reserved slugs used to address the corresponding guideline records. Core ships scopes such as Site, Copy, Images, and Blocks, each one a guideline record at a reserved slug like guideline-copy. Plugins register additional scopes through a filter, and the Settings page reads the registry through a read-only REST route at /wp/v2/knowledge/guideline-scopes.
Scopes are not knowledge types. The wp_knowledge_type taxonomy answers what kind of record this is (guideline, memory, note). The scope registry answers where a guideline applies in the Guidelines UI. A scope is addressed by its reserved slug, not a taxonomy term, since a term per scope would attach to exactly one record and duplicate identity into a second system.
Privacy, security, and access model
Knowledge records are not exposed as a public index. The post type is registered as an internal storage primitive, not a front-end content type: it is not publicly queryable, and management flows through the Guidelines UI, REST, and registered programmatic surfaces rather than a native public post-type UI. Collection reads require authentication, per-item reads are capability-checked through read_post, and non-publishers can only create private records. New records default to private on creation.
Actor
Site-wide guideline records
Own private records
Others’ private records
Publish / manage global records
Subscriber
No
No
No
No
Contributor
Read where capabilities allow
Create, read, edit, delete
No
No
Author / Editor
Read where capabilities allow
Create, read, edit, delete
No
No
Administrator
Manage
Yes
Yes
Yes
This matrix reflects the access policy from gutenberg#78296 and will be aligned exactly with the final core patchpatchA special text file that describes changes to code, by identifying the files and lines which are added, removed, and altered. It may also be referred to as a diff. A patch can be applied to a codebase for testing.. The built-in type set is defined in code through a filter, while the underlying taxonomy terms are created lazily when a record is first saved with a given type, so authoring a record can create its term. Revisions are retained, and autosave is disabled, since knowledge records have no editor session.
Testing
Enable the Guidelines experiment in Gutenberg and verify:
Settings → Guidelines renders its sections from the scope registry, and each scope can be edited, revised, and restored through the REST APIREST APIThe REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”)
https://developer.wordpress.org/rest-api/
The scope registry route returns core scopes and plugin-registered scopes
A Contributor can create and read only their own private records, and a Subscriber cannot access the post type through REST
REST collection reads require authentication, and non-publishers cannot create published records
No knowledge records are exposed through front-end public queries
Automated coverage for the controller, capability mapping, and type registry ships with the implementation and will be part of the core patch.
FAQ
Is this an AI-only feature? No. The storage primitive is already used for plain note-taking and draft syncing with no AI involved, and the Guidelines experience serves any multi-author site that wants consistent standards. AI tools are one consumer among several.
Does WordPress now have a “memory system”? No. Core ships storage with a clear access policy. A memory record is a durable context a user explicitly saved, comparable to a private post. Anything resembling a memory architecture, including relevance ranking, decay, or consolidation, is left to plugins and integration layers by design.
What about existing plugins that store AI context their own way? Nothing breaks. Plugins can keep their own storage or adopt the shared primitive to gain interoperability, revisions, and the capability model for free.
Why core instead of a plugin? Because the main value is interoperability. A plugin can store its own knowledge records, but it cannot establish a shared convention for how other plugins, editorial tools, and AI integrations store, protect, revise, and expose that knowledge. Without a core primitive, every integration defines its own architecture and the records cannot reliably interoperate. The footprint stays intentionally small: a post type, taxonomy, capabilities, and REST routes that sit unused until something writes to them, with no public queries, no frontend behavior, and no AI processing by default.
The naming and scope model are settled, so this proposal moves forward on that basis unless a blockerblockerA bug which is so severe that it blocks a release. surfaces. The open question is narrower: is the API ready to stabilize for core? The names freeze at WordPress 7.1 BetaBetaA pre-release of software that is given out to a large group of users to trial under real conditions. Beta versions have gone through alpha testing in-house and are generally fairly close in look, feel and function to the final product; however, design changes often occur as part of the process. 1 on July 15, when the post type, taxonomy, REST routes, capabilities, and type slugs become long-term compatibility commitments. Feedback that would block stabilizing for core is most useful in the next three weeks, while there is still room to act on it.
Call for feedback
The questions below are where input matters most before these decisions become core commitments:
Is wp_knowledge the right long-term name for the primitive, and are guideline, memory, and note the right built-in type slugs?
Are the capability boundaries in the access model correct?
Is anything missing that should be settled before these names become core compatibility commitments?
You must be logged in to post a comment.