Problem
WP_Agent_Routine + WP_Agent_Routine_Registry give us a first-class primitive for scheduled agent runs (cron interval, persistent session, agent slug, prompt). There is no symmetric primitive for hook-triggered runs: "WordPress hook fires → resolve prompt → dispatch agent". Every consumer that wants "react to a do_action and run an agent" re-implements the dispatch wiring (hook attachment, placeholder substitution, async hop).
Proposal
Mirror the Routines/ directory with a new Triggers/ directory.
Value object
WP_Agent_Event_Trigger with:
id, agent_slug, enabled
hook_name (e.g. transition_post_status, wp_login, woocommerce_order_status_changed)
args_shape[] — declared payload keys exposed to placeholders
placeholders[] — flat dotted keys (post.title, user.email, order.total) with extractor callbacks
conditions[] — pre-dispatch filters (post_type, status, role, …)
prompt_template — string with {{placeholder}} substitution
Registry + helper
WP_Agent_Event_Trigger_Registry — register/unregister/list, mirrors WP_Agent_Routine_Registry.
register_event_trigger_handler( WP_Agent_Event_Trigger \$trigger ) — attaches add_action( \$trigger->hook_name(), ... ) at a late priority, evaluates conditions, resolves placeholders, dispatches via wp_schedule_single_event so the user-facing request never blocks.
Boundary
Substrate owns the value object, registry, and dispatch wiring helper only. Concrete catalogs of triggers (WordPress core hooks, WooCommerce hooks, form-plugin hooks) stay consumer-owned, mirroring how workflow definitions are split today.
Acceptance
Problem
WP_Agent_Routine+WP_Agent_Routine_Registrygive us a first-class primitive for scheduled agent runs (cron interval, persistent session, agent slug, prompt). There is no symmetric primitive for hook-triggered runs: "WordPress hook fires → resolve prompt → dispatch agent". Every consumer that wants "react to ado_actionand run an agent" re-implements the dispatch wiring (hook attachment, placeholder substitution, async hop).Proposal
Mirror the
Routines/directory with a newTriggers/directory.Value object
WP_Agent_Event_Triggerwith:id,agent_slug,enabledhook_name(e.g.transition_post_status,wp_login,woocommerce_order_status_changed)args_shape[]— declared payload keys exposed to placeholdersplaceholders[]— flat dotted keys (post.title,user.email,order.total) with extractor callbacksconditions[]— pre-dispatch filters (post_type, status, role, …)prompt_template— string with{{placeholder}}substitutionRegistry + helper
WP_Agent_Event_Trigger_Registry— register/unregister/list, mirrorsWP_Agent_Routine_Registry.register_event_trigger_handler( WP_Agent_Event_Trigger \$trigger )— attachesadd_action( \$trigger->hook_name(), ... )at a late priority, evaluates conditions, resolves placeholders, dispatches viawp_schedule_single_eventso the user-facing request never blocks.Boundary
Substrate owns the value object, registry, and dispatch wiring helper only. Concrete catalogs of triggers (WordPress core hooks, WooCommerce hooks, form-plugin hooks) stay consumer-owned, mirroring how workflow definitions are split today.
Acceptance
src/Triggers/class-wp-agent-event-trigger.php(value object)src/Triggers/class-wp-agent-event-trigger-registry.phpsrc/Triggers/register-event-trigger-handler.php(the wiring helper)wp_schedule_single_eventis called)