Skip to content

Guard the agent runtime against admin-UI deactivation on managed installs - #329

Merged
chubes4 merged 1 commit into
mainfrom
fix/328-runtime-deactivation-guard
Aug 4, 2026
Merged

Guard the agent runtime against admin-UI deactivation on managed installs#329
chubes4 merged 1 commit into
mainfrom
fix/328-runtime-deactivation-guard

Conversation

@chubes4

@chubes4 chubes4 commented Aug 4, 2026

Copy link
Copy Markdown
Member

Closes #328. Supersedes #323.

On a managed install the site owner has wp-admin access and no reason to understand the plugins list. Deactivating Data Machine there costs them their assistant's memory and tools, with no obvious way back.

Why not the mu-plugin move

#323 proposed relocating the runtime into wp-content/mu-plugins/. It would work, and it is the wrong instrument.

WordPress fatal-error recovery pauses a plugin that fatals. It cannot do that for an mu-plugin.

Data Machine fatals
regular plugin WP pauses it — site stays up, agent gone, operator restores
mu-plugin white screen — store down, no checkout

Making the runtime unkillable also makes it unpausable. On a site taking card payments that trades "the assistant stopped working" for "the shop stopped taking money", and it moves a failure out of the range the agent can recover from into one only the operator can — the inverse of what the managed posture is for.

It would also fork the install layout on posture across install_plugin, update_plugin_to_latest_tag, and the recovery procedure, plus a loader stub since mu-plugins do not autoload subdirectories. #318, #320, and #322 were each posture leaking somewhere that should have stayed uniform.

Worth recording: the move only ever bought deactivation protection. Editing is already denied for both wp-content/plugins/** and wp-content/mu-plugins/** under managed (#322).

What this does

A guard in the mu-plugin layer wp-coding-agents already installs:

  • Deactivate and Delete links replaced with a short explanation, so the owner understands rather than seeing a row with missing controls
  • the request refused server-side — hiding links is presentation only; the request can still arrive by URL, by bulk action, or from any caller of deactivate_plugins()

Both deactivate_plugin and delete_plugin fire immediately before the mutation in core, so wp_die prevents it.

WP-CLI stays open, deliberately

wp plugin deactivate data-machine is the operator's recovery path — it was used on h44lacrosse.com during the managed-posture rollout. The guard targets wp-admin, where the accident happens, not the CLI, where recovery happens. WP_CLI and cron are exempted explicitly.

Which plugins

data-machine unconditionally — wp-coding-agents installs it on every agent install, so that is a property of the product, not of one site. Companions are discovered from what is actually active rather than assumed, per the #320 rule about not naming a stack the tool has not inspected.

Written only under managed posture, and removed on a switch back so an engineering install cannot keep a guard nothing maintains.

Tests

tests/runtime-guard.sh dispatches through the captured hook registry rather than calling the functions directly, so the add_action/add_filter wiring is part of what is tested.

That mattered: an earlier version called the functions directly and passed even with the hooks unregistered. Verified non-vacuous against four regressions —

removed result
deactivate_plugin hook fails
delete_plugin hook fails
plugin_action_links filter fails
WP-CLI escape fails

Also covered: posture scoping, cleanup on switch, generated PHP parses, companions discovered, unrelated plugins untouched.

Full suite: no new failures (same 7 pre-existing environment failures as main). bash -n and php -l clean.

…d installs

Closes #328. Supersedes #323.

On a managed install the site owner has wp-admin access and no reason to
understand the plugins list. Deactivating Data Machine there costs them their
assistant's memory and tools, with no obvious way back.

#323 proposed moving the runtime into mu-plugins. That would also prevent
deactivation, but WordPress fatal-error recovery PAUSES a plugin that fatals
and cannot do that for an mu-plugin — so a Data Machine fatal would go from
"WP pauses it, site stays up, agent gone" to a white screen. On a site taking
card payments that trades a broken assistant for a broken shop, and it moves a
failure out of the range the agent can recover from into one only the operator
can. It would also fork the install layout on posture across install_plugin,
update_plugin_to_latest_tag, and recovery, plus a loader stub.

This does the same job from the mu-plugin layer wp-coding-agents already
installs: the Deactivate and Delete links are replaced with a short
explanation, and the request is refused server-side because hiding links is
presentation only — the request can still arrive by URL, bulk action, or any
caller of deactivate_plugins().

WP-CLI is deliberately not guarded. `wp plugin deactivate data-machine` is the
operator's recovery path and was used during the h44lacrosse.com rollout. The
accident happens in wp-admin; the recovery happens on the CLI.

data-machine is guarded unconditionally because wp-coding-agents installs it on
every agent install — a property of the product, not of one site. Companions
are DISCOVERED from what is actually active rather than assumed, which is the
#320 rule.

The guard is written only under managed posture and removed on a switch back,
so an engineering install cannot keep a guard nothing maintains.

tests/runtime-guard.sh dispatches through the captured hook registry rather
than calling the functions directly, so the add_action/add_filter wiring is
part of what is tested. Verified non-vacuous against four regressions: removing
the deactivate hook, the delete hook, the action-links filter, or the WP-CLI
escape each fail the suite. An earlier version of the test called the functions
directly and did not catch the first three.
@chubes4
chubes4 merged commit 4f7fda4 into main Aug 4, 2026
19 checks passed
@chubes4
chubes4 deleted the fix/328-runtime-deactivation-guard branch August 4, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Guard the agent runtime against admin-UI deactivation on managed installs

1 participant