# ADR-008: Permission System Design

## Status
**Proposed** - Phase 2 Implementation (Weeks 13-22)

**Priority**: P0 - CRITICAL (blocks safe autonomous operation)

## Context

Kaizen agents currently have **unlimited access** to all tools and operations. This creates critical safety risks:

**Security Risks**:
- Agents can delete files (`Write`, `Edit` nodes)
- Agents can execute arbitrary bash commands (`PythonCode`, `Bash` nodes)
- Agents can make unlimited API calls (unbounded costs)
- Agents can access sensitive data without restrictions
- No approval gates for risky operations

**Production Impact**:
- Users cannot safely deploy autonomous agents
- No budget enforcement (OpenAI API costs can spiral)
- No audit trail of tool usage
- Cannot restrict specialists to specific tools (from ADR-009)
- No human-in-the-loop for critical decisions

**Problem**: Kaizen needs a **runtime permission system** that:
1. Enforces tool restrictions per agent/specialist
2. Requires approval for risky operations
3. Tracks and enforces budget limits
4. Integrates with Control Protocol (ADR-007) for approval prompts
5. Integrates with Specialist System (ADR-009) for tool allowlists

## Requirements

### Functional Requirements

1. **FR-1**: Support multiple permission modes (default, accept_edits, plan, bypass)
2. **FR-2**: Tool-level permissions (allow/deny/ask per tool)
3. **FR-3**: Interactive approval prompts via Control Protocol
4. **FR-4**: Budget enforcement (per-agent and global limits)
5. **FR-5**: Specialist tool restrictions (from `SpecialistDefinition.available_tools`)
6. **FR-6**: Permission rules with regex patterns
7. **FR-7**: Permission overrides at runtime
8. **FR-8**: Audit trail of permission decisions

### Non-Functional Requirements

1. **NFR-1**: Permission check latency <5ms (critical path)
2. **NFR-2**: Budget check latency <1ms (simple arithmetic)
3. **NFR-3**: Approval prompt latency <50ms (via Control Protocol)
4. **NFR-4**: Thread-safe permission state
5. **NFR-5**: Zero permission checks when disabled (bypass mode)

## Decision

We will implement a **layered permission system** with four components: ExecutionContext, PermissionPolicy, ToolApprovalManager, and BudgetEnforcer.

### Architecture Overview

```
┌──────────────────────────────────────────────────────────────┐
│ PERMISSION SYSTEM (4 Layers)                                 │
├──────────────────────────────────────────────────────────────┤
│                                                               │
│  1. EXECUTION CONTEXT                                        │
│     - Tracks permissions during agent execution              │
│     - Maintains budget counters                              │
│     - Stores allowed/disallowed tools                        │
│     - Thread-safe state management                           │
│                                                               │
│  2. PERMISSION POLICY                                        │
│     - Rules engine (allow/deny/ask)                          │
│     - Regex pattern matching                                 │
│     - Mode-based behavior (default/accept_edits/plan)        │
│     - Budget limit checks                                    │
│                                                               │
│  3. TOOL APPROVAL MANAGER                                    │
│     - Interactive approval via Control Protocol              │
│     - Approval prompt generation                             │
│     - User response handling                                 │
│     - Approval history tracking                              │
│                                                               │
│  4. BUDGET ENFORCER                                          │
│     - Real-time cost tracking                                │
│     - Per-tool cost estimation                               │
│     - Budget limit enforcement                               │
│     - Cost reporting                                         │
│                                                               │
└──────────────────────────────────────────────────────────────┘

BaseAgent.execute_tool()
  ↓
PermissionPolicy.can_use_tool()
  ↓
[allow] → Execute tool
[deny]  → Raise PermissionError
[ask]   → ToolApprovalManager.request_approval()
           ↓
           ControlProtocol.send_request("approval")
           ↓
           [approved] → Execute tool
           [denied]   → Raise PermissionError
```

### Core Components

#### 1. Permission Modes (`kaizen/core/autonomy/permissions/types.py`)

