The duplicate-and-approve workflow from Lesson 5 assumes a human is paying attention and will genuinely review a duplicate before approving it. That assumption fails the moment someone wires an agent up to run unattended, or forgets which environment a script is pointed at. This lesson adds a second, independent layer that doesn’t rely on anyone remembering anything: destructive page-builder-editing abilities check WordPress’s own real environment type and refuse to run at all outside development or staging.
Lesson 5’s create-edit-duplicate and apply-approved-edit abilities. Course 4’s permission_callback patterns, this lesson adds one more layer on top of those, it doesn’t replace them.
Step 1: What wp_get_environment_type() actually returns
wp_get_environment_type() is a real WordPress core function, available since WordPress
5.5, that returns one of four values: 'local', 'development', 'staging', or
'production'. It reads the WP_ENVIRONMENT_TYPE constant (typically set in
wp-config.php) or an equivalent system environment variable of the same name. If
neither is set, the documented default is 'production', which means an unconfigured
site is already treated as the most restrictive case by default, not the most
permissive one.
// File: wp-config.php
define( 'WP_ENVIRONMENT_TYPE', 'staging' );
Step 2: A reusable safety check
Write one function that every destructive ability’s permission_callback calls first:
// File: my-plugin.php
function my_plugin_is_safe_for_ai_editing(): bool {
$environment = wp_get_environment_type();
return in_array( $environment, array( 'local', 'development', 'staging' ), true );
}
Step 3: Wire it into the destructive abilities from Lesson 5
The check belongs directly inside each ability’s own permission_callback, registered
alongside the rest of the ability, the same place Lesson 5 already checks capabilities.
There’s no separate hook for this, an ability’s permission_callback is just a regular
PHP callable, so the environment check is simply the first line inside it, run before
any capability check:
// File: my-plugin.php
'permission_callback' => function ( $input ) {
if ( ! my_plugin_is_safe_for_ai_editing() ) {
return new WP_Error(
'production_environment_blocked',
__( 'AI page builder editing is disabled outside staging and development.', 'my-plugin' )
);
}
$original_id = get_post_meta( $input['duplicate_id'], '_ai_edit_of', true );
if ( ! $original_id ) {
return new WP_Error( 'not_a_duplicate', __( 'This is not a tracked AI edit duplicate.', 'my-plugin' ) );
}
return current_user_can( 'publish_posts' ) && current_user_can( 'edit_post', $original_id );
},
Apply my_plugin_is_safe_for_ai_editing() to create-edit-duplicate and every builder
element-editing ability too, not only apply-approved-edit. A duplicate created on a
production site, even one that never gets approved, is still a real post consuming
storage and potentially indexed or discovered before anyone reviews it. The environment
gate should block the entire workflow from starting on production, not just its final,
most destructive step.
Step 4: A weaker, secondary check for misconfigured hosts
Some hosting environments never set WP_ENVIRONMENT_TYPE correctly, or override it in
ways a plugin author doesn’t control. As a defense-in-depth addition, not a replacement,
you can also compare the site’s own URL against an explicit allowlist of known
non-production domains:
// File: my-plugin.php
function my_plugin_is_known_safe_domain(): bool {
$safe_domains = array( 'staging.example.com', 'example.local', 'localhost' );
$host = wp_parse_url( home_url(), PHP_URL_HOST );
return in_array( $host, $safe_domains, true );
}
This is deliberately a secondary, custom pattern, not a core API, and it’s weaker than
wp_get_environment_type() because it requires maintaining a list by hand and doesn’t
benefit from any WordPress-wide convention. Use it as a second opinion alongside the
environment check, never as a replacement for it.
Test it: simulate production locally
Temporarily set the environment type to production in your local wp-config.php and
confirm the gate actually fires:
# File: terminal
wp config set WP_ENVIRONMENT_TYPE production --type=constant
wp eval '
$result = wp_get_ability( "page-builder-ai/apply-approved-edit" )->execute( array( "duplicate_id" => 123 ) );
var_dump( $result );
'
You should get a WP_Error with the code production_environment_blocked. Set the
constant back to staging or development afterward and confirm the same call now
proceeds to the capability checks instead of stopping immediately.
If you spin up a fresh local environment and every destructive ability refuses to run
with no obvious reason, check whether WP_ENVIRONMENT_TYPE is actually set at all.
Since the documented default is 'production' when it’s missing, a local install that
never explicitly configured the constant is correctly, if confusingly, being treated as
production. That’s the safe failure mode, but it means you must set the constant
explicitly in every local and staging environment rather than assuming a fresh install
defaults to something permissive.
Recap
wp_get_environment_type() is a real, current core function returning 'local',
'development', 'staging', or 'production', defaulting to 'production' when
unconfigured. Wrapping every destructive page-builder ability’s permission_callback
with a check against that value means an AI agent’s builder-editing abilities are
structurally unreachable in production, independent of whether anyone remembers to turn
them off by hand. A secondary domain allowlist can back this up for misconfigured hosts,
but the environment check is the primary, reliable layer.
Resources & further reading
- wp_get_environment_type() function reference, developer.wordpress.org
- WordPress MCP Security & Authentication, Course 4