-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
complexity:complexHigh effort, significant design neededHigh effort, significant design neededphase:firewallContext firewall, budgets, redactionContext firewall, budgets, redactionpriority:mediumImportant but not blockingImportant but not blockingsize:LLarge change, over 200 linesLarge change, over 200 linestype:featureNew functionalityNew functionality
Milestone
Description
Milestone: v0.3.0 | Tier: Creative Bet | Effort: Medium
Problem
PolicyDenied errors contain a static reason string (e.g., "WRITE capabilities require the 'writer' or 'admin' role"). When policies are complex — especially with declarative YAML/TOML rules (#42) — users struggle to understand:
- Why a request was denied (which specific rule matched?)
- What they need to change (which role/attribute/justification is missing?)
- How close they were (did they fail one condition or many?)
No other capability-based security library provides structured denial explanations. This is a DX differentiator.
Proposed Change
1. explain_denial() method on Kernel
async def explain_denial(
self,
request: CapabilityRequest,
principal: Principal,
*,
justification: str = "",
) -> DenialExplanation:
"""Explain why a request would be denied, and what's needed to fix it."""2. DenialExplanation model
@dataclass
class DenialExplanation:
"""Structured explanation of a policy denial."""
denied: bool
rule_name: str # Which rule caused the denial
failed_conditions: list[FailedCondition] # What specifically failed
remediation: list[str] # What the principal needs to satisfy the policy
narrative: str # Human-readable summary@dataclass
class FailedCondition:
"""A single condition that was not met."""
condition: str # e.g., "roles"
required: Any # e.g., ["writer", "admin"]
actual: Any # e.g., ["reader"]
suggestion: str # e.g., "Add 'writer' or 'admin' role to principal"3. Example output
explanation = await kernel.explain_denial(request, principal, justification="fix bug")
# DenialExplanation(
# denied=True,
# rule_name="write-requires-writer",
# failed_conditions=[
# FailedCondition(
# condition="roles",
# required=["writer", "admin"],
# actual=["reader"],
# suggestion="Add 'writer' or 'admin' role to principal 'agent-1'"
# ),
# FailedCondition(
# condition="min_justification",
# required=15,
# actual=7,
# suggestion="Provide justification with at least 15 characters (currently 7)"
# )
# ],
# remediation=[
# "Grant role 'writer' or 'admin' to principal 'agent-1'",
# "Provide a justification of at least 15 characters"
# ],
# narrative="Request denied by rule 'write-requires-writer': principal 'agent-1' lacks required roles (has: reader, needs: writer or admin) and justification is too short (7/15 chars)."
# )4. Works with both policy engines
- DefaultPolicyEngine: Traverse the hardcoded rule chain, identify the first failing check.
- DeclarativePolicyEngine (Declarative policy rules (YAML + TOML) #42): Traverse YAML/TOML rules, identify the matching deny rule.
- Both engines implement an
explain()method that returns structured failure details.
5. Deterministic — no LLM dependency
Despite the name "explanation engine," this is pure deterministic logic — rule traversal + structured diff. No LLM calls involved.
Acceptance Criteria
-
explain_denial()returnsDenialExplanationwith correct failed conditions - Each
FailedConditionincludes what was required vs. what the principal has -
remediationlist provides actionable steps to fix the denial -
narrativeis a complete human-readable sentence - Works with
DefaultPolicyEngine(all 5 rule categories covered) - Works with
DeclarativePolicyEngine(when available from Declarative policy rules (YAML + TOML) #42) - For allowed requests,
denied=Falseand empty failure details - Purely deterministic — no randomness or LLM dependency
Affected Files
src/agent_kernel/kernel.py(addexplain_denial()method)src/agent_kernel/models.py(addDenialExplanation,FailedConditiondataclasses)src/agent_kernel/policy.py(addexplain()method toDefaultPolicyEngine)tests/test_policy.py(explanation tests for all denial scenarios)tests/test_kernel.py(end-to-end explain_denial tests)
Dependencies
- Enhanced by Declarative policy rules (YAML + TOML) #42 (Declarative policies) but works independently with
DefaultPolicyEngine
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
complexity:complexHigh effort, significant design neededHigh effort, significant design neededphase:firewallContext firewall, budgets, redactionContext firewall, budgets, redactionpriority:mediumImportant but not blockingImportant but not blockingsize:LLarge change, over 200 linesLarge change, over 200 linestype:featureNew functionalityNew functionality