feat: write-excluding safe default ceiling for autonomous executions (Stage A)#413
Merged
Merged
Conversation
…ecutions Part of Automattic#412 (Stage A). Adds WP_Agent_Autonomous_Capability_Policy, a substrate helper that derives a filterable, write-excluding capability ceiling for autonomous execution principals (system/runtime/agent_token) when the host has not made an explicit grant. Introduces an additive denied_capabilities deny-list on WP_Agent_Capability_Ceiling that composes with the existing allow-list (deny takes precedence) without widening prior restrictions, and an is_autonomous_execution() accessor on the principal.
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.
Part of #412 (Stage A).
What
Gives autonomous execution principals a safe default capability ceiling that excludes content-mutating WordPress capabilities, unless the host has already made an explicit grant. This is the derivation + ceiling-model half of #412; it does not touch
src/Tools/enforcement, which another change owns (Stage B).Why
WP_Agent_WordPress_Authorization_Policyalready composes aWP_Agent_Capability_Ceilingwithuser_can(), but today a ceiling is only attached when a caller (token authenticator, host) explicitly supplies one. An autonomous run that arrives with no ceiling is effectively unrestricted by the ceiling layer. Stage A closes that gap at the derivation layer: there is now a substrate helper that produces a conservative default for autonomous runs, expressed generically and filterably so Core/hosts stay in control.How
Autonomy is derived from principal fields, not consumer modes
WP_Agent_Autonomous_Capability_Policy::is_autonomous()treats an execution as autonomous when itsauth_sourceissystem,runtime, oragent_token— i.e. no human is directly authorizing actions in real time. The substrate never references consumer orchestration vocabulary; the set is purely a property of the principal.agents_api_autonomous_auth_sources— adjust which auth sources count as autonomous.agents_api_principal_is_autonomous— per-principal override (e.g. a host can mark a customaudienceprincipal autonomous, or declassify one).Safe default excludes content-mutating capabilities
WP_Agent_Autonomous_Capability_Policy::DEFAULT_DENIED_CAPABILITIESis a principled, conservative set: post/page authoring at every lifecycle stage, taxonomy and options administration, media uploads, unfiltered markup, and plugin/theme/user management. Read access and other non-mutating capabilities are left unrestricted.agents_api_autonomous_denied_capabilities— adjust the denied set.Additive deny-list on the ceiling value object
WP_Agent_Capability_Ceilinggains an optionaldenied_capabilitiesdeny-list (appended aftermetadatato preserve positional compatibility).allows_capability()consults the deny-list first, so a denied capability is never allowed even when the allow-list is unrestricted — the natural way to express "everything except these dangerous ones" without enumerating an infinite WordPress capability vocabulary.from_array/to_array/with_denied_capabilities/with_allowed_capabilitiesall carry and preserve it.Composition (never widens, never overrides an explicit grant)
WP_Agent_Autonomous_Capability_Policy::resolve_ceiling( $principal ):null/unrestricted as the host supplied.Convenience accessor
WP_Agent_Execution_Principal::is_autonomous_execution()delegates to the policy for ergonomic identity checks.Verification
New
tests/autonomous-capability-ceiling-smoke.php(76 assertions) covers: deny-list precedence and composition with the allow-list;with_denied_capabilities/with_allowed_capabilities/from_array/to_arrayround-trips; autonomoussystem/agent_token/runtimeprincipals get the write-excluding default while keeping read access; interactiveuser/application_passwordprincipals are unaffected; explicit host grants are returned unchanged by identity (including an explicit write grant to an autonomous principal, and an unrestricted ceiling that still triggers the safe default); the documented default denied set; and all three filters (agents_api_autonomous_auth_sources,agents_api_principal_is_autonomous,agents_api_autonomous_denied_capabilities).composer smoke— green (full suite, including the layer-purityno-product-imports-smokecheck).composer phpstan(level max) — green.Layer purity
No consumer orchestration vocabulary, product names, or mode strings enter the substrate. Autonomy is a property of the principal's
auth_source; the default capability set and the autonomous auth-source set are both filterable. No changes tosrc/Tools/(Stage B territory).Out of scope (deliberately)
Wiring
resolve_ceiling()into tool execution / the authorization policy'scan()path is Stage B. This PR ships the derivation mechanism and the ceiling-model enhancement, proven by smoke tests that driveresolve_ceiling()directly.