```python
from enum import Enum

class PermissionMode(Enum):
    """Permission enforcement modes"""

    DEFAULT = "default"
    # Ask for approval on risky tools (Write, Edit, Bash, PythonCode)
    # Enforce budget limits
    # Apply permission rules

    ACCEPT_EDITS = "accept_edits"
    # Auto-approve file modifications (Write, Edit)
    # Still ask for Bash, PythonCode
    # Enforce budget limits

    PLAN = "plan"
    # Read-only mode (code review, planning)
    # Block all execution tools
    # Allow only Read, Grep, Glob

    BYPASS = "bypass"
    # Skip all permission checks (DANGEROUS!)
    # For testing or trusted environments only
```

#### 2. Permission Rule (`kaizen/core/autonomy/permissions/types.py`)

```python
from dataclasses import dataclass
import re

@dataclass
class PermissionRule:
    """Permission rule with regex pattern matching"""

    # Tool pattern (regex)
    tool_pattern: str  # e.g., ".*Write.*", "Bash", ".*Agent.*"

    # Action
    behavior: Literal["allow", "deny", "ask"]

    # Optional conditions
    conditions: dict[str, Any] | None = None
    # Example: {"input_contains": "rm -rf"}

    # Priority (higher = checked first)
    priority: int = 0

    # Compiled pattern (lazy)
    _compiled_pattern: re.Pattern | None = None

    def matches(self, tool_name: str) -> bool:
        """Check if tool matches pattern"""
        if not self._compiled_pattern:
            self._compiled_pattern = re.compile(self.tool_pattern)
        return bool(self._compiled_pattern.match(tool_name))

    def evaluate_conditions(self, tool_input: dict[str, Any]) -> bool:
        """Check if conditions are met"""
        if not self.conditions:
            return True

        # Example condition: input_contains
        if "input_contains" in self.conditions:
            input_str = str(tool_input)
            return self.conditions["input_contains"] in input_str

        return True
```

#### 3. ExecutionContext (`kaizen/core/autonomy/permissions/context.py`)

```python
from dataclasses import dataclass, field
from threading import Lock

@dataclass
class ExecutionContext:
    """Tracks permissions and usage during agent execution"""

    # Permission mode
    mode: PermissionMode = PermissionMode.DEFAULT

    # Permission rules (priority sorted)
    rules: list[PermissionRule] = field(default_factory=list)

    # Explicit tool lists
    allowed_tools: set[str] = field(default_factory=set)
    disallowed_tools: set[str] = field(default_factory=set)

    # Budget tracking
    budget_limit_usd: float | None = None
    total_spent_usd: float = 0.0

    # Tool usage counters
    tool_usage_count: dict[str, int] = field(default_factory=dict)
    tool_total_cost: dict[str, float] = field(default_factory=dict)

    # Approval history
    approved_tools: set[str] = field(default_factory=set)
    denied_tools: set[str] = field(default_factory=set)

    # Thread safety
    _lock: Lock = field(default_factory=Lock, init=False, repr=False)

    def record_tool_usage(self, tool_name: str, cost_usd: float = 0.0):
        """Thread-safe tool usage recording"""
        with self._lock:
            self.tool_usage_count[tool_name] = self.tool_usage_count.get(tool_name, 0) + 1
            self.tool_total_cost[tool_name] = self.tool_total_cost.get(tool_name, 0.0) + cost_usd
            self.total_spent_usd += cost_usd

    def is_budget_exceeded(self, estimated_cost_usd: float = 0.0) -> bool:
        """Check if budget would be exceeded"""
        if self.budget_limit_usd is None:
            return False
        return (self.total_spent_usd + estimated_cost_usd) > self.budget_limit_usd

    def get_remaining_budget(self) -> float | None:
        """Get remaining budget"""
        if self.budget_limit_usd is None:
            return None
        return self.budget_limit_usd - self.total_spent_usd
```

#### 4. PermissionPolicy (`kaizen/core/autonomy/permissions/policy.py`)

