Code

Build a custom user role and capability set

A store-manager role scenario covering add_role timing, map_meta_cap for post ownership edge cases, and safely rolling out capability changes to existing installs.

Works with Claude / GPT730 uses 4.2

The prompt

code-custom-user-role-capabilities
I need a custom role called "Store Manager" that can manage products and orders (a
custom post type `shop_order`) but cannot access Plugins, Themes, or Users screens,
and cannot edit other roles' posts outside their own department.

CONTEXT: [this plugin is already installed on about 200 client sites, and I need to
add this role in a plugin update without breaking sites where an admin has already
manually created a role with the same name]

Build this like a senior dev rolling out a capability change safely, in order:
1. Show the `add_role()` call with a specific capability array (not just cloning
   `administrator`), explicitly listing `edit_shop_orders`, `edit_products`,
   `read`, etc., and explain why granting `manage_options` "just to be safe" is the
   wrong instinct here, since it would expose the Plugins and Themes screens this role
   is supposed to be locked out of.
2. Explain the critical timing issue: `add_role()` only needs to run once (it persists
   in the `wp_options` table), so calling it on every page load is wasteful, but it
   must NOT be called on `plugin activation` alone either, since a user might already
   have the role from a previous version. Show the correct pattern: check a stored
   option like `myplugin_role_version`, and only re-run `add_role()` (removing and
   re-adding to pick up capability changes) when that version is behind the current
   plugin version, so upgrades can add new capabilities to the existing role.
3. Handle the "site already has a role with this name" collision: check
   `get_role( 'store_manager' )` before creating, and if it exists with a different
   capability set than expected, log a notice rather than silently overwriting a
   site's manually customized role.
4. Show a `map_meta_cap` filter for the department-ownership rule: a Store Manager
   should only get `edit_post` capability on `shop_order` posts where a custom
   `department` meta field matches their own user meta, not on every order, which the
   default capability system can't express without this filter.
5. On plugin uninstall, remove the role via `remove_role()` only if it matches the
   plugin's known capability signature, to avoid deleting a role a site admin
   customized further after install.

Replace the role name, capabilities, and post types with your actual setup.

The version-gated add_role() pattern in step 2 is the part most tutorials skip entirely, and it’s exactly what breaks a capability rollout across an existing install base: without it, upgrades either never apply new capabilities or wipe out an admin’s manual customizations on every activation.