Skip to content

refactor(routines): WP_Agent_Routine_Backend contract; Action Scheduler becomes the default backend - #545

Merged
chubes4 merged 1 commit into
mainfrom
refactor/routine-backend-interface
Sep 9, 2026
Merged

refactor(routines): WP_Agent_Routine_Backend contract; Action Scheduler becomes the default backend#545
chubes4 merged 1 commit into
mainfrom
refactor/routine-backend-interface

Conversation

@chubes4

@chubes4 chubes4 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

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>>, and cancel( int $handle ). Handles are opaque ints; the registry never interprets them.
  • WP_Agent_Routine_Action_Scheduler_Bridge is 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_Backend resolves once per request via apply_filters( 'wp_agent_routine_backend', $default ), where $default is the AS bridge instance when WP_Agent_Routine_Action_Scheduler_Bridge::instance()->is_available() is true, else null. A filter return that is not a WP_Agent_Routine_Backend falls 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 _scheduler error when no backend resolves.
  • register-routine-bridge-sync.php is backend-agnostic: the five lifecycle hooks call WP_Agent_Routine_Registry::backend()?->… and no-op when null. We kept the filename (avoids churn in agents-api.php ordering); 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.0 shims: pending_routine_actions()pending_by_routine(), cancel_action_by_id()cancel(). agents-api.php now 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.php no longer adds/removes the openclawp_chat_ability_permission filter — a vendor/product hook name in the generic substrate. The listener already grants scheduled-dispatch permission through the generic agents_chat_permission filter; the product that needs the other hook should subscribe to agents_chat_permission on its side (or register its own filter from its own plugin). A grep of src/ confirms no vendor names remain in the Routines module.

Tests

New tests/routines-backend-contract-smoke.php (registered in composer.json scripts.smoke right after the durability smoke) runs with no as_* functions defined and proves: a fake in-memory backend installed via the wp_agent_routine_backend filter receives register with the exact routine instance; pause/resume/run_now/unregister route through; reconcile() uses pending_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 and reconcile() reports _scheduler while 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). Full composer smoke suite passes; composer phpstan reports no errors; phpcbf (Generic.Formatting.MultipleStatementAlignment) clean on every changed file.

Docs: docs/channels-workflows-operations.md gains 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

…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.
@chubes4
chubes4 merged commit 6be90cf into main Sep 9, 2026
4 checks passed
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.

routines: introduce WP_Agent_Routine_Backend so Action Scheduler is a backend, not a dependency

1 participant