```python
class PermissionPolicy:
    """Permission decision engine"""

    def __init__(self, context: ExecutionContext):
        self.context = context

    async def can_use_tool(
        self,
        tool_name: str,
        tool_input: dict[str, Any],
        estimated_cost_usd: float = 0.0
    ) -> tuple[bool | None, str | None]:
        """
        Check if tool can be used.

        Returns:
            (decision, reason)
            - (True, None): Allowed
            - (False, reason): Denied
            - (None, None): Ask user (requires approval)
        """

        # ──────────────────────────────────────────────────────────
        # 1. BYPASS MODE: Skip all checks
        # ──────────────────────────────────────────────────────────
        if self.context.mode == PermissionMode.BYPASS:
            return True, None

        # ──────────────────────────────────────────────────────────
        # 2. BUDGET CHECK: Block if budget exceeded
        # ──────────────────────────────────────────────────────────
        if self.context.is_budget_exceeded(estimated_cost_usd):
            remaining = self.context.get_remaining_budget()
            return False, f"Budget exceeded: ${self.context.total_spent_usd:.2f} spent, ${remaining:.2f} remaining, tool needs ${estimated_cost_usd:.2f}"

        # ──────────────────────────────────────────────────────────
        # 3. PLAN MODE: Only allow read operations
        # ──────────────────────────────────────────────────────────
        if self.context.mode == PermissionMode.PLAN:
            read_only_tools = {"Read", "Grep", "Glob", "ListDirectoryNode"}
            if tool_name not in read_only_tools:
                return False, f"Plan mode: Only read-only tools allowed (tried: {tool_name})"
            return True, None

        # ──────────────────────────────────────────────────────────
        # 4. EXPLICIT DISALLOW LIST: Hard deny
        # ──────────────────────────────────────────────────────────
        if tool_name in self.context.disallowed_tools:
            return False, f"Tool '{tool_name}' is explicitly disallowed"

        # ──────────────────────────────────────────────────────────
        # 5. EXPLICIT ALLOW LIST: Skip further checks
        # ──────────────────────────────────────────────────────────
        if tool_name in self.context.allowed_tools:
            return True, None

        # ──────────────────────────────────────────────────────────
        # 6. PERMISSION RULES: Pattern matching (priority order)
        # ──────────────────────────────────────────────────────────
        sorted_rules = sorted(self.context.rules, key=lambda r: r.priority, reverse=True)

        for rule in sorted_rules:
            if rule.matches(tool_name) and rule.evaluate_conditions(tool_input):
                if rule.behavior == "allow":
                    return True, None
                elif rule.behavior == "deny":
                    return False, f"Denied by rule: {rule.tool_pattern}"
                elif rule.behavior == "ask":
                    return None, None  # Need approval

        # ──────────────────────────────────────────────────────────
        # 7. MODE-BASED DEFAULT BEHAVIOR
        # ──────────────────────────────────────────────────────────
        risky_tools = {"Write", "Edit", "Bash", "PythonCode", "DeleteFileNode"}

        if self.context.mode == PermissionMode.ACCEPT_EDITS:
            # Auto-approve file edits
            if tool_name in {"Write", "Edit"}:
                return True, None
            # Ask for bash/code execution
            if tool_name in {"Bash", "PythonCode"}:
                return None, None
            # Allow others
            return True, None

        elif self.context.mode == PermissionMode.DEFAULT:
            # Ask for risky tools
            if tool_name in risky_tools:
                return None, None
            # Allow others
            return True, None

        # Fallback: ask
        return None, None
```

#### 5. ToolApprovalManager (`kaizen/core/autonomy/permissions/approval.py`)

