# Decision Domain Model

The decision subsystem records every choice point in a plan's lifecycle
as a **Decision** node in a persistent tree.  Decisions are created
during the Strategize and Execute phases and form the basis for
targeted correction and replay.

## Decision Types

| Type                     | Phase      | Description                               |
|--------------------------|------------|-------------------------------------------|
| `prompt_definition`      | Strategize | Root decision — the plan prompt           |
| `invariant_enforced`     | Strategize | An invariant constraint was applied       |
| `strategy_choice`        | Strategize | High-level approach chosen                |
| `implementation_choice`  | Execute    | How to implement a specific task          |
| `resource_selection`     | Execute    | Which resources to read / modify          |
| `subplan_spawn`          | Strategize | Decision to create a child plan           |
| `subplan_parallel_spawn` | Strategize | Spawn a group of child plans in parallel  |
| `tool_invocation`        | Execute    | Which skill / tool to use                 |
| `error_recovery`         | Execute    | How to handle a failure                   |
| `validation_response`    | Execute    | Response to a validation failure          |
| `user_intervention`      | Any        | User-provided guidance / correction       |

## Decision Fields

### Identity

- **decision_id** — ULID, auto-generated
- **plan_id** — ULID of the parent plan (required)
- **parent_decision_id** — ULID of the parent decision, or `None` for the root
- **sequence_number** — monotonic order within the plan (0-indexed, never reused)

### Classification

- **decision_type** — one of the 11 `DecisionType` enum values

### Content

- **question** — what question was being answered (required, non-empty)
- **chosen_option** — the option that was selected (required, non-empty)
- **alternatives_considered** — list of other options evaluated
- **confidence_score** — float 0.0–1.0, or `None` if not applicable

### Context Snapshot

Every decision captures a `ContextSnapshot` for replay:

- **hot_context_hash** — cryptographic hash of the context window
- **hot_context_ref** — storage pointer to the full serialised context
- **relevant_resources** — list of `ResourceRef` entries (resource_id + optional path)
- **actor_state_ref** — LangGraph actor checkpoint reference

### Rationale

- **rationale** — human-readable explanation
- **actor_reasoning** — raw LLM reasoning trace, if available

### Downstream Impact

- **downstream_decision_ids** — ULIDs of decisions that depend on this one
- **downstream_plan_ids** — ULIDs of child plans spawned from this decision
- **artifacts_produced** — list of `ArtifactRef` (artifact_path + artifact_type)

### Timestamps

- **created_at** — UTC datetime, auto-set on creation

### Correction Metadata

- **is_correction** — boolean, True if this decision corrects another
- **corrects_decision_id** — ULID of the original decision
- **correction_reason** — why the correction was made
- **superseded_by** — ULID of the decision that replaced this one

## Tree Structure

Decisions form a tree via `parent_decision_id`:

```
prompt_definition (root, parent=None)
├── invariant_enforced
├── strategy_choice
│   ├── implementation_choice
│   │   ├── resource_selection
│   │   └── tool_invocation
│   └── subplan_spawn
└── strategy_choice
```

The `prompt_definition` type is always the root and must have
`parent_decision_id = None`.  This is enforced by a model validator.

## Correction Lifecycle

Corrections never mutate existing decisions.  Instead:

1. A new `Decision` is created with `is_correction=True` and
   `corrects_decision_id` pointing to the original.
2. The original decision has its `superseded_by` field set to the
   new decision's ID.
3. All downstream decisions of the original are also superseded.

The **current tree** consists of all decisions where
`superseded_by IS NULL`.

## Validation Rules

- `plan_id` must be a valid ULID
- `parent_decision_id`, `corrects_decision_id`, `superseded_by` must
  be valid ULIDs or None
- `confidence_score` must be in [0.0, 1.0] or None
- `prompt_definition` decisions must have `parent_decision_id = None`
- If `is_correction` is True, `corrects_decision_id` must be set
- If `corrects_decision_id` is set, `is_correction` must be True

## Source

- Domain model: `src/cleveragents/domain/models/core/decision.py`
- Specification: `docs/specification.md` L18390–L18521
- ADR-007: Decision tree and correction
- ADR-033: Decision recording protocol
- ADR-034: Decision tree versioning and history
