feat: bridge wp_agent_can_access_agent into datamachine_can_access_agent#2863
Open
chubes4 wants to merge 3 commits into
Open
feat: bridge wp_agent_can_access_agent into datamachine_can_access_agent#2863chubes4 wants to merge 3 commits into
chubes4 wants to merge 3 commits into
Conversation
Contributor
Homeboy Results —
|
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.
Problem
Data Machine has two agent-access resolution paths that silently disagree:
PermissionHelper::can_access_agent()andAgentAbilities::user_can_access_agent_row()apply thedatamachine_can_access_agentfilter after the grant-store check. Host plugins hook this filter to widen (or tighten) access based on live state (capabilities, roles, memberships).agents/can-access-agentandagents/list-accessible-agentsresolve throughWP_Agent_Access→WP_Agent_WordPress_Authorization_Policy→ DM'sAgentAccessStoreAdapter. Store rows only; the host filter never runs.Any consumer of the abilities (Frontend Agent Chat, MCP, REST) gets path 2 and never sees filter-granted access. On a live site this left ~47 team members who should see an agent invisible to the chat widget — only the 3 with explicit grant rows appeared — for weeks, because both paths exist and both look authoritative.
Fixes #2859.
Upstream dependency (resolved)
The upstream blocker was Automattic/agents-api#417. It landed as Automattic/agents-api#418, which added the
wp_agent_can_access_agentfilter wrapping the final authorization decision (including the effective-agent short-circuit) so hosts can tighten as well as widen. The list path (list_accessible_agents_for_principal) was rewritten to iterate every registered agent through the samecan_access_agent()decision — so list/check symmetry is guaranteed by construction. There is only one filter; no separate list-path seam exists to wire.Bridge design
New
inc/Core/Auth/AgentAccessFilterBridge.phphookswp_agent_can_access_agentand forwards into DM's existingdatamachine_can_access_agentfilter so both access paths converge.agent_id. The bridge owns the translation viaAgents::get_by_slug(), with a numeric-identifier passthrough (mirrorsAgentAccessStoreAdapter::storage_agent_id()). Unresolvable slugs pass the substrate decision through unchanged — the host filter can't be invoked without a numeric ID.acting_user_id > 0) are forwarded; audience/anonymous principals pass through with the substrate's store-derived decision unchanged. DM's filter is user-centric, so non-user principals have nothing to translate.$alloweddecision (which already includes the store lookup and the effective-agent short-circuit) is seeded as the initial$can_accessvalue intodatamachine_can_access_agent, and whatever the filter returns is returned. A host hook can therefore grant access with no store row and revoke access despite a store grant — including denying the effective-agent short-circuit.datamachine_can_access_agentcallbacks,apply_filtersreturns the seeded value unchanged, so the store-derived decision is preserved exactly.Registration
AgentAccessFilterBridge::register()is wired ininc/bootstrap.phpimmediately afterAgentAccessStoreAdapter::register(), following the same cached-default-instance +add_filterpattern the adapter uses. The class takes an optionalAgentsrepository in its constructor for direct-injection testing (mirrors the adapter's testability shape).Layer purity
DM stays generic. The bridge forwards to DM's existing filter and contains zero site-, platform-, or vendor-specific knowledge. Platform policy stays in integration plugins that already hook
datamachine_can_access_agent.composer.lock bump
composer.jsonrequireswordpress/agents-api: dev-main, butcomposer.lockpinned a commit (59d1e6b) predating #418.composer update wordpress/agents-apibumps the lock toc1183c4, which contains thewp_agent_can_access_agentseam. Committed as its own commit (chore: bump agents-api to pick up wp_agent_can_access_agent seam).Tests
New
tests/agents-api-access-filter-bridge-smoke.php(12 assertions) follows the existing pure-PHP smoke harness:register()hookswp_agent_can_access_agent.Verification
php tests/agents-api-access-filter-bridge-smoke.php— 12/12 pass.php tests/agents-api-access-store-adapter-smoke.php— 19/19 pass (no regression).agents-chat-access-permission,agents-api-registration,auth-principal-scoped) — all pass.phpcs(WordPress standard) on changed files — clean.homeboy review audit— passed (alignment 0.82, no new findings).homeboy review lint— passed (phpcs + phpstan level 7, 0 errors).homeboy review test— failed due to the WP Codebox CLI infrastructure issue (missing Node module on this host), not a code issue. This is the same infra limitation noted in feat: add wp_agent_can_access_agent host access filter for list/check symmetry Automattic/agents-api#418. GitHub Actions CI is the real gate.