```python
from kaizen.core.autonomy.control import ControlProtocol, ControlRequest

class ToolApprovalManager:
    """Manages interactive tool approval via Control Protocol"""

    def __init__(self, control_protocol: ControlProtocol):
        self.protocol = control_protocol
        self.approval_history: dict[str, bool] = {}  # tool_name → approved

    async def request_approval(
        self,
        tool_name: str,
        tool_input: dict[str, Any],
        context: ExecutionContext
    ) -> bool:
        """
        Request user approval for tool usage.

        Returns:
            True if approved, False if denied
        """

        # Check approval history (optional caching)
        cache_key = f"{tool_name}:{hash(str(tool_input))}"
        if cache_key in self.approval_history:
            return self.approval_history[cache_key]

        # Generate approval prompt
        prompt = self._generate_approval_prompt(tool_name, tool_input, context)

        # Send approval request via Control Protocol
        request = ControlRequest.create(
            type="approval",
            data={
                "tool_name": tool_name,
                "tool_input": tool_input,
                "prompt": prompt,
                "options": ["Approve", "Deny", "Approve All", "Deny All"],
            }
        )

        try:
            response = await self.protocol.send_request(request, timeout=60.0)
            approved = response.data.get("approved", False)
            action = response.data.get("action", "once")

            # Handle "Approve All" / "Deny All"
            if action == "all":
                if approved:
                    context.allowed_tools.add(tool_name)
                else:
                    context.disallowed_tools.add(tool_name)

            # Cache decision
            self.approval_history[cache_key] = approved

            return approved

        except Exception as e:
            logger.error(f"Approval request failed: {e}")
            # Fail closed (deny by default)
            return False

    def _generate_approval_prompt(
        self,
        tool_name: str,
        tool_input: dict[str, Any],
        context: ExecutionContext
    ) -> str:
        """Generate human-readable approval prompt"""

        # Special handling for risky tools
        if tool_name == "Bash":
            command = tool_input.get("command", "")
            return f"""
🤖 Agent wants to execute bash command:

  {command}

⚠️  This could modify your system. Review carefully.

Budget: ${context.total_spent_usd:.2f} / ${context.budget_limit_usd or 'unlimited'} spent

Approve this action?
            """.strip()

        elif tool_name in {"Write", "Edit"}:
            file_path = tool_input.get("file_path", "unknown")
            return f"""
🤖 Agent wants to modify file:

  {file_path}

⚠️  This will change your codebase.

Approve this action?
            """.strip()

        else:
            return f"""
🤖 Agent wants to use tool: {tool_name}

Input: {tool_input}

Approve this action?
            """.strip()
```

#### 6. BudgetEnforcer (`kaizen/core/autonomy/permissions/budget.py`)

```python
class BudgetEnforcer:
    """Enforces budget limits and tracks costs"""

    # Cost estimation (approximate, per-tool)
    TOOL_COSTS = {
        "LLMAgentNode": 0.01,  # ~1 cent per LLM call (depends on model)
        "OpenAINode": 0.01,
        "AnthropicNode": 0.015,
        "OllamaNode": 0.0,  # Local, free
        "Write": 0.0,
        "Read": 0.0,
        "Bash": 0.0,
    }

    @staticmethod
    def estimate_tool_cost(tool_name: str, tool_input: dict[str, Any]) -> float:
        """Estimate cost for tool usage (USD)"""

        # LLM nodes: estimate based on token count
        if "LLM" in tool_name or "Agent" in tool_name:
            # Rough estimate: $0.01 per 1000 tokens
            prompt_length = len(str(tool_input.get("prompt", "")))
            estimated_tokens = prompt_length // 4  # ~4 chars per token
            return (estimated_tokens / 1000) * 0.01

        # Other tools: fixed cost
        return BudgetEnforcer.TOOL_COSTS.get(tool_name, 0.0)

    @staticmethod
    def get_actual_cost(result: dict[str, Any]) -> float:
        """Extract actual cost from tool result"""
        if "usage" in result and "cost_usd" in result["usage"]:
            return result["usage"]["cost_usd"]
        return 0.0
```

### Integration with BaseAgent

