# Decision Service Reference

## Overview

`DecisionService` provides the application-layer interface for recording
decisions during plan execution, retrieving decision histories, and
managing context snapshots.  It lives in
`src/cleveragents/application/services/decision_service.py`.

## Dual-Mode Persistence

| Mode       | UnitOfWork | Storage                                        |
|------------|------------|------------------------------------------------|
| In-memory  | `None`     | Internal dicts (`_decisions`, `_plan_decisions`) |
| Persisted  | provided   | DB via `DecisionRepository` + in-memory cache  |

When a `UnitOfWork` is wired, mutations are written to the database
first, then the in-memory cache is updated (write-through).

## Constructor

```python
DecisionService(
    settings: Settings | None = None,
    unit_of_work: UnitOfWork | None = None,
)
```

- **settings** – Application settings (optional for in-memory mode).
- **unit_of_work** – When provided, decisions are persisted via
  `DecisionRepository`.

## Recording

### `record_decision()`

```python
svc.record_decision(
    plan_id="01HV...",
    decision_type=DecisionType.STRATEGY_CHOICE,   # or a string
    question="Which approach?",
    chosen_option="Build a REST API",
    parent_decision_id=None,            # optional
    alternatives_considered=None,       # list[str]
    confidence_score=None,              # 0.0-1.0
    rationale="",                       # human-readable
    actor_reasoning=None,               # raw LLM trace
    context_snapshot=None,              # auto-captured if omitted
    artifacts_produced=None,            # list[ArtifactRef]
    is_correction=False,
    corrects_decision_id=None,
    correction_reason=None,
)
```

- Auto-assigns a monotonically increasing **sequence number** per plan.
- Auto-generates a **ULID** for `decision_id`.
- Auto-captures a **context snapshot** (SHA-256 hash) when no explicit
  snapshot is provided.
- Stores the snapshot in the built-in `SnapshotStore`.

**Raises:**

- `ValidationError` – required fields missing or empty.
- `DuplicateDecisionError` – decision ID already exists.

## Retrieval

| Method                                | Description                                |
|---------------------------------------|--------------------------------------------|
| `get_decision(decision_id)`           | Single decision by ULID                    |
| `list_decisions(plan_id)`             | All decisions for a plan, ordered by seq   |
| `list_by_type(plan_id, decision_type)`| Filtered by type, ordered by seq           |

All raise `DecisionNotFoundError` when the target decision does not
exist (where applicable).

## Tree Operations

| Method                        | Description                              |
|-------------------------------|------------------------------------------|
| `get_tree(plan_id)`           | BFS from root(s), level by level         |
| `get_path_to_root(decision_id)` | Walk from a decision up to the root    |

## Superseded Decisions

| Method                                        | Description                           |
|-----------------------------------------------|---------------------------------------|
| `mark_superseded(decision_id, new_decision_id)` | Mark a decision as superseded       |
| `get_superseded(plan_id)`                      | List all superseded decisions        |

## Delete

```python
svc.delete_decision(decision_id)  # -> True
```

Removes from both persistence and the snapshot store.  Raises
`DecisionNotFoundError` if not found.

## Snapshot Store

`SnapshotStore` manages `ContextSnapshot` objects keyed by decision ID
with hash-based deduplication.

| Method                          | Description                                  |
|---------------------------------|----------------------------------------------|
| `get_snapshot(decision_id)`     | Retrieve snapshot for a decision             |
| `get_snapshots_for_plan(plan_id)` | All snapshots for a plan's decisions       |
| `snapshots.get_by_hash(hash)`   | Decision IDs sharing the same context hash  |
| `snapshots.store(id, snap)`     | Store a snapshot (internal)                  |
| `snapshots.remove(id)`          | Remove a snapshot                            |

## Statistics

| Method                        | Description                      |
|-------------------------------|----------------------------------|
| `count_decisions(plan_id)`    | Number of decisions for a plan   |
| `get_next_sequence(plan_id)`  | Next available sequence number   |

## Custom Exceptions

| Exception               | Base                     | Description                         |
|--------------------------|--------------------------|-------------------------------------|
| `DuplicateDecisionError` | `BusinessRuleViolation`  | Decision ID already exists          |
| `DecisionNotFoundError`  | `ResourceNotFoundError`  | Decision not found                  |
| `SequenceConflictError`  | `BusinessRuleViolation`  | Sequence number already used        |

## Running Tests

```bash
# Behave BDD tests
nox -s unit_tests -- features/decision_recording.feature

# Robot Framework integration smoke tests
nox -s integration_tests

# ASV benchmarks
nox -s benchmark
```

## Related

- Domain model: `src/cleveragents/domain/models/core/decision.py`
- Persistence: `src/cleveragents/infrastructure/database/repositories.py`
  (`DecisionRepository`)
- Database schema: `docs/reference/database_schema.md`
- ADR: `docs/adr/ADR-033-decision-recording-protocol.md`
