--- name: specx-diwire-composition description: Wire dependency injection for a specx Python service with `diwire`. Use when adding `ioc/container.py`, explicit dependency registrations for capabilities, repositories, gateways, UoW managers, clients, settings, or factories, `Injected[...]` constructor fields, FastAPI app factory/lifecycle composition, test overrides, or rules that keep containers out of core use cases and services. --- # specx Diwire Composition Use this skill whenever object graphs, factories, controllers, adapters, or test overrides need `diwire`. Read `references/diwire.md` before writing DI code. ## Rules - Application classes receive dependencies through constructor fields typed as `Injected[DependencyType]`. - Keep injection constructor-based. Do not hide dependency resolution in application functions with `resolver_context.inject`. - Capabilities are normal injectable collaborators. Register only capability abstractions or existing instances that auto-wiring cannot infer. - Only `ioc/`, top-level delivery `__main__.py`/factory/lifecycle modules, and tests create or use `diwire.Container`. - Do not inject `Container` except in `FastAPILifecycle`, where it is used only to call `container.aclose()` during app shutdown. - Do not inject `logging.Logger` or register loggers in the container. Runtime logging is configured once by a top-level infrastructure configurator; classes that log create local stdlib loggers. - Do not call `container.resolve()` inside core use cases or services. - Let `diwire` auto-wire concrete project classes. - Let `diwire` auto-register `BaseRuntimeSettings` subclasses as root-scoped singleton factories. Register a settings instance only for an intentional test override or an explicitly prebuilt configuration object. - Register only abstractions, external adapter bindings, gateway implementations, factories, and instances that auto-wiring cannot infer. Keep registration in private `_register_dependencies(...)` inside `ioc/container.py`. - Register the container instance itself with `provides=Container` only for the FastAPI lifecycle shutdown dependency. - Register concrete gateway implementations for their core `BaseGateway` ports. - Register health readiness gateway implementations, such as `SQLAlchemyReadinessCheckGateway`, for `ReadinessCheckGateway` when `/readyz` needs infrastructure checks. - Register unit-of-work managers for UoW abstractions. Persistence use cases inject the manager and open the active UoW inside `execute(...)`; they do not inject repositories, active UoWs, providers, SQLAlchemy sessions, engines, session factories, or concrete infrastructure adapters directly. - Unit tests receive a fresh native pytest `container` fixture built by the project's real `get_container()`. Put project-wide test overrides in that fixture and register scenario-specific overrides directly in the test before resolving the target. - Integration tests receive the transactional real-app `container` fixture. They must use the real internal graph; do not mock internal use cases, services, or capabilities. - Use `container.resolve(...)` for normal synchronous graph construction, even for targets with async methods; use `await container.aresolve(...)` only when DI construction itself has async providers. - One-off class-based test doubles live in the `test_*.py` module that uses them. Reused unit-test doubles may live in mirrored `tests/unit/core//{capabilities,gateways,repositories}/fake_.py` modules. - Inline `MagicMock` or `AsyncMock` in the test function when only one behavior needs to change. - Do not create DI-backed test factories, target harnesses, `harness.py`, `tests/_support/fakes`, `tests/**/_fakes.py`, or fake modules outside those mirrored unit port/capability packages. - Keep tests on native pytest fixtures. Do not enable `diwire.integrations.pytest_plugin`, and do not use `Injected[...]` parameters in test functions. - FastAPI async test helpers must enter `LifespanManager` and pass the yielded manager's state-aware `manager.app` to the HTTPX2 `ASGITransport`. ## Code Style Use blank lines as logical separators in all code. Keep related statements together, but separate independent setup, action, assertion, response, branch, and transformation groups so long blocks stay readable. ## References - `references/diwire.md` - container code, registration examples, controller composition, and test override patterns.