# Dependency Injection Reference

The CleverAgents DI container is built on `dependency-injector`'s
`DeclarativeContainer`.  All service registrations live in
`src/cleveragents/application/container.py`.

## Container Overview

| Provider                      | Type      | Description                             |
|-------------------------------|-----------|-----------------------------------------|
| `settings`                    | Singleton | Application settings                    |
| `provider_registry`           | Singleton | AI provider registry                    |
| `database_url`                | Callable  | Database URL resolution                 |
| `unit_of_work`                | Factory   | Unit of Work (new per request)          |
| `ai_provider`                 | Singleton | AI provider instance                    |
| `project_service`             | Factory   | Project management service              |
| `vector_store_service`        | Factory   | Vector store operations                 |
| `context_service`             | Factory   | Context management service              |
| `actor_service`               | Factory   | Actor configuration service             |
| `actor_registry`              | Factory   | Actor registry with provider binding    |
| `plan_service`                | Factory   | Legacy plan management (deprecated)     |
| **`decision_service`**        | Factory   | Decision tree recording service         |
| **`plan_lifecycle_service`**  | Factory   | V3 four-phase plan lifecycle            |
| `resource_registry_service`   | Factory   | Resource registry operations            |
| `namespaced_project_repo`     | Factory   | Namespaced project repository           |
| `project_resource_link_repo`  | Factory   | Project-resource link repository        |
| `stream_router`               | Singleton | Reactive stream routing                 |
| `langgraph_bridge`            | Singleton | LangGraph integration bridge            |
| `route_bridge`                | Singleton | Reactive route bridge                   |

## Decision Service Registration

The `DecisionService` is registered as a **Factory** provider, meaning
each resolution returns a new instance.  It depends on:

- `settings` — Application settings (Singleton)
- `unit_of_work` — Unit of Work (Factory)

```python
decision_service = providers.Factory(
    DecisionService,
    settings=settings,
    unit_of_work=unit_of_work,
)
```

`DecisionService` wraps `DecisionRepository` (accessible via
`UnitOfWorkContext.decisions`) and provides:

| Method                  | Description                                |
|-------------------------|--------------------------------------------|
| `record_decision()`    | Persist a new decision                     |
| `get_decision()`       | Retrieve a decision by ID                  |
| `list_decisions()`     | Get all decisions for a plan              |
| `get_tree()`           | BFS traversal of the decision tree         |
| `get_path_to_root()`   | Walk from a decision up to the root        |
| `mark_superseded()`    | Mark a decision as superseded              |
| `list_by_type()`       | List decisions by type for a plan          |

## Plan Lifecycle Service Registration

The `PlanLifecycleService` is registered as a **Factory** provider
with the `DecisionService` injected:

```python
plan_lifecycle_service = providers.Factory(
    PlanLifecycleService,
    settings=settings,
    unit_of_work=unit_of_work,
    decision_service=decision_service,
)
```

When a `DecisionService` is wired, the lifecycle service automatically
records decisions during phase transitions:

- **start_strategize** → records a `strategy_choice` decision
- **start_execute** → records an `implementation_choice` decision

If no `DecisionService` is provided (e.g. in legacy tests), decision
recording is silently skipped.

### Cache-Sharing Caveat

Because `plan_lifecycle_service` is a **Factory**, each
`container.plan_lifecycle_service()` call returns a **new instance**
with its own in-memory `_plans` cache.  Code that creates a
`PlanLifecycleService` and separately constructs a `PlanExecutor`
**must** pass the same service instance to both; otherwise the
executor's phase mutations (e.g. `complete_strategize` →
`auto_progress` → `execute/queued`) will not be visible to the
caller's service instance, producing stale state reads.

```python
# Correct: shared instance
service = container.plan_lifecycle_service()
executor = PlanExecutor(lifecycle_service=service, ...)

# Wrong: two independent instances with separate caches
service = container.plan_lifecycle_service()     # Instance A
executor = PlanExecutor(
    lifecycle_service=container.plan_lifecycle_service(),  # Instance B
    ...
)
```

The CLI helper `_get_plan_executor(lifecycle_service=...)` accepts an
optional pre-existing service for exactly this purpose.  See
[CLI Executor Wiring](plan_execute.md#cli-executor-wiring-_get_plan_executor).

## Usage Example

```python
from cleveragents.application.container import get_container

container = get_container()

# Resolve services
decision_svc = container.decision_service()
lifecycle_svc = container.plan_lifecycle_service()

# Record a decision manually
from cleveragents.domain.models.core.decision import DecisionType

decision = decision_svc.record_decision(
    plan_id="01HV...",
    decision_type=DecisionType.STRATEGY_CHOICE,
    question="Which approach?",
    chosen_option="Approach A",
)

# Retrieve decisions
decisions = decision_svc.list_decisions("01HV...")
```
