Skip to content

feat: bridge wp_agent_can_access_agent into datamachine_can_access_agent#2863

Open
chubes4 wants to merge 3 commits into
mainfrom
bridge-agent-access-filter
Open

feat: bridge wp_agent_can_access_agent into datamachine_can_access_agent#2863
chubes4 wants to merge 3 commits into
mainfrom
bridge-agent-access-filter

Conversation

@chubes4

@chubes4 chubes4 commented Jul 8, 2026

Copy link
Copy Markdown
Member

Problem

Data Machine has two agent-access resolution paths that silently disagree:

  1. DM-internal pathPermissionHelper::can_access_agent() and AgentAbilities::user_can_access_agent_row() apply the datamachine_can_access_agent filter after the grant-store check. Host plugins hook this filter to widen (or tighten) access based on live state (capabilities, roles, memberships).
  2. agents-api ability pathagents/can-access-agent and agents/list-accessible-agents resolve through WP_Agent_AccessWP_Agent_WordPress_Authorization_Policy → DM's AgentAccessStoreAdapter. 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_agent filter 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 same can_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.php hooks wp_agent_can_access_agent and forwards into DM's existing datamachine_can_access_agent filter so both access paths converge.

  • Slug ↔ numeric ID translation. The substrate deals in agent slugs; DM's filter takes a numeric agent_id. The bridge owns the translation via Agents::get_by_slug(), with a numeric-identifier passthrough (mirrors AgentAccessStoreAdapter::storage_agent_id()). Unresolvable slugs pass the substrate decision through unchanged — the host filter can't be invoked without a numeric ID.
  • User-principal guard. Only user principals (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.
  • Tighten + widen semantics. The substrate's current $allowed decision (which already includes the store lookup and the effective-agent short-circuit) is seeded as the initial $can_access value into datamachine_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.
  • No behavior change when unhooked. With no datamachine_can_access_agent callbacks, apply_filters returns the seeded value unchanged, so the store-derived decision is preserved exactly.

Registration

AgentAccessFilterBridge::register() is wired in inc/bootstrap.php immediately after AgentAccessStoreAdapter::register(), following the same cached-default-instance + add_filter pattern the adapter uses. The class takes an optional Agents repository 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.json requires wordpress/agents-api: dev-main, but composer.lock pinned a commit (59d1e6b) predating #418. composer update wordpress/agents-api bumps the lock to c1183c4, which contains the wp_agent_can_access_agent seam. 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:

  • Grant hook widens a denied store decision to true (no store row).
  • Hook receives the numeric agent ID, user ID, and the seeded decision.
  • Deny hook tightens a granted store decision to false.
  • No hook = unchanged store behavior (both true and false seeds).
  • Audience principals pass through unchanged; the hook is not invoked.
  • Unresolvable slug passes through unchanged; the hook is not invoked.
  • Numeric agent identifier is forwarded without slug resolution.
  • register() hooks wp_agent_can_access_agent.
  • Non-principal value passes through unchanged.

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).
  • Related access-path smokes (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.

@homeboy-ci

homeboy-ci Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Homeboy Results — data-machine

Lint

review lint — passed

ℹ️ Full options: homeboy self docs commands/lint
Deep dive: homeboy review lint data-machine --changed-since c174857

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-review-lint-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-review-lint-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine/actions/runs/28973407103

Test

review test — passed

ℹ️ Auto-fix lint issues: homeboy refactor data-machine --from lint --write
ℹ️ Collect coverage: homeboy test data-machine --coverage
ℹ️ Pass args to test runner: homeboy test -- [args]
ℹ️ Full options: homeboy self docs commands/test
Deep dive: homeboy review test data-machine --changed-since c174857

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-review-test-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-review-test-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine/actions/runs/28973407103

Audit

review audit — passed

  • audit — 4 finding(s)
  • Total: 4 finding(s)

Deep dive: homeboy review audit data-machine --changed-since c174857

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-review-audit-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-review-audit-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine/actions/runs/28973407103
Tooling versions
  • Homeboy CLI: homeboy 0.281.20+123145ee082b+82ab505f
  • Extension: wordpress from https://github.com/Extra-Chill/homeboy-extensions
  • Extension revision: f89b4297
  • Action: unknown@unknown

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.

datamachine_can_access_agent filter is not applied on the agents-api ability path — DM's two access surfaces disagree

1 participant