Problem
WP_Agent_Routine_Registry is in-memory per request, so consumers with persisted routine sets must call Registry::register() on every boot. Each call fires wp_agent_routine_registered, and the default bridge-sync listener forwards it to WP_Agent_Routine_Action_Scheduler_Bridge::register(), which unconditionally:
- mints and persists a fresh generation option,
as_unschedule_all_actions() for the routine,
as_schedule_recurring_action() / as_schedule_cron_action().
For a consumer with a large persisted fleet (Data Machine converges ~700 scheduled flows onto routines in Extra-Chill/data-machine#3475), naive per-request re-declaration rewrites every AS chain on every request — thousands of pointless writes per request, plus generation churn that defeats the purpose of fencing (each re-register invalidates all in-flight actions).
There is currently no supported way to say "adopt this routine into the registry for this request without (re)scheduling it if the backend already owns an equivalent schedule".
What consumers are forced to do today
Install a backend decorator via the wp_agent_routine_backend filter that implements WP_Agent_Routine_Backend by delegation and no-ops register() when a consumer-persisted schedule fingerprint (trigger + target + input hash) matches what it last scheduled. This works (that is exactly what the DM PR above ships as an interim consumer-side cache), but it:
- duplicates scheduling-state bookkeeping in every heavyweight consumer,
- requires a verification-mode escape hatch so
Registry::reconcile() repairs are not swallowed by the hash gate (a no-op register() inside reconcile would leave genuinely missing schedules unrepaired),
- risks divergence if the bridge's registration semantics change.
Proposal
Some combination of:
- Coverage-aware registration — a
register() mode (or separate adopt()/declare() method) that adds the routine to the in-memory registry and asks the backend to schedule it only when no pending action exists for the routine id (the bridge already has exact-match coverage queries; this is one indexed lookup, not the bulk pending_by_routine() scan).
- A documented reconciliation-only register contract — guarantee that
reconcile()'s internal backend->register() calls are distinguishable from boot-time re-declaration, so consumer-side gating cannot accidentally block reconcile repairs.
Either would let consumers drop the decorator entirely: boot becomes Registry::declare( $id, $args ) per routine with backend work happening only for genuinely new/changed schedules, and reconcile() remains the sole drift-repair path.
Context
Problem
WP_Agent_Routine_Registryis in-memory per request, so consumers with persisted routine sets must callRegistry::register()on every boot. Each call fireswp_agent_routine_registered, and the default bridge-sync listener forwards it toWP_Agent_Routine_Action_Scheduler_Bridge::register(), which unconditionally:as_unschedule_all_actions()for the routine,as_schedule_recurring_action()/as_schedule_cron_action().For a consumer with a large persisted fleet (Data Machine converges ~700 scheduled flows onto routines in Extra-Chill/data-machine#3475), naive per-request re-declaration rewrites every AS chain on every request — thousands of pointless writes per request, plus generation churn that defeats the purpose of fencing (each re-register invalidates all in-flight actions).
There is currently no supported way to say "adopt this routine into the registry for this request without (re)scheduling it if the backend already owns an equivalent schedule".
What consumers are forced to do today
Install a backend decorator via the
wp_agent_routine_backendfilter that implementsWP_Agent_Routine_Backendby delegation and no-opsregister()when a consumer-persisted schedule fingerprint (trigger + target + input hash) matches what it last scheduled. This works (that is exactly what the DM PR above ships as an interim consumer-side cache), but it:Registry::reconcile()repairs are not swallowed by the hash gate (a no-opregister()inside reconcile would leave genuinely missing schedules unrepaired),Proposal
Some combination of:
register()mode (or separateadopt()/declare()method) that adds the routine to the in-memory registry and asks the backend to schedule it only when no pending action exists for the routine id (the bridge already has exact-match coverage queries; this is one indexed lookup, not the bulkpending_by_routine()scan).reconcile()'s internalbackend->register()calls are distinguishable from boot-time re-declaration, so consumer-side gating cannot accidentally block reconcile repairs.Either would let consumers drop the decorator entirely: boot becomes
Registry::declare( $id, $args )per routine with backend work happening only for genuinely new/changed schedules, andreconcile()remains the sole drift-repair path.Context
adopt()lands instead.