Pending Actions
Data Machine implements the Agents API pending-action approval storage contract with WordPress-backed durable storage.
Pending actions are approval and audit records. Normal runtime requires the durable datamachine_pending_actions table; if database access is unavailable, store/resolution operations fail closed and emit datamachine_pending_action_store_unavailable. The transient store path is reserved for pure-PHP smoke tests or explicit pre-table boot by defining DATAMACHINE_PENDING_ACTION_TRANSIENT_FALLBACK.
Boundary
- Agents API owns generic approval vocabulary and contracts.
- Data Machine owns the concrete WordPress table, CLI, REST routes, abilities, and legacy resolver/tool compatibility.
- Domain plugins register action-specific handlers through
datamachine_pending_action_handlers.
Available Agents API Contracts
Data Machine directly adapts to these merged contracts from automattic/agents-api:
AgentsAPIAIApprovalsPendingAction_StoreAgentsAPIAIApprovalsPendingAction_ResolverAgentsAPIAIApprovalsPendingAction_HandlerAgentsAPIAIApprovalsWP_Agent_Pending_Action_ObserverAgentsAPIAIApprovalsWP_Agent_Approval_DecisionAgentsAPIAIApprovalsPendingActionAgentsAPIAIToolsActionPolicy
The compatibility seam remains the existing datamachine_pending_action_handlers map. Handler objects that implement AgentsAPIAIApprovalsPendingAction_Handler can be placed under the same apply key and Data Machine will dispatch to the Agents API handler method. Legacy callable handlers continue to receive the stored apply_input and full Data Machine payload.
Lifecycle Observers
PendingActionStore dispatches registered WP_Agent_Pending_Action_Observer instances after durable lifecycle transitions complete. Data Machine registers a default WordPress adapter with these hooks:
datamachine_pending_action_stored( WP_Agent_Pending_Action $action )datamachine_pending_action_resolved( WP_Agent_Pending_Action $action, WP_Agent_Approval_Decision $decision, string $resolver )datamachine_pending_action_expired( WP_Agent_Pending_Action $action )
The resolved hook receives the same canonical value-object signature as WP_Agent_Pending_Action_Observer::on_resolved().
Surfaces
- Ability:
datamachine/list-pending-actions - Ability:
datamachine/get-pending-action - Ability:
datamachine/summarize-pending-actions - Ability:
datamachine/sign-pending-action-resolution - Resolver ability:
datamachine/resolve-pending-action - Chat tool:
resolve_pending_action - REST:
GET /datamachine/v1/actions - REST:
GET /datamachine/v1/actions/{action_id} - REST:
GET /datamachine/v1/actions/summary - REST:
GET /datamachine/v1/actions/resolve-by-token?t={token} - REST resolver:
POST /datamachine/v1/actions/resolve - CLI:
wp datamachine pending-actions list|get|summary
PendingActionStore::get() remains the live-pending lookup used by legacy callers. Resolved rows are retained for audit and are available through inspect/list/get surfaces.
Signed Resolution URLs
datamachine/sign-pending-action-resolution creates short-lived approve and reject URLs for a pending action. The URLs are stateless: the t query parameter contains a JSON payload signed with an HMAC secret stored in the datamachine_pending_action_resolution_secret option.
Input:
array(
'action_id' => 'act_...',
'lifetime' => 604800, // Optional, seconds; capped at 30 days.
'resolver' => 'email_approval', // Optional audit identifier.
)Output:
array(
'success' => true,
'action_id' => 'act_...',
'approve_url' => 'https://example.test/wp-json/datamachine/v1/actions/resolve-by-token?t=...',
'reject_url' => 'https://example.test/wp-json/datamachine/v1/actions/resolve-by-token?t=...',
'expires_at' => '2026-05-17T12:00:00+00:00',
)The public token route validates the signature and expiry before delegating to the same resolver path as datamachine/resolve-pending-action. Already-resolved accepted/rejected rows return their existing decision instead of being overwritten. Expired, deleted, missing, or otherwise non-resolvable rows return 410.
Use SignPendingActionResolutionAbility::rotate_secret() to invalidate existing signed URLs during a security incident.