# `cleveragents.action` — Action Configuration Schema

The `action` package provides the YAML configuration schema, validation, and
loading utilities for CleverAgents action definitions.

Actions are the top-level automation units in CleverAgents: each action binds
lifecycle actors, typed arguments, invariants, and completion criteria into a
named, reusable workflow template.

See the [Action Configuration Files](../schema/action.schema.yaml) schema and
`examples/actions/` for reference examples.

---

## Quick Start

```python
from cleveragents.action.schema import ActionConfigSchema

# Parse from a YAML string
config = ActionConfigSchema.from_yaml("""
name: local/my-action
description: "Refactor a Python module"
strategy_actor: openai/gpt-4o
execution_actor: openai/gpt-4o-mini
definition_of_done: "All tests pass and coverage >= 90%"
arguments:
  - name: target_module
    type: string
    required: true
    description: "Module path to refactor"
invariants:
  - "Do not modify public API signatures"
""")

print(config.name)          # "local/my-action"
print(config.arguments[0])  # ActionArgumentSchema(name='target_module', ...)

# Load from a file
config = ActionConfigSchema.from_yaml_file("examples/actions/refactor.yaml")
```

---

## `ActionConfigSchema`

```python
from cleveragents.action.schema import ActionConfigSchema
```

Pydantic model that validates a complete action YAML configuration.

### Factory Methods

#### `ActionConfigSchema.from_yaml(yaml_string: str) → ActionConfigSchema`

Parse and validate an action YAML string.

| Parameter | Type | Description |
|-----------|------|-------------|
| `yaml_string` | `str` | Raw YAML content |

**Returns:** Validated `ActionConfigSchema` instance.

**Raises:**
- `ValueError` — if the YAML is empty, `None`, or not a mapping
- `pydantic.ValidationError` — if schema validation fails

**Processing pipeline:**
1. Parse YAML with `yaml.safe_load`
2. Normalize camelCase keys → snake_case (with deprecation warnings)
3. Interpolate `${ENV_VAR}` placeholders from environment variables
4. Validate with Pydantic

---

#### `ActionConfigSchema.from_yaml_file(path: str | Path) → ActionConfigSchema`

Load and validate an action YAML file from disk.

| Parameter | Type | Description |
|-----------|------|-------------|
| `path` | `str \| Path` | Path to the YAML file |

**Returns:** Validated `ActionConfigSchema` instance.

**Raises:**
- `FileNotFoundError` — if the file does not exist
- `ValueError` — if the path is `None` or does not point to a regular file
- `pydantic.ValidationError` — if schema validation fails

---

### Fields

#### Identity

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `schema_version` | `str` | No (default: `"1"`) | Schema version for forward compatibility |
| `name` | `str` | **Yes** | Namespaced action name — `namespace/name` format |
| `description` | `str` | **Yes** | Short description of the action |
| `long_description` | `str \| None` | No | Detailed multi-line description |

The `name` field must match `^[a-z0-9][a-z0-9_-]*/[a-z0-9][a-z0-9_-]*$`.
Examples: `local/my-action`, `builtin/refactor`.

#### Lifecycle Actors

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `strategy_actor` | `str` | **Yes** | Actor for the Strategize phase |
| `execution_actor` | `str` | **Yes** | Actor for the Execute phase |
| `estimation_actor` | `str \| None` | No | Optional actor for cost/risk estimation |
| `review_actor` | `str \| None` | No | Optional actor for code review / QA |
| `apply_actor` | `str \| None` | No | Optional actor for the Apply phase |
| `invariant_actor` | `str \| None` | No | Optional actor for invariant reconciliation |

All actor names must follow the `namespace/name` format.

#### Completion Criteria

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `definition_of_done` | `str` | **Yes** | Testable criteria defining task completion |

#### Behaviour

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `reusable` | `bool` | `True` | Whether the action persists after use |
| `read_only` | `bool` | `False` | Restrict the action to read-only operations |
| `state` | `str` | `"available"` | Action state: `available` or `archived` |

#### Arguments

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `arguments` | `list[ActionArgumentSchema]` | `[]` | Typed arguments accepted by the action |