```python
# kaizen/core/base_agent.py

class BaseAgent(Node):
    def __init__(self, config: BaseAgentConfig):
        super().__init__(config)

        # Initialize permission components
        self.execution_context = ExecutionContext(
            mode=config.permission_mode or PermissionMode.DEFAULT,
            budget_limit_usd=config.budget_limit_usd,
        )
        self.permission_policy = PermissionPolicy(self.execution_context)
        self.approval_manager = None  # Set when control_protocol enabled

    def enable_control_protocol(self, transport: Transport) -> None:
        """Enable control protocol (from ADR-007)"""
        super().enable_control_protocol(transport)
        self.approval_manager = ToolApprovalManager(self.control_protocol)

    async def execute_tool(self, tool_name: str, tool_input: dict[str, Any]) -> dict[str, Any]:
        """
        Execute tool with permission checks.

        Flow:
        1. Estimate cost
        2. Check permissions
        3. Request approval if needed
        4. Execute tool
        5. Record usage and actual cost
        """

        # ──────────────────────────────────────────────────────────
        # 1. ESTIMATE COST
        # ──────────────────────────────────────────────────────────
        estimated_cost = BudgetEnforcer.estimate_tool_cost(tool_name, tool_input)

        # ──────────────────────────────────────────────────────────
        # 2. CHECK PERMISSIONS
        # ──────────────────────────────────────────────────────────
        allowed, reason = await self.permission_policy.can_use_tool(
            tool_name, tool_input, estimated_cost
        )

        if allowed is False:
            raise PermissionError(f"Tool '{tool_name}' denied: {reason}")

        # ──────────────────────────────────────────────────────────
        # 3. REQUEST APPROVAL IF NEEDED
        # ──────────────────────────────────────────────────────────
        if allowed is None:  # Need approval
            if not self.approval_manager:
                raise RuntimeError("Tool requires approval but Control Protocol not enabled")

            approved = await self.approval_manager.request_approval(
                tool_name, tool_input, self.execution_context
            )

            if not approved:
                raise PermissionError(f"Tool '{tool_name}' denied by user")

        # ──────────────────────────────────────────────────────────
        # 4. EXECUTE TOOL
        # ──────────────────────────────────────────────────────────
        result = await self._execute_tool_impl(tool_name, tool_input)

        # ──────────────────────────────────────────────────────────
        # 5. RECORD USAGE & ACTUAL COST
        # ──────────────────────────────────────────────────────────
        actual_cost = BudgetEnforcer.get_actual_cost(result)
        self.execution_context.record_tool_usage(tool_name, actual_cost)

        logger.info(
            f"Tool executed: {tool_name}, "
            f"cost: ${actual_cost:.4f}, "
            f"total: ${self.execution_context.total_spent_usd:.2f}"
        )

        return result
```

### Integration with Specialist System (ADR-009)

```python
# kaizen/runtime/async_local.py

class AsyncLocalRuntime:
    async def create_agent_from_specialist(self, specialist: SpecialistDefinition) -> BaseAgent:
        """Create agent from specialist with tool restrictions"""

        agent = BaseAgent.from_specialist(specialist)

        # Apply specialist tool restrictions
        if specialist.available_tools:
            agent.execution_context.allowed_tools = set(specialist.available_tools)
            logger.info(f"Specialist '{specialist.description}' restricted to tools: {specialist.available_tools}")

        return agent
```

## Usage Examples

### Example 1: Default Mode (Ask for Risky Tools)

```python
config = BaseAgentConfig(
    permission_mode=PermissionMode.DEFAULT,
    budget_limit_usd=10.0,
)

agent = SimpleQAAgent(config=config)
agent.enable_control_protocol(CLITransport())

# Agent tries to use Bash
# → PermissionPolicy returns (None, None) → Ask user
# → ToolApprovalManager sends approval request
# → User approves
# → Tool executes
```

### Example 2: Accept Edits Mode (Auto-Approve File Mods)

```python
config = BaseAgentConfig(
    permission_mode=PermissionMode.ACCEPT_EDITS,
)

agent = CodeGenerationAgent(config=config)

# Agent uses Write node
# → PermissionPolicy returns (True, None) → Allowed
# → Tool executes immediately (no prompt)

# Agent tries Bash
# → PermissionPolicy returns (None, None) → Ask user
# → Approval prompt shown
```

### Example 3: Plan Mode (Read-Only)

