# 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...") ```