# Decision Correction Reference

## Overview

The correction subsystem allows operators to modify a plan's decision tree
after execution by either **reverting** a subtree of decisions or
**appending** new decisions as a child plan.

## Correction Modes

### Revert

Invalidates the targeted decision and every descendant reachable via
BFS traversal.  Associated artifacts are archived and affected child
plans are rolled back.

```text
          ┌─── D1 (target) ◄── revert starts here
          │       │
          │    ┌──┴──┐
          │    D2    D3          ← all invalidated
          │          │
          │          D4          ← also invalidated
```

### Append

Preserves the original decision and spawns a **new child plan** rooted
at the target node.  The child plan carries the operator's guidance and
produces additional decisions without disturbing the existing tree.

```text
          D1 (target)
           │
        ┌──┴──────────┐
        D2 (original)  CP-new   ← child plan appended
```

## Impact Analysis — BFS Algorithm

Impact analysis uses breadth-first search over the decision tree's
adjacency list (parent → children mapping):

```python
from collections import deque

def compute_affected_subtree(target_id, tree):
    affected = []
    queue = deque([target_id])
    while queue:
        node = queue.popleft()
        affected.append(node)
        queue.extend(tree.get(node, []))
    return affected
```

### Risk Classification

| Affected Count | Risk Level |
|----------------|------------|
| ≤ 3            | low        |
| 4 – 10         | medium     |
| > 10           | high       |

## Dry-Run Output

A dry-run report contains:

- **impact** — Full `CorrectionImpact` with affected decisions, files,
  child plans, estimated cost, and risk level.
- **decisions_to_invalidate** — Decision IDs that *would* be marked
  invalid (revert mode only).
- **child_plans_to_rollback** — Child plans that *would* be rolled back.
- **estimated_recompute_time_seconds** — Wall-clock estimate.
- **warnings** — Human-readable cautions (e.g. high risk).

## Status Lifecycle

```text
PENDING → ANALYZING → EXECUTING → APPLIED
                                 → FAILED
         PENDING → CANCELLED
         ANALYZING → CANCELLED
```

## Service API

| Method                      | Description                                   |
|-----------------------------|-----------------------------------------------|
| `request_correction()`      | Create a new correction request               |
| `analyze_impact()`          | BFS impact analysis on the decision tree      |
| `generate_dry_run_report()` | Full report without side effects               |
| `execute_revert()`          | Invalidate subtree + archive artifacts         |
| `execute_append()`          | Spawn child plan preserving original decision  |
| `execute_correction()`      | Dispatch to revert or append based on mode     |
| `get_correction()`          | Retrieve a correction by ID                    |
| `list_corrections()`        | List corrections (optional plan_id filter)     |
| `list_attempts()`           | List execution attempts for a correction       |
| `cancel_correction()`       | Cancel a pending/analyzing correction          |

---

## Correction Attempts (v3.8.0+)

Each execution of a correction is tracked as a `CorrectionAttemptRecord`.
Multiple attempts may exist for a single correction (e.g. if a first attempt
fails and the operator retries).

### `CorrectionAttemptRecord`

**Module:** `cleveragents.domain.models.core.correction_attempt`

| Field | Type | Description |
|-------|------|-------------|
| `id` | `str` | ULID of this attempt |
| `correction_id` | `str` | Parent correction ULID |
| `plan_id` | `str` | Plan this correction targets |
| `original_decision_id` | `str` | Decision being corrected |
| `new_decision_id` | `str \| None` | Replacement decision (set on success) |
| `mode` | `CorrectionMode` | `revert` or `append` |
| `state` | `CorrectionAttemptState` | Current state (see lifecycle below) |
| `guidance` | `str` | Operator guidance text (1–10,000 chars) |
| `created_at` | `datetime` | UTC creation timestamp |
| `completed_at` | `datetime \| None` | UTC completion timestamp (terminal states only) |
| `archived_artifacts_path` | `str \| None` | Path to archived artifacts (revert mode) |

### `CorrectionAttemptState` Lifecycle

```text
pending → executing → complete
                    → failed
```

Valid transitions:

| From | To | Notes |
|------|----|-------|
| `pending` | `executing` | Attempt begins execution |
| `executing` | `complete` | Correction applied successfully |
| `executing` | `failed` | Correction failed; `completed_at` auto-set |

Terminal states (`complete`, `failed`) cannot transition further.
Self-transitions are rejected.

### `CorrectionAttemptRepository`

**Module:** `cleveragents.infrastructure.repositories.correction_attempt`

| Method | Description |
|--------|-------------|
| `create(record)` | Persist a new attempt; raises `DatabaseError` on FK violation |
| `get(attempt_id)` | Retrieve by ULID; raises `ResourceNotFoundError` if absent |
| `list_by_plan(plan_id)` | List all attempts for a plan (ordered by `created_at`) |
| `update_state(attempt_id, new_state, *, new_decision_id, completed_at, archived_artifacts_path)` | Transition state; auto-sets `completed_at` for terminal states |
| `delete(attempt_id)` | Hard-delete an attempt record |

### Usage Example

```python
from cleveragents.domain.models.core.correction_attempt import (
    CorrectionAttemptRecord,
    CorrectionAttemptState,
)
from cleveragents.domain.models.core.correction import CorrectionMode

attempt = CorrectionAttemptRecord(
    id="01HXYZ...",
    correction_id="01HABC...",
    plan_id="01HDEF...",
    original_decision_id="01HGHI...",
    mode=CorrectionMode.REVERT,
    state=CorrectionAttemptState.PENDING,
    guidance="Revert the database migration decision and retry with a safer approach.",
)

# Transition to executing
repo.update_state(attempt.id, CorrectionAttemptState.EXECUTING)

# Complete the attempt
repo.update_state(
    attempt.id,
    CorrectionAttemptState.COMPLETE,
    new_decision_id="01HJKL...",
    archived_artifacts_path="/var/cleveragents/archives/01HXYZ",
)
```

### Database Schema

The `correction_attempts` table uses `RESTRICT` foreign keys for both
`original_decision_id` and `new_decision_id`, preserving the correction
audit trail when decisions are cleaned up. The ORM-level
`cascade="all, delete-orphan"` on `LifecyclePlanModel` ensures that
deleting a plan cascades to its correction attempts.
