refactor(routines): WP_Agent_Routine_Backend contract; Action Scheduler becomes the default backend - #545
Merged
Merged
Conversation
…er becomes the default backend The routine registry depends on a contract, not on Action Scheduler: - interface WP_Agent_Routine_Backend defines the minimal scheduling surface the registry consumes; handles are opaque ints. - WP_Agent_Routine_Action_Scheduler_Bridge is an instance class implementing the contract. The pre-0.11.0 static facade for the interface methods is removed (PHP cannot carry a static and an instance method of the same name); pending_routine_actions() and cancel_action_by_id() remain as thin deprecation shims, and the AS-specific option helpers and fence installers stay on the bridge. - WP_Agent_Routine_Registry::backend() resolves once per request via the wp_agent_routine_backend filter (default: the AS bridge when AS is present, else null); reset_backend() supports tests. reconcile() works purely off the interface and reports _scheduler with no backend. - register-routine-bridge-sync.php calls backend() and no-ops when null (filename kept to avoid churn). - The vendor 'openclawp_chat_ability_permission' filter is removed from the AS listener; the generic agents_chat_permission filter is the seam products subscribe to. - New tests/routines-backend-contract-smoke.php proves register/ pause/resume/run_now/reconcile route through a fake backend with no as_* functions defined, and that a garbage filter return falls back to the null default. Closes #544.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #544.
What
The routine registry now depends on a contract instead of naming Action Scheduler:
interface WP_Agent_Routine_Backend(src/Routines/interface-wp-agent-routine-backend.php) carries the minimal surface the registry consumes:is_available(),register(),unregister(),pause(),resume(),run_now(),is_paused(),current_generation(),pending_by_routine(): array<string, list<int>>, andcancel( int $handle ). Handles are opaque ints; the registry never interprets them.WP_Agent_Routine_Action_Scheduler_Bridgeis now an instance class (singleton via::instance()) implementing the contract. Generation fencing, the option helpers (generation_option_name,action_generation*), and the fence installers (register_generation_fence,stamp_stored_action,fence_before_execute) stay on the bridge as AS-specific plumbing, not on the interface.WP_Agent_Routine_Registry::backend(): ?WP_Agent_Routine_Backendresolves once per request viaapply_filters( 'wp_agent_routine_backend', $default ), where$defaultis the AS bridge instance whenWP_Agent_Routine_Action_Scheduler_Bridge::instance()->is_available()is true, else null. A filter return that is not aWP_Agent_Routine_Backendfalls back to the default.WP_Agent_Routine_Registry::reset_backend()supports tests. Every direct bridge call in the registry is gone;reconcile()works purely off the interface and returns the_schedulererror when no backend resolves.register-routine-bridge-sync.phpis backend-agnostic: the five lifecycle hooks callWP_Agent_Routine_Registry::backend()?->…and no-op when null. We kept the filename (avoids churn inagents-api.phpordering); its docblock now describes backend sync, not AS sync.Deprecation shim policy
The issue suggested keeping the static facade as deprecation shims, but PHP cannot carry a static and an instance method of the same name — and the instance forms are required by the interface. The static facade for the interface method names (
register,unregister,pause,resume,run_now,is_available,is_paused,current_generation) is therefore removed outright; per the issue's own grep, only agents-api itself called them, and all in-tree call sites are migrated in this PR. The two non-colliding statics remain as thin@deprecated 0.11.0shims:pending_routine_actions()→pending_by_routine(),cancel_action_by_id()→cancel().agents-api.phpnow explicitly requires the interface before the implementing class (first-boot loading, matching the Workflows branch-executor pattern).Vendor hook removal (layer purity)
src/Routines/register-action-scheduler-listener.phpno longer adds/removes theopenclawp_chat_ability_permissionfilter — a vendor/product hook name in the generic substrate. The listener already grants scheduled-dispatch permission through the genericagents_chat_permissionfilter; the product that needs the other hook should subscribe toagents_chat_permissionon its side (or register its own filter from its own plugin). A grep ofsrc/confirms no vendor names remain in the Routines module.Tests
New
tests/routines-backend-contract-smoke.php(registered incomposer.jsonscripts.smokeright after the durability smoke) runs with noas_*functions defined and proves: a fake in-memory backend installed via thewp_agent_routine_backendfilter receivesregisterwith the exact routine instance; pause/resume/run_now/unregister route through;reconcile()usespending_by_routine()/cancel()/register()and returns the enqueued/removed/unchanged shape (including dry-run and paused-routine cases); a garbage filter return falls back to the null default andreconcile()reports_schedulerwhile lifecycle verbs stay inert-safe.All three routine smokes are green:
routine-smoke(36),routines-durability-smoke(56 — fencing/reconcile behavior unchanged),routines-backend-contract-smoke(35). Fullcomposer smokesuite passes;composer phpstanreports no errors; phpcbf (Generic.Formatting.MultipleStatementAlignment) clean on every changed file.Docs:
docs/channels-workflows-operations.mdgains a "Routine backends" subsection (the interface, the filter, AS as the default backend when present, routines registered but inert with no backend).AI-generated with Claude Code via Homeboy worktree