#### Automation & Invariants

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `automation_profile` | `str \| None` | `None` | Default automation profile for derived plans |
| `invariants` | `list[str]` | `[]` | Constraints carried as plan-level invariants |
| `inputs_schema` | `dict[str, Any] \| None` | `None` | Optional JSON Schema for advanced input validation |

Invariants are automatically trimmed, de-duplicated, and blank entries are
dropped during validation.

---

## `ActionArgumentSchema`

```python
from cleveragents.action.schema import ActionArgumentSchema
```

Pydantic model for a single typed action argument. Arguments are values
supplied by the user at plan-use time (`agents plan use --arg name=value`).

### Fields

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `name` | `str` | **Required** | Argument name (1–64 chars, valid identifier) |
| `type` | `str` | `"string"` | Data type: `string`, `integer`, `float`, `boolean`, `list` |
| `required` | `bool` | `False` | Whether the argument must be provided |
| `description` | `str` | `""` | Human-readable description (max 500 chars) |
| `default` | `str \| int \| float \| bool \| list[str] \| None` | `None` | Default value when argument is not provided |
| `validation_pattern` | `str \| None` | `None` | Regex pattern for string argument validation |
| `min_value` | `float \| None` | `None` | Minimum value for numeric arguments |
| `max_value` | `float \| None` | `None` | Maximum value for numeric arguments |

The `name` field must be a valid Python identifier (alphanumeric, underscores,
or hyphens).

---

## camelCase Compatibility

The schema accepts both `snake_case` and `camelCase` keys for backward
compatibility. camelCase keys are automatically normalized with a deprecation
warning:

| camelCase | snake_case |
|-----------|------------|
| `strategyActor` | `strategy_actor` |
| `executionActor` | `execution_actor` |
| `definitionOfDone` | `definition_of_done` |
| `longDescription` | `long_description` |
| `estimationActor` | `estimation_actor` |
| `reviewActor` | `review_actor` |
| `applyActor` | `apply_actor` |
| `invariantActor` | `invariant_actor` |
| `readOnly` | `read_only` |
| `automationProfile` | `automation_profile` |
| `inputsSchema` | `inputs_schema` |
| `validationPattern` | `validation_pattern` |
| `minValue` | `min_value` |
| `maxValue` | `max_value` |
| `schemaVersion` | `schema_version` |

---

## Environment Variable Interpolation

String values in the YAML may reference environment variables using
`${VAR_NAME}` syntax. Missing variables are left as-is (no error):

```yaml
name: local/deploy
description: "Deploy to ${TARGET_ENV}"
strategy_actor: openai/gpt-4o
execution_actor: openai/gpt-4o-mini
definition_of_done: "Deployment to ${TARGET_ENV} verified"
```

```python
import os
os.environ["TARGET_ENV"] = "staging"
config = ActionConfigSchema.from_yaml(yaml_string)
# config.description == "Deploy to staging"
```

---

## Database Integrity Note

When updating an action that already has arguments or invariants registered,
`ActionRepository.update()` uses explicit `sa_delete()` + `session.flush()`
before re-inserting child rows. This avoids the SQLAlchemy `.clear()` +
`.append()` pattern that could trigger a `UNIQUE constraint failed` error
on `(action_name, name)` pairs. See [#4197](https://git.cleverthis.com/cleveragents/cleveragents-core/pulls/4197).

---

## Complete Example

```yaml
# examples/actions/refactor.yaml
schema_version: "1"
name: local/refactor
description: "Refactor a Python module to improve code quality"
long_description: |
  Analyses the target module for code smells, applies refactoring
  patterns, and ensures all tests continue to pass.

strategy_actor: openai/gpt-4o
execution_actor: openai/gpt-4o-mini
review_actor: anthropic/claude-4-sonnet
estimation_actor: local/estimator

definition_of_done: |
  - All existing tests pass
  - Coverage >= 90%
  - No new linting errors
  - Public API unchanged

reusable: true
read_only: false
state: available

arguments:
  - name: target_module
    type: string
    required: true
    description: "Python module path to refactor (e.g. src/myapp/utils.py)"
  - name: max_complexity
    type: integer
    required: false
    default: 10
    description: "Maximum cyclomatic complexity threshold"
    min_value: 1
    max_value: 50

invariants:
  - "Do not modify public API signatures"
  - "Preserve all existing test assertions"

automation_profile: standard
```
