# Skill Protocol Reference

The **Skill Protocol** defines the type contracts used for skill discovery,
composition, execution, and error reporting within CleverAgents v3.

All protocol types are frozen Pydantic `BaseModel` instances, ensuring
immutability after construction.

---

## Overview

| Type | Purpose |
|------|---------|
| `SkillErrorType` | `StrEnum` of all skill error categories |
| `SkillError` | Structured error payload |
| `SkillMetadata` | Lightweight discovery descriptor |
| `SkillDefinition` | Full skill + resolved tools + metadata |
| `SkillResult` | Outcome of a skill-tool invocation |

---

## SkillErrorType

A `StrEnum` with the following members:

| Value | Description |
|-------|-------------|
| `skill_not_found` | The requested skill does not exist |
| `resolution_failure` | Skill resolution failed (missing include, etc.) |
| `cycle_detected` | Circular dependency in skill includes |
| `tool_activation_failure` | Tool could not be activated |
| `tool_execution_failure` | Tool execution failed at runtime |
| `validation_error` | Input/output schema validation failed |
| `permission_denied` | Operation not permitted |

---

## SkillError

Structured error payload for skill operations.

### Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `error_type` | `SkillErrorType` | Yes | Error category |
| `message` | `str` | Yes | Human-readable description |
| `details` | `dict[str, Any]` | No | Diagnostic context |
| `skill_name` | `str` | Yes | Originating skill |
| `tool_name` | `str \| None` | No | Originating tool |

---

## SkillMetadata

Lightweight metadata for skill discovery and catalogue listings.

### Fields

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `name` | `str` | — | Namespaced skill name |
| `description` | `str` | — | Skill description |
| `version` | `str` | `"0.0.0"` | Semantic version |
| `tool_count` | `int` | `0` | Number of resolved tools |
| `capability_summary` | `SkillCapabilitySummary` | empty | Aggregated capabilities |
| `source_types` | `list[str]` | `[]` | Distinct source type labels |
| `writes` | `bool` | `False` | Any tool can write |
| `read_only` | `bool` | `True` | Entire skill is read-only |

### Factory: `from_skill()`

```python
meta = SkillMetadata.from_skill(skill, resolved=resolved_entries, version="1.0.0")
```

Derives `writes` and `read_only` from the `SkillCapabilitySummary`:

- `writes = True` when `write_tools > 0` or `has_side_effects` is true
- `read_only = True` when `write_tools == 0` and no side effects

### Writes / Read-Only Gating

The `writes` and `read_only` flags are propagated to the ToolRuntime
gating layer.  A skill marked `read_only=True` will never be allowed
to trigger a write-capable tool at runtime.

---

## SkillDefinition

Full skill definition combining the domain `Skill` model, resolved tools,
and pre-computed metadata.

### Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `skill` | `Skill` | Yes | Domain-level Skill model |
| `resolved_tools` | `list[ResolvedToolEntry]` | No | Flattened tool set |
| `metadata` | `SkillMetadata` | Yes | Pre-computed metadata |
| `input_schema` | `dict \| None` | No | JSON Schema for inputs |
| `output_schema` | `dict \| None` | No | JSON Schema for outputs |

### JSON Schema Validation

When `input_schema` or `output_schema` is provided, it **must** include a
`"type"` key to be considered a valid JSON Schema object.  The validator
rejects schemas missing this key at construction time.

```json
{
  "type": "object",
  "properties": {
    "query": {"type": "string"}
  }
}
```

### Writes Consistency

The model validator checks that if `metadata.read_only` is `True`, none
of the resolved inline tools declare `writes=True`.  A mismatch raises
a `ValidationError`.

---

## SkillResult

Outcome of a single skill-tool invocation.

### Fields

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `skill_name` | `str` | — | Invoked skill |
| `tool_name` | `str` | — | Invoked tool |
| `success` | `bool` | — | Whether invocation succeeded |
| `output_data` | `Any` | `None` | Tool output |
| `error` | `SkillError \| None` | `None` | Error on failure |
| `duration_ms` | `float` | `0.0` | Execution time in ms |
| `resource_changes` | `list[dict]` | `[]` | Resource change descriptors |

---

## Error Mapping

The `map_tool_error()` helper normalises arbitrary exceptions into
`SkillError` payloads:

```python
from cleveragents.skills.protocol import map_tool_error

try:
    run_tool(...)
except Exception as exc:
    error = map_tool_error(exc, skill_name="local/my-skill", tool_name="local/my-tool")
```

### Mapping Rules

| Exception Type | Message Contains | Mapped To |
|----------------|------------------|-----------|
| `ValueError` | `"cycle"` | `CYCLE_DETECTED` |
| `ValueError` | `"not found"` | `SKILL_NOT_FOUND` |
| `ValueError` | (other) | `RESOLUTION_FAILURE` |
| `PermissionError` | — | `PERMISSION_DENIED` |
| `ValidationError` | — | `VALIDATION_ERROR` |
| Any | `"activation"` | `TOOL_ACTIVATION_FAILURE` |
| Any | (other) | `TOOL_EXECUTION_FAILURE` |

---

## JSON Schema Rules Summary

1. **Input/output schemas** on `SkillDefinition` must include `"type"`.
2. **Writes/read_only metadata** on `SkillMetadata` must be consistent
   with the resolved tool capabilities.
3. **Error payloads** always include `skill_name`; `tool_name` is
   optional but recommended.
4. All protocol types are **frozen** (immutable after construction).