```python
config = BaseAgentConfig(
    permission_mode=PermissionMode.PLAN,
)

agent = CodeReviewAgent(config=config)

# Agent uses Read node → Allowed
# Agent tries Write node → PermissionError("Plan mode: Only read-only tools allowed")
```

### Example 4: Specialist Tool Restrictions

```python
specialist = SpecialistDefinition(
    description="DataFlow specialist",
    system_prompt="You are a DataFlow expert",
    available_tools=["Read", "Write", "DataFlowReadNode", "DataFlowCreateNode"],
)

agent = BaseAgent.from_specialist(specialist)

# Agent tries to use Bash
# → PermissionPolicy checks allowed_tools
# → Bash not in allowed_tools
# → PermissionError("Tool 'Bash' is explicitly disallowed")
```

### Example 5: Permission Rules

```python
context = ExecutionContext(
    mode=PermissionMode.DEFAULT,
    rules=[
        PermissionRule(
            tool_pattern="Bash",
            behavior="deny",
            conditions={"input_contains": "rm -rf"},
            priority=100,
        ),
        PermissionRule(
            tool_pattern=".*Write.*",
            behavior="ask",
            priority=50,
        ),
    ]
)

agent = BaseAgent(config=BaseAgentConfig())
agent.execution_context = context

# Agent tries: Bash with "rm -rf /"
# → Rule matches, behavior="deny"
# → PermissionError("Denied by rule: Bash")

# Agent tries: Write to "test.txt"
# → Rule matches, behavior="ask"
# → Approval prompt shown
```

### Example 6: Budget Enforcement

```python
config = BaseAgentConfig(
    budget_limit_usd=5.0,
)

agent = ResearchAgent(config=config)

# Agent uses LLMAgentNode multiple times
# → Each call costs ~$0.01
# → After 500 calls: $5.00 spent
# → Next call: PermissionError("Budget exceeded: $5.00 spent, $0.00 remaining, tool needs $0.01")
```

## Consequences

### Positive

1. **✅ Safe Autonomous Operation**: Agents cannot perform unauthorized actions
2. **✅ Budget Control**: Prevents runaway API costs
3. **✅ Human-in-the-Loop**: Interactive approval for critical operations
4. **✅ Flexible Modes**: Different behaviors for different scenarios (dev vs prod)
5. **✅ Specialist Tool Restrictions**: Enforces `available_tools` from ADR-009
6. **✅ Audit Trail**: Complete record of tool usage and decisions
7. **✅ Performance**: <5ms permission checks (critical path)
8. **✅ Thread-Safe**: Concurrent agent execution supported

### Negative

1. **⚠️ Approval Prompts**: Can interrupt flow (requires Control Protocol)
2. **⚠️ Cost Estimation**: Approximate (not exact until after execution)
3. **⚠️ User Experience**: Users must understand permission modes
4. **⚠️ Bypass Mode**: Dangerous if misused

### Mitigations

1. **Approval Prompts**: Provide "Approve All" option for repeated tools
2. **Cost Estimation**: Log actual costs, improve estimates over time
3. **User Experience**: Clear documentation, sensible defaults
4. **Bypass Mode**: Warn prominently in docs, only for testing

## Performance Targets

| Metric | Target | Validation |
|--------|--------|------------|
| Permission check (cached) | <1ms | Benchmark 10,000 checks |
| Permission check (uncached) | <5ms | Benchmark 1,000 checks |
| Budget check | <1ms | Simple arithmetic |
| Approval prompt (round-trip) | <50ms | Via Control Protocol |
| Rule evaluation (10 rules) | <5ms | Pattern matching overhead |
| ExecutionContext thread safety | 100% | Concurrent access test |

## Testing Strategy

### Tier 1: Unit Tests

```python
def test_permission_policy_bypass_mode():
    """Test bypass mode skips all checks"""
    context = ExecutionContext(mode=PermissionMode.BYPASS)
    policy = PermissionPolicy(context)

    allowed, reason = await policy.can_use_tool("Bash", {"command": "rm -rf /"})
    assert allowed is True
    assert reason is None

def test_budget_enforcement():
    """Test budget limit enforcement"""
    context = ExecutionContext(budget_limit_usd=10.0, total_spent_usd=9.50)
    policy = PermissionPolicy(context)

    allowed, reason = await policy.can_use_tool("LLMAgentNode", {}, estimated_cost_usd=1.0)
    assert allowed is False
    assert "Budget exceeded" in reason
```

### Tier 2: Integration Tests (Real Control Protocol)

```python
@pytest.mark.tier2
async def test_approval_prompt_via_control_protocol():
    """Test approval prompt with real CLI transport"""
    transport = CLITransport()
    protocol = ControlProtocol(transport)
    await protocol.start()

    approval_manager = ToolApprovalManager(protocol)

    # Simulate user approval in background
    async def approve():
        await asyncio.sleep(0.1)
        # Send approval response
        ...

    asyncio.create_task(approve())

    approved = await approval_manager.request_approval(
        "Bash", {"command": "ls"}, ExecutionContext()
    )

    assert approved is True
```

### Tier 3: E2E Tests (Real Agents)

```python
@pytest.mark.tier3
async def test_agent_with_budget_limit():
    """Test agent respects budget limit with real Ollama"""
    config = BaseAgentConfig(
        llm_provider="ollama",
        model="llama2",
        budget_limit_usd=0.0,  # Free local model
    )

    agent = SimpleQAAgent(config=config)

    # Should work (Ollama is free)
    result = agent.ask("What is 2+2?")
    assert result["answer"] == "4"
```

## Implementation Plan

**Phase 2 Timeline**: 10 weeks (Weeks 13-22)

| Week | Tasks |
|------|-------|
| 13-14 | Implement types (PermissionMode, PermissionRule, ExecutionContext) |
| 15-16 | Implement PermissionPolicy (rules engine) |
| 17 | Implement BudgetEnforcer |
| 18-19 | Implement ToolApprovalManager + Control Protocol integration |
| 20 | BaseAgent integration + specialist tool restrictions |
| 21 | Tests (Tier 1-3) + performance benchmarks |
| 22 | Documentation + examples |

**Deliverables**:
- [ ] Permission types (~200 lines)
- [ ] PermissionPolicy (~400 lines)
- [ ] ToolApprovalManager (~200 lines)
- [ ] BudgetEnforcer (~150 lines)
- [ ] BaseAgent integration (~150 lines)
- [ ] 80+ tests (Tier 1-3)
- [ ] 3 example applications
- [ ] Comprehensive documentation

## Documentation Requirements

- [ ] **ADR** (this document)
- [ ] **API Reference**: `docs/reference/permission-system-api.md`
- [ ] **User Guide**: `docs/guides/permission-modes.md`
- [ ] **Best Practices**: `docs/guides/safe-autonomous-agents.md`
- [ ] **Troubleshooting**: `docs/reference/permission-errors.md`

## Dependencies

**This ADR depends on**:
- ADR-007: Control Protocol (for approval prompts)
- ADR-009: Specialist System (for tool restrictions)

**Other ADRs depend on this**:
- ADR-010: Hooks System (PreToolUse hooks check permissions)
- ADR-009: State Persistence (checkpoint ExecutionContext)

## References

1. **Gap Analysis**: `.claude/improvements/CLAUDE_AGENT_SDK_KAIZEN_GAP_ANALYSIS.md` (Section 2.2)
2. **Implementation Proposal**: `.claude/improvements/KAIZEN_AUTONOMOUS_AGENT_ENHANCEMENT_PROPOSAL.md` (Section 3.3.2)
3. **Architectural Patterns**: `.claude/improvements/ARCHITECTURAL_PATTERNS_ANALYSIS.md` (Section 4)
4. **ADR-007**: Control Protocol Architecture
5. **ADR-009**: Specialist System & User-Defined Capabilities

## Approval

**Proposed By**: Kaizen Architecture Team
**Date**: 2025-10-18
**Reviewers**: TBD
**Status**: Proposed (awaiting team review)

---

**Next ADR**: ADR-010: Hooks System Architecture (PreToolUse hooks use permission system)
