# Resource Handlers

Resource handlers bridge resource types to sandbox provisioning. When a plan
executes, each tool's resource slot is resolved to a physical sandbox path
through the handler pipeline.

## Architecture

```
BindingResolutionService          ResourceHandlerService
   resolve(tool, project)            resolve_binding(binding, plan_id)
        │                                  │
        ▼                                  ▼
   BindingResult(resource_id)    ┌── Resource (location)
                                 │   ResourceTypeSpec (handler, strategy)
                                 │         │
                                 │         ▼
                                 │   resolve_handler("module:Class")
                                 │         │
                                 │         ▼
                                 │   handler.resolve(resource, sandbox_manager)
                                 │         │
                                 │         ▼
                                 └── BoundResource(sandbox_path="/tmp/sandbox/...")
```

## Handler Protocol

All handlers implement the `ResourceHandler` protocol:

```python
class ResourceHandler(Protocol):
    def resolve(
        self,
        *,
        resource: Resource,
        plan_id: str,
        slot_name: str,
        sandbox_manager: SandboxManager,
        access: str = "read_only",
    ) -> BoundResource: ...
```

## Built-in Handlers

### GitCheckoutHandler

Resolves `git-checkout` resources using the `git_worktree` sandbox strategy.

| Property | Value |
|----------|-------|
| Module | `cleveragents.resource.handlers.git_checkout` |
| Class | `GitCheckoutHandler` |
| Default strategy | `git_worktree` |
| Fallback strategy | `copy_on_write` |
| Required fields | `resource.location` (path to git repo root) |

### FsDirectoryHandler

Resolves `fs-directory` resources using the `copy_on_write` sandbox strategy.

| Property | Value |
|----------|-------|
| Module | `cleveragents.resource.handlers.fs_directory` |
| Class | `FsDirectoryHandler` |
| Default strategy | `copy_on_write` |
| Required fields | `resource.location` (path to directory) |

## Handler Resolution

Handler strings use the `module.path:ClassName` format and are stored on
`ResourceTypeSpec.handler`. Resolution is dynamic via `importlib`:

```python
from cleveragents.resource.handlers import resolve_handler

handler = resolve_handler(
    "cleveragents.resource.handlers.git_checkout:GitCheckoutHandler"
)
```

Resolved handlers are cached for the process lifetime. Call
`clear_handler_cache()` to reset.

### Fallback Behavior

If no handler string is set (or resolution fails), the
`ResourceHandlerService` uses a default handler that delegates directly
to `SandboxManager` using the type's `sandbox_strategy`.

## Strategy Precedence

The sandbox strategy is determined in this order:

1. **Resource-level override** (`resource.sandbox_strategy`) — per-resource
2. **Type default** (`ResourceTypeSpec.sandbox_strategy`) — per-type
3. **Fallback** (`none`) — no sandboxing

## ResourceHandlerService

The orchestration service that chains the resolution pipeline:

```python
from cleveragents.application.services.resource_handler_service import (
    ResourceHandlerService,
)

service = ResourceHandlerService(
    sandbox_manager=sandbox_manager,
    resource_lookup=registry_service.show_resource,
    type_lookup=registry_service.show_type,
)

# Resolve a single binding
bound = service.resolve_binding(binding, plan_id="01ARZ3...")

# Resolve all bindings for a tool
bindings_map = service.resolve_bindings(bindings, plan_id="01ARZ3...")
# -> {"repo": BoundResource(sandbox_path="/tmp/sandbox/...")}
```

## Sandbox Outputs

After resolution, each `BoundResource` carries:

| Field | Description |
|-------|-------------|
| `slot_name` | Tool slot this binding fills |
| `resource_id` | ULID of the resolved resource |
| `resource_type` | Type name (e.g. `git-checkout`) |
| `sandbox_path` | Root path of the provisioned sandbox |
| `access` | `read_only` or `read_write` |

The `sandbox_path` points to an isolated directory managed by
`SandboxManager`. Changes are committed or rolled back via
`SandboxManager.commit_all()` / `rollback_all()`.

## Writing Custom Handlers

To add a handler for a new resource type:

1. Create a module under `cleveragents/resource/handlers/`.
2. Implement a class satisfying `ResourceHandler`.
3. Register it on the `ResourceTypeSpec` via the `handler` field:
   `"cleveragents.resource.handlers.my_handler:MyHandler"`.

```python
class MyHandler:
    def resolve(
        self,
        *,
        resource: Resource,
        plan_id: str,
        slot_name: str,
        sandbox_manager: SandboxManager,
        access: str = "read_only",
    ) -> BoundResource:
        # Custom validation / setup
        sandbox = sandbox_manager.get_or_create_sandbox(
            plan_id=plan_id,
            resource_id=resource.resource_id,
            original_path=resource.location,
            sandbox_strategy="copy_on_write",
        )
        return BoundResource(
            slot_name=slot_name,
            resource_id=resource.resource_id,
            resource_type=resource.resource_type_name,
            sandbox_path=sandbox.context.sandbox_path if sandbox.context else "",
            access=access,
        )
```
