Orchestrata Ultimate › Plans › evidence › d4-di-container
D4 Evidence Pack — DI Container vs Setter Wiring
Source: plans/evidence/d4-di-container.md at 8a2cb42ad · last changed 2026-08-19 · first committed 2026-07-05 · 68 lines.
Prose pending.
Plan text
D4 Evidence Pack — DI Container vs Setter Wiring
Source: auditor agent W3.E4 (2026-07-06). Decision: plan §D, D4. Blocks W3.4. Agent-declared not-verified: gateway/rpc.py setter bodies (prior-session cites), session/cli submodules, whether DIContainer.shutdown()'s _context-vs-_singletons discrepancy is a live bug (FLAGGED — chase during W3.4), no tests executed.
Headline
8 of 9 real composition roots NEVER touch the 951-LOC DI container. The sole consumer (gateway/server.py) registers ONE instance and wires everything else via 9 setter calls. The container's flagship SPEC-024 FR-008 hierarchy feature is an UNIMPLEMENTED STUB (ContainerRegistry.get_with_parent, container.py:923 — body is get + comment + return) that docs/rtm.json marks VERIFIED via REQ-024-044 / REQ-003-US-008-022 — a false-confidence RTM entry (VERIFIED = "decorator present", not "functionally tested"). All 77 spec-024 RTM entries are VERIFIED.
Capability inventory (di/container.py)
register/register_instance with subclass validation; ComponentScope SINGLETON (double-checked locking, REQ-024-053) / REQUEST (per-context cache) / TRANSIENT; async resolve() with recursive ctor-dep walk + cycle detection (REQ-024-030) + AUTO-INVOKED IComponent.initialize(config) (REQ-024-016); shutdown() reverse-order stop() — but iterates self._context not self._singletons (possible live bug, unchased); ContainerBuilder (build() returns the same mutated instance despite "independent container" comment — doc/code mismatch; with_components() bypasses register() validation via private _registry writes); ContainerRegistry named-container map + the get_with_parent stub.
Composition census (only 3 import sites of di.container outside its package)
| Root | Mechanism |
|---|---|
| gateway/server.py | MIXED: DIContainer(:252) + 1 register_instance(:257) vs 9 setters into rpc.py (set_processor :222, set_memory_engine :258, set_test_runner :288, set_policy_engine_factory :308, set_kernel_client :359, set_pipeline_client :399, ...) |
| kernel/cli_main.py | hand-construction (boot* helpers; manual reversed-stop loop ~:267 duplicating container.shutdown()) |
| kernel/cli.py | hand-construction (OrchestrationKernel :53) |
| postman/cli.py | hand-construction (Postman :61) |
| agents/runtime_main.py | hand-construction (CXPServer :49). agents/di.py::register_agents = DEAD (0 importers, who-imports confirmed) |
| knowledge_graph/server.py | hand-construction (CXPServer :29) |
| root cli.py | N/A (FastAPI health stub) |
| session/cli/main.py | N/A top-level (click dispatch; submodules unaudited) |
| harness/cli.py | hand-construction (HarnessRunner, executors) |
SPEC-024 reality
Status "Implemented"; 13 MUST FRs. FR-008 parent-child hierarchy (spec L261-262, ERR-009 L293) is the unimplemented stub above. NO FR mandates the container as the exclusive composition mechanism ("assembly backbone" framing only) — so option (b) does not violate the spec's letter, but FR-008/FR-012 need descoping text.
Plugin SDK reality
plugin/context.py:114-136 resolve() is the ONLY container touchpoint (gated by a di_access capability that ZERO real plugin manifests declare — only unit tests). All plugin registration flows through plugin/registration.py registries, not DI. A ~20-30 LOC dict[type, Any] registry would fully serve this consumer.
Options
(a) Route all roots through container: >=6 boot files + resurrect dead agents/di.py
- BUILD FR-008 hierarchy (new functionality); 33 test files construct components directly (risk if lifecycle auto-init leaks). Medium-High effort. (b) Shrink to plugin+gateway scope, bless setter/ctor wiring: delete agents/di.py, get_with_parent/hierarchy surface, with_components() private-mutation path; keep register/resolve/register_instance; rewrite SPEC-024 (FR-008/012 descoped, honest); add architecture_checker rule (no new DIContainer() outside plugin/gateway; roots use explicit ctor/setter wiring). Low-Medium effort, mostly deletion.
RECOMMENDATION: (b)
Adoption evidence is decisive; the "universal composition" vision was aspirational (flagship FR stubbed, RTM claim false). Codify what 6/9 boot paths + 33 tests already do. Follow-ups regardless of choice: fix the false-VERIFIED RTM semantics for stub bodies (ties into W1.3/check-traces), chase the shutdown() singleton-reach question, delete dead agents/di.py.