Opened 6 weeks ago
Last modified 3 weeks ago
#64596 reopened enhancement
Abilities API: Allow nested namespace ability names (2-4 segments)
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 7.1 | Priority: | normal |
| Severity: | normal | Version: | trunk |
| Component: | AI | Keywords: | has-patch has-unit-tests |
| Focuses: | Cc: |
Description
The current ability naming convention only allows a single namespace separator (my-plugin/my-ability). This is too restrictive for organizing abilities into logical resource groups. With this change, plugins can register abilities like core/posts/find, core/posts/create, or my-plugin/resource/sub/action.
We should change the validation regex in WP_Abilities_Registry::register() from:
/^[a-z0-9-]+\/[a-z0-9-]+$/
to:
/^[a-z0-9-]+(?:\/[a-z0-9-]+){1,3}$/
This allows the first segment plus 1-3 additional slash-delimited segments (2-4 total). The REST API route patterns in both controllers already accepted multiple slashes, so no route changes were needed.
This ticket comes from a suggestion from @justlevine at โhttps://github.com/WordPress/wordpress-develop/pull/10665#issuecomment-3769756790 :
Necessary caveats out of the way, I'd recommend we solve the naming issue holistically with nested namespaces, a pattern that because it's good for human cognitive overload, which just so happens to mean that it aligns with inference best practices too. (related: [core.trac.wordpress.org/ticket/64345#comment:5](https://core.trac.wordpress.org/ticket/64345#comment:5)).
Which would turn these into:
core/posts/find or get (As noted inline, I think getting a single post is superfluous, and both use cases can be more intuitively handled with an improved input shape, e.g. by ).
core/posts/create
core/posts/update
We plan to use this new type of naming for post abilities.
Change History (17)
#2
in reply to:
โย 1
@
5 weeks ago
Replying to jorbin:
This is too restrictive for organizing abilities into logical resource groups.
Isn't organizing abilities into groups why Ability Categories exist?
Categories offer some layer of organization, discoverability, but we may still benefit from nesting. For example all these abilities may be on orders category:
my-plugin/orders/find my-plugin/orders/update my-plugin/orders/external/find
But nesting on /external/ for orders coming from 3rd party marketplaces and that have a different retrieval mechanism may still be useful.
In our case in core we will have a general category for post management across post types. To categorize all post management abilities as something similar but then we can have nesting to different post types something similar to:
core/posts/create core/pages/create
It is not a requirement to use multiple nesting levels but this change opens the door to its usage when it makes sense.
#4
@
5 weeks ago
- Resolution fixed deleted
- Status changed from closed to reopened
The โdev note from when abilities was introduced states that these are namesapaces that aim to prevent conflicts. This is inline with blocks and the rest API. Introducing a new way for slashes to be used introduces unnecessary differences between abilities and other apis in core.
If we are looking for a way to give core provided abilities better prominence, than perhaps what should be explored is a _built_in flag similar to post types.
[61602] should be reverted.
#5
@
5 weeks ago
Thank you for your insights @jorbin. @jason_the_adams, @justlevine given the point given by @jorbin of being consistent with existing API's, is it ok if we revert this change, follow this constraint and name our abilities in a single name space as before e.g: core/get-post, core/update-post etc...?
#6
@
5 weeks ago
@jorgefilipecosta While I'm all for reverting the commit before beta1 so we can have more time review, discuss, and iterate, I would strongly recommend against rushing to ship the functionality as a single-namespace into core without a real conversation about what good usage/holistic API looks like. I'm on mobile which is making it hard for me to dig up and share all the prior discussion in the different core-ai silos, but step 1 here is getting core contributors and committers up-to-date with the feedback loops that happened prior to Trac, not to throw it all out with the bathwater.
IMO this is the whole reason we have an AI Experiments canonical plugin, so if we can't reach consensus before the B1 cutoff, I'd strongly suggest we iterate there and not push a potentially compromised API to core WP7.0.
(Broader side note, I think going forward we need to be a lot more conscious to create Trac tickets upfront with our wordpress-develop Abilities API PRs, and out of our WordPress/ai Issue bubble - especially if when we aren't first releasing them via an Experiment).
This is too restrictive for organizing abilities into logical resource groups.
Isn't organizing abilities into groups why Ability Categories exist?
To be pedantic, Ability Categories exist because folks on the team felt there would be _some_ eventual need for taxonomic grouping, and if a solution wasn't squeezed into the 6.9 deadline and we instead waited until we had a holistic use case, then we'd need to deal with the $default = 'Uncategorized' backcompat problem that the built-in post Category taxonomies have.
(I believe the last 3 months has proved that to be an _implementation_ mistake, and hopefully this entire thread will serve as a stronger argument about shipping speculative functionality to Core, where it's then unintentionally used as "prior art" that prevents holistic iteration. cc extends \WP_Ability and the pre-6.9 merge discussion about top-level props versus meta nesting, for when I return with the receipts).
Regardless, and potentially redundant to what @jorgefilipecosta already noted, the architectural purpose of is also different. Nested namespaces aren't about semantic categorization but about routing, discovery, hierarchical control, scope encapsulation, caching, etc etc. No matter how Abilities Categories evolve over time it will always exist in the metadata plane, as it a potential solution to an entirely different set of problems that we can't deduplicate without compromising the schema-based isolation that Abilities are premised on.
The dev note from when abilities was introduced states that these are namesapaces that aim to prevent conflicts. This is inline with blocks and the rest API. Introducing a new way for slashes to be used introduces unnecessary differences between abilities and other apis in core.
@jorbin Can you clarify your argument here and perhaps your broader concern with slug fragments (consumption, docs, dx, aesthetics, something else?)
- While I think there's a _lot_ of inaccuracies in that dev note (sadly it wasn't shared for team review before it was published), I don't think it's fair to consider
Use namespaced names to prevent conflicts (e.g., my-plugin/my-ability)as philosophically prescriptive. Not do I think that it would somehow preclude or even contradict having additional (still unique) sub-fragments in an ability name.
- Indeed blocks (+ currently templates/partials and patterns) don't support slug fragments, but I'm not sure why we'd want a functional type safety primitive to mirror a design hydration API. As for REST I've got no idea what you mean - of course it supports nested namespaces. Not just the base concept:
/wp-json/{namespace}/{version namespace}/{endpoint namespace}/{selector namespace}but even implement in core we've got off the top of my head we've got Post Revisionswp-json/wp/v2/posts/{selector}/revisionsand Block Directory Search at/wp-json/wp/v2/block-directory/search.
Other programmatic aspects of WordPress that support slug fragments to the same purpose as #61062:
- Routing via Pretty Permalinks & the Rewrite API
- Hierarchical Post Types and Taxonomy terms
- Hooks (Supported, and a very popular pattern in plugin ecosystems, just not adopted internally unless you count SCF)
- Official PSR-4 based libraries like Requests, PHP AI Client (in addition to serving WP internally, both these libs and abilities are intended to integrate with non-WP-ecosystem integrations).
So yeah, IMO both necessary and fairly aligned - I'd go as far to say intuitive / complimentary and definitely not in conflict - with anything in core.
If we are looking for a way to give core provided abilities better prominence, than perhaps what should be explored is a _built_in flag similar to post types.
Hope I made it clear that's not one of the considerations behind nesting. But just in case it's a concern elsewhere:
We can get this information directly from the root namespace core/ with 0 additional effort or API footprint. (This parallels REST, where wp-json/wp/v2 gives you a list of all the available Endpoints in the namespace+subnamespace). Sadly, it looks like the API to query abilities won't make it in time for 7.0 so folks still need to array_*() the registry for any discovery or enumerative prominence, but that's not impacted by the availability of an extra class prop or meta field that just serves as a flag.
Hopefully the above dump was enough to keep the discussion unblocked and moving - at least until I can come back and drop any aforementioned/requested receipts (my current 5h/w week volunteering doesn't seem to be enough for lorekeeping, working on it ๐
).
@jorbin if you're keen and have the time, I'd love to catch you up on the prior art/decisions and any other questions you might have in slack/call before we bring things back here for further discussion/decision, could shave off a good amount of async back-and-forth and improve the chances of us reaching consensus in time to bring in some new core Abilities for 7.0.
#7
@
4 weeks ago
Thanks so much @justlevine for the detailed and thoughtful response, really appreciate you taking the time to lay all of that out, especially from mobile! And thank you @jorbin for raising the consistency concern and reopening the discussion, it's an important point.
I'll revert [61602] before beta1 so we don't have this landing under time pressure.
I also want to walk back my comment https://core.trac.wordpress.org/ticket/64596#comment:5 a bit, I didn't mean to jump ahead and suggest we should lock in flat single-namespace names like core/get-post right away either. @justlevine is right that we should have a real conversation about what good naming looks like holistically before committing to any pattern in core.
I think @justlevine raised some really compelling examples of existing WordPress APIs that already use nested slug fragments (hierarchical post types, REST subroutes like revisions, the Rewrite API, hooks, etc.), and those are worth weighing alongside @jorbin's consistency concerns. Both perspectives have a lot of merit here.
I love the idea of @justlevine and @jorbin syncing up to share context on the prior discussions, and would love to participate. I think that could save us a lot of back-and-forth and help us land on something we're all happy with.
This ticket was mentioned in โPR #10929 on โWordPress/wordpress-develop by โ@jorbin.
4 weeks ago
#8
- Keywords has-patch has-unit-tests added
This is the result of svn merge -c -61602 '^/trunk' . and the resulting code being applied on a git checkout.
#9
@
4 weeks ago
Thanks @jorgefilipecosta and @justlevine. Reverting still seems like the best choice. svn merge -c -61602 '^/trunk' . seems to work cleanly and i've just put โhttps://github.com/WordPress/wordpress-develop/pull/10929 up to test and make sure there aren't any knock on affects.
I would strongly recommend against rushing to ship the functionality as a single-namespace into core without a real conversation about what good usage/holistic API looks like
I worry that ship sailed since a single namespace is what shipped in 6.9. I completely agree that taking the time to get API design right is important since once something ships, it should stay supported in that form for a very long run. This is how we get and maintain user trust.
@jorbin Can you clarify your argument here and perhaps your broader concern with slug fragments (consumption, docs, dx, aesthetics, something else?)
I think one of the reasons WordPress Core has gained the trust of its developer community is the ease of moving between different APIs. The fact that meta behaves the same regardless if it is for a comment, post, or user. The way register_rest_route and both should be run on specific actions. This consistency helps keep people in System 1 thinking which is much more productive.
While WordPress's sustained position often gives a lot of credit to backwards compatibility, the forwards compatibility is also important. Knowing that an ability registered for 7.0 should work in 6.9 for anyone who hasn't upgraded helps with the adoption of features. While more and more sites upgrade quickly each release, there are always more risk-adverse people who wait for a x.y.1 or try to stay one major behind.
#10
@
4 weeks ago
@jorbin thanks for clarifying ๐
I very much share your general concern and agree about the need for both forward and backwards compatibility.
That's why we intentionally designed what we merged into 6.9 around being about to make this decision in 7.0 in the most graceful possible way, since as outlined above and elsewhere the additive benefits to URL fragments are significant and justify the change.
Ability authors who wish to adopt nested namespaces while supporting WordPress 6.9 can fallback to a non-nested name. Here's how that'd look as a one-line ternary:
<?php wp_register_ability( // Fallback to subpar namespace if unsupported. version_compare( '7.0', wp_get_wp_version(), '>=' ) ? 'youcommerce/checkout/coupons/validate' : 'youcommerce/validate_checkout_coupons', $ability_args );
And if an author is targeting 6.9 but forgets to add backcompat? Well, that's why we made it so WP_Abilities_Registry::register() in WP6.9 doesn't throw an error that would break things for the user, and instead just logs a notice that it's unsupported and skips to the next.
Ofc, Abilities registered without nested fragments continue to work no matter what version of WordPress they were added in. As with REST, URL fragments, etc there's nothing stopping developers from polluting their root ability namespace with as many flat-level abilities as they want.
To me that feels like we've more than passed the bar for bidirectional compat here. If there are some specific frictions you're seeing please share and I'm confident we can get them bugfixed in 6.9.x.
Lmk if there are other concerns I or @jorgefilipecosta can assuage.
This ticket was mentioned in โSlack in #core by justlevine. โView the logs.
4 weeks ago
This ticket was mentioned in โSlack in #core by audrasjb. โView the logs.
4 weeks ago
#13
@
4 weeks ago
- Type changed from defect (bug) to enhancement
As per today's devchat, switching to Enhancement.
#14
follow-up:
โย 15
@
3 weeks ago
That's why we intentionally designed what we merged into 6.9 around being about to make this decision in 7.0 in the most graceful possible way
Where was this discussed? It wasn't on #64098 which is where the decision to merge the abilities API was made.
Stepping back, the arguments I see in favor of nesting are:
organizing abilities into logical resource groups
As pointed out, ability catagories are the canonical way to group abilities. Expecting people to run a substring search on all of the abilities isn't creating logical groups, it's creating unnecessary complication.
We plan to use this new type of naming for post abilities.
Assuming this is referring to #64455, that ticket is no longer in the 7.0 milestone.
As much as it strains me to quote @nacin, he rightfuly credited WordPress's success to being "...because the project maintainers deal with all the pain โ technical debt, undoing breakage in upstream libraries, working on as many server (mis)configurations as possible โ its users donโt have to."[1] Forcing version detection onto each plugin is forcing pain on to them. And for what? So there can be a second way to create groupings? In order for the abilities API to do namespaces differently than every other part of core that does namespaces?
1) โhttps://web.archive.org/web/20140417182355/http://opensourcebridge.org/proposals/1413
#15
in reply to:
โย 14
@
3 weeks ago
Replying to jorbin:
That's why we intentionally designed what we merged into 6.9 around being about to make this decision in 7.0 in the most graceful possible way
Where was this discussed? It wasn't on #64098 which is where the decision to merge the abilities API was made.
As I'm sure you can imagine a lot of design discussion and iteration happened before we ported and proposed it to Core for merge consideration. As you've pointed out yourself on a few occasions, the team's been struggling working to centralize conversation and decisions, and outdated PR comments are again flaking on me (thanks GitHub slop team), so bear with me:
I can't find the code-specific discussion on the ability registry class (@gziolo maybe you remember? IIRC it was when we were refactoring the property validation to allow for ability class overloading), but I see that @ovidiu-galatan โit up in August, which I called out in โhttps://github.com/WordPress/ai/issues/40#issuecomment-3382538219 (and some interspersed conversation) and โhttps://github.com/WordPress/ai/issues/21#issuecomment-3269403237 .
|To my best recollection the final decision was made during the [Sep 25 Contributor call](โhttps://make.wordpress.org/ai/2025/09/25/core-ai-contributor-check-in-september-24-2025/) to focus on semantic grouping via Categories for 6.9 and revisit after we hit that deadline.
It then came up a few times on random PRs/calls, and I believe โhttps://github.com/WordPress/wordpress-develop/pull/10665#issuecomment-3780868863 was when the frictions with flat abilities became most visible (cc @jasontheadams)
Stepping back, the arguments I see in favor of nesting are:
organizing abilities into logical resource groups
As pointed out, ability catagories are the canonical way to group abilities.
I believe I already answered this in my first direct reply to you on this ticket, so to repeat: no they are not. They are the canonical way to semantically categorize abilities. In contrast (as I wrote above):
"Nested namespaces aren't about semantic categorization but about routing, discovery, hierarchical control, scope encapsulation, caching, etc etc. No matter how Abilities Categories evolve over time it will always exist in the metadata plane, as it a potential solution to an entirely different set of problems that we can't deduplicate without compromising the schema-based isolation that Abilities are premised on."
A contrived attempt to illustrate the difference:
[Ability Category: Images - it's a conceptual feature grouping] core/post/generate-featured-image core/media/search core/media/edit-image woocommerce/products/create-mockup my-local-sync/media-library/push-images my-local-sync/media-library/pull-image my-local-sync/media-library/download-images s3-offload/library
Notice how Ability Categories exist entirely independently of ownership or structure.
(Yes this is more similar to traditional post Tags than hierarchical post Categories. I remember you were an integral part of the discussion on renaming to Ability Categories to clarify the distinction at โhttps://github.com/WordPress/wordpress-develop/pull/9410 so I won't be redundant by rehashing here, sharing for other readers.)
Expecting people to run a substring search on all of the abilities isn't creating logical groups, it's creating unnecessary complication.
Nobody's expecting this(?). I'm not entirely sure why you think people will need to run a substring search to use abilities. They don't use one to invoke REST, pretty permalinks, or hooks. Invoking remains the same.
What it actually allows is browsability and discoverability. Think of scrolling a list of hundreds core/get-* trying to find the one you want. Conceptually this is the same for typing autocomplete, programmatically surfacing and mapping CP commands (and all the other programmatic concerns I mentioned above), and of course allowing LLMs to efficiently find and discover the correct ability to invoke.
We plan to use this new type of naming for post abilities.
Assuming this is referring to #64455, that ticket is no longer in the 7.0 milestone.
Post Abilities was the concrete visualization that caused the team to push forward the approach, This is an enhancement in and of itself that we plan to use for most - if not all - complex abilities.
We cannot test or iterate on essential Core abilities outside of core (e.g. in the Experiments plugin for feedback) that would benefit from slug fragments without nested abilities being in core (the other solutions I came up with add a significant amount of tech-debt, like a hook to short-circuit the registry validation - which compromises the schema integrity of Abilities, it's entire raison d'etre).
And we obviously don't want to ship a bunch of subpar core/{verb}-{post-type} to core to only then have to deprecate and cause the actual futurecompat friction we both agree is important to avoid.
Forcing version detection onto each plugin is forcing pain on to them.
Again |not what's happening here. Nobody is forced into version detection, nobody is forced into adding namespaces.
And for what? So there can be a second way to create groupings?
Asked and answered (I hope - 3x times by my count)
In order for the abilities API to do namespaces differently than every other part of core that does namespaces?
Again, nope. Different than blocks. The same as every other instance I could think of - and listed out for you - including REST.
(I understand I'm not the best written communicator, but it's very frustrating for me when I directly and repeatedly reply to your concerns and you pivot or "take a step back" and don't actually engage with what I replied - and then ignore it all and just restate your original point. I'm a part-time volunteer contributor, and I believe we both share the same goals which are to prevent trigger-happy folks from merging slop into core. If you disagree with a point I raised, that's obvs fine, but call it out please don't ignore it. If you don't understand something I wrote, highlight it and I'll try to rephrase more succinctly. My offer to answer any questions or concerns you have shortly and directly over DM or a call, still stands. But I'm not sure how to advance this conversation forward resolution if you won't directly engage ๐)
#16
@
3 weeks ago
I don't have much to add but just noting my support for allowing additional namespacing. My instinct when I started tinkering with this was to name like plugin/products/edit and was surprised to find out that wasn't allowed. Being able to separate out resource type and action feels a lot more intuitive. (and +1 to @justlevine's note about browsability/discoverability)
Isn't organizing abilities into groups why Ability Categories exist?