Orchestrata Ultimate / Specs / 024-dependency-injection

024-dependency-injection: Dependency Injection Container

The DI container provides interface-based component registration, async resolution with constructor injection, scope management (singleton/transient/request), and circular dependency detection. Its scope is the plugin SDK's capability-gated resolve() touchpoint (plugin/context.py) and the WebSocket gateway's assembly root (gateway/server.py) — see the "Descoped (2026-07-06)" section below for why this is narrower than the original "universal composition backbone" framing.

At a glance

Item Value
Spec directory specs/024-dependency-injection/
Status line in the spec Implemented (scope reduced — see "Descoped" section)
Requirement IDs defined 53
With a @traces citation in src/ 49 (92.5%)
With a @traces citation in tests/ 0 (0.0%)
Tracked files 6
RTM page 024-dependency-injection

Reading this spec

The Dependency Injection Container provides interface-based component registration, async resolution with constructor injection, scope management (singleton, transient and request) and circular dependency detection. Its scope was deliberately reduced: it now serves the plugin SDK's capability-gated resolve() touchpoint and the WebSocket gateway's assembly root, and a "Descoped" section in the spec explains why. Every other composition root wires its dependencies explicitly, with constructor arguments and setter calls, per the architecture standards, and an architecture checker enforces that contract.

The scope is seven user stories: component registration, component resolution and circular dependency detection (priority 0), container builder, instance registration and container registry (priority 1), and introspection (priority 2). The functional requirements cover interface registration, scope enforcement, constructor injection, lifecycle integration, cycle detection, factory support, instance registration and context propagation. The container hierarchy requirement is struck through as removed in the descoping. Requirement IDs use the REQ-024- prefix.

The design is a DIContainer that maps interfaces to implementations with scope metadata, a fluent ContainerBuilder, an async resolve() that walks the dependency graph, detects cycles and creates instances, a ContainerRegistry of named containers, thread-safe registration and lock-free resolution. The reasons given are loose coupling, swappable implementations, testability through the same interfaces, and automatic IComponent.initialize() on resolution.

In the code, nearly all citations are in orchestrata_ultimate/di/container (49 requirements), with one in orchestrata_ultimate/core/protocols/base. The per-requirement view is on the RTM page.

The spec's status line reads Implemented, with the scope reduced as described. It tracks 6 files and defines 53 requirement IDs. The full text follows below.

Measured coverage is 49 of 53 requirements cited by at least one @traces in src/ (92.5%), spread over 2 modules; the module citing the most of them is orchestrata_ultimate/di/container (49 requirements). In tests/, 0 requirements are cited by a test-side @traces.

Coverage gaps: 4 requirements have no citing symbol. The largest untraced groups are REQ-024-* (4). Untraced means no @traces names the ID; the behaviour may still exist in code that was never annotated. Test-side citations are thinner than code-side ones (0 against 49).

Requirements

Checkbox is the state recorded in the requirements checklist; Traced by is the count of src/ symbols whose @traces names the ID.

ID Done Requirement Traced by
REQ-024-001 no DIContainer.register() accepts an interface class and an implementation class and stores the mapping internally. 1
REQ-024-002 no register() accepts a scope parameter of type ComponentScope (SINGLETON, TRANSIENT, REQUEST) and defaults to SINGLETON when omitted. 2
REQ-024-003 no register() accepts an optional config dict that is stored with the registration and passed to IComponent.initialize() at resolution time. 1
REQ-024-004 no register() accepts an optional factory callable; when provided, the factory is used instead of instantiating the implementation class. 1
REQ-024-005 no When factory is None, register() validates that implementation is a subclass of interface and raises TypeError if it is not. 1
REQ-024-006 no register() raises ValueError when neither implementation nor factory is provided. 1
REQ-024-007 no The config dict is shallow-copied at registration time so subsequent mutation of the caller's dict does not affect the stored registration. 1
REQ-024-008 no register() accepts an optional dependencies list of types that explicitly override constructor parameter resolution order. 1
REQ-024-009 no register() returns self to allow method chaining. 1
REQ-024-010 no Registering the same interface a second time on the same container raises ValueError unless the container was constructed with allow_override=True. 1
REQ-024-011 no DIContainer.resolve() is an async method that returns an instance satisfying the requested interface. 1
REQ-024-012 no resolve() raises ValueError when the requested interface has no registration in the container. 1
REQ-024-013 no SINGLETON-scoped registrations return the same instance object on every call to resolve() for the same interface. 1
REQ-024-014 no TRANSIENT-scoped registrations return a new instance object on every call to resolve(). 1
REQ-024-015 no REQUEST-scoped registrations return the same instance within a single resolution tree and a new instance for each top-level resolve() call. 1
REQ-024-016 no After instantiating a class, resolve() calls await instance.initialize(config) if the instance is an IComponent. 1
REQ-024-017 no initialize() is called exactly once per singleton instance (not on every resolve). 1
REQ-024-018 no resolve() inspects the constructor (init) signature of the implementation class and resolves each type-annotated parameter recursively. 1
REQ-024-019 no Parameters without type annotations or with built-in types (str, int, etc.) are skipped during constructor injection. 1
REQ-024-020 no If the factory callable is registered, resolve() calls await factory(container) and skips constructor inspection. 1
REQ-024-021 no resolve() passes the stored config dict to the factory as a keyword argument when the factory signature accepts config. 1
REQ-024-022 no Resolved instances from register_instance() are returned directly without calling initialize() again. none
REQ-024-023 no resolve() supports generic type parameters (e.g., resolve(IStore[int])) by falling back to the base generic alias if the parameterized form is unregistered. 1
REQ-024-024 no resolve() propagates exceptions raised inside factory callables or initialize() without wrapping them. 1
REQ-024-025 no The ResolutionContext cache is per-call (not shared across concurrent resolve calls) to prevent cross-request contamination. 1
REQ-024-026 no When resolving interface A causes a recursive resolve of A, a CircularDependencyError is raised before instantiation. 1
REQ-024-027 no The CircularDependencyError message contains the full dependency chain as a readable arrow-delimited string (e.g., IPostman -> IRouter -> IPostman). 1
REQ-024-028 no Circular detection works for transitive cycles of arbitrary depth (A→B→C→A). 1
REQ-024-029 no Diamond dependencies (A→B, A→C, B→D, C→D) do not trigger a false CircularDependencyError. 1
REQ-024-030 no The resolving stack is stored on ResolutionContext and is unwound correctly if an exception occurs mid-resolution. 2
REQ-024-031 no ContainerBuilder.with_component() accepts interface, implementation, scope, and config and returns self for chaining. 1
REQ-024-032 no ContainerBuilder.build() constructs and returns a fully configured DIContainer with all registered components. 1
REQ-024-034 no build() validates that all registrations are internally consistent (no duplicate interfaces, valid implementations) and raises ValueError on the first violation. 1
REQ-024-035 no CORRECTED 2026-07-06 (was a doc/code mismatch, D4 audit) — the DIContainer returned by build() is the SAME instance the builder has been mutating since construction, not an independent copy; subsequent with_* calls on the builder continue to mutate the container already handed to callers of build(). 1
REQ-024-036 no DIContainer.register_instance() accepts an interface type and a pre-created instance object. 1
REQ-024-037 no register_instance() raises ValueError if the instance does not implement (is not an instance of) the given interface. 1
REQ-024-038 no resolve() returns the pre-created instance directly for interfaces registered via register_instance(). 1
REQ-024-039 no Instances registered via register_instance() coexist with class-based registrations in the same container without conflict. 1
REQ-024-040 no register_instance() returns self to allow method chaining. none
REQ-024-041 no ContainerRegistry.register() stores a DIContainer under a given string name. 1
REQ-024-042 no ContainerRegistry.get() retrieves a previously registered container by name and raises KeyError if the name is unknown. 1
REQ-024-043 no ContainerRegistry.has() returns True if a container is registered under the given name, False otherwise. 1
REQ-024-045 no ContainerRegistry is a global singleton accessible without instantiation. 1
REQ-024-046 no DIContainer.get_registry_info() returns a dict mapping interface names to registration metadata (scope, implementation name, has_factory). 1
REQ-024-047 no DIContainer.has() returns True if the given interface has a registration, False otherwise. 1
REQ-024-048 no DIContainer.get_all_registrations() returns the raw mapping of interface type to ComponentRegistration dataclass. 1
REQ-024-049 no DIContainer.set_context() stores an arbitrary key-value pair accessible throughout resolution. 1
REQ-024-050 no DIContainer.get_context() retrieves a stored ambient value by key, returning the provided default if the key is absent. 1
REQ-024-051 no All write operations to the registration map (_registrations) and singleton cache (_singletons) are protected by a threading.Lock. 1
REQ-024-052 no Resolution (read path) does not hold the lock during async instantiation or initialize() calls to prevent deadlock under concurrent usage. 1
REQ-024-053 no Singleton creation uses a double-checked locking pattern: check without lock, acquire lock, check again, create if still absent. 1
REQ-024-044 - removed from none
REQ-024-033 - ) — deleted because it bypassed register()'s own none

Code that traces this spec

Module Symbols Requirements
orchestrata_ultimate/core/protocols/base 1 1
orchestrata_ultimate/di/container 23 49

Checklists

checklists/constitutional.md

Constitutional Checklist — SPEC-024: Dependency Injection Container

Security (CONST-024-001 to 005)
Reliability (CONST-024-006 to 010)
Performance (CONST-024-011 to 013)
Interoperability (CONST-024-014 to 016)

checklists/requirements.md

The requirement list above comes from this file; 0 of 51 checkbox items are ticked.

checklists/testing.md

Testing Checklist — SPEC-024: Dependency Injection Container

Each item maps 1:1 to a requirement in requirements.md.

Core Registration Tests
Resolution Tests
Circular Dependency Detection Tests
Container Builder Tests
Instance Registration Tests
Container Registry Tests
Introspection Tests
Thread Safety Tests

Other tracked files

Full spec text

Verbatim from specs/024-dependency-injection/spec.md; headings are pushed down two levels.

SPEC-024: Dependency Injection Container

Metadata


Overview

What

The DI container provides interface-based component registration, async resolution with constructor injection, scope management (singleton/transient/request), and circular dependency detection. Its scope is the plugin SDK's capability-gated resolve() touchpoint (plugin/context.py) and the WebSocket gateway's assembly root (gateway/server.py) — see the "Descoped (2026-07-06)" section below for why this is narrower than the original "universal composition backbone" framing.

Why
How

User Scenarios and Stories

US-001: Component Registration (P0)

As a package author I want to register my component's interface and implementation So that other packages can resolve my component without knowing the concrete class

Acceptance Criteria:


US-002: Component Resolution (P0)

As a consuming package I want to resolve an interface to get an instance So that I can use the component without knowing its concrete type

Acceptance Criteria:


US-003: Circular Dependency Detection (P0)

As a developer I want to get a clear error when circular dependencies exist So that I can fix the dependency graph

Acceptance Criteria:


US-004: Container Builder (P1)

As a system bootstrap I want to assemble a container from configuration So that the system can be configured declaratively

Acceptance Criteria:


US-005: Instance Registration (P1)

As a test or bootstrap code I want to register pre-created instances So that I can inject specific objects (mocks, singletons)

Acceptance Criteria:


US-006: Container Registry (P1)

As the kernel I want to maintain named containers So that different subsystems can have isolated DI scopes

Acceptance Criteria:


US-007: Introspection (P2)

As a diagnostic tool I want to inspect container registrations So that I can debug wiring issues

Acceptance Criteria:


Terminology

Term Definition
Interface Abstract base class (IComponent subclass) defining a contract
Implementation Concrete class implementing an interface
Scope Lifecycle policy: SINGLETON (one instance), TRANSIENT (new each time), REQUEST (per-request)
Resolution Process of creating an instance by walking the dependency graph
Constructor Injection Resolving dependencies by inspecting init parameter types
Factory Callable that creates an instance, alternative to class-based registration
Circular Dependency A→B→A cycle detected during resolution
Context Key-value store for ambient values (e.g., config, session)

Data Models

ComponentRegistration
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "ComponentRegistration",
  "required": ["interface", "scope"],
  "properties": {
    "interface": {
      "type": "string",
      "description": "Fully qualified interface class name"
    },
    "implementation": {
      "type": "string",
      "description": "Fully qualified implementation class name"
    },
    "scope": {
      "type": "string",
      "enum": ["SINGLETON", "TRANSIENT", "REQUEST"],
      "description": "Lifecycle scope of the component"
    },
    "config": {
      "type": "object",
      "description": "Configuration dictionary passed to initialize()"
    },
    "factory": {
      "type": "object",
      "description": "Callable factory function for instance creation"
    },
    "dependencies": {
      "type": "array",
      "items": {"type": "string"},
      "description": "List of interface names this component depends on"
    },
    "is_instance": {
      "type": "boolean",
      "description": "Whether this is a pre-registered instance"
    }
  }
}
ResolutionContext
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "ResolutionContext",
  "properties": {
    "container": {
      "type": "object",
      "description": "The DIContainer performing resolution"
    },
    "resolution_stack": {
      "type": "array",
      "items": {"type": "string"},
      "description": "Stack of interfaces being resolved (for cycle detection)"
    },
    "singletons": {
      "type": "object",
      "description": "Map of singleton instances keyed by interface name"
    },
    "context": {
      "type": "object",
      "description": "Ambient key-value store for resolution"
    },
    "request_id": {
      "type": "string",
      "description": "Unique identifier for this resolution request"
    }
  }
}
ComponentScope
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "ComponentScope",
  "enum": ["SINGLETON", "TRANSIENT", "REQUEST"],
  "description": "Defines the lifecycle of a component within the container"
}

Functional Requirements

FR-001: Interface Registration

The container MUST allow registration of interface-to-implementation mappings with configurable scope and optional configuration dictionary.

FR-002: Scope Enforcement

The container MUST enforce scope policies: SINGLETON returns same instance, TRANSIENT creates new instance, REQUEST creates instance per request context.

FR-003: Constructor Injection

The container MUST automatically resolve constructor dependencies by inspecting init parameter type annotations.

FR-004: Lifecycle Integration

The container MUST call IComponent.initialize(config) on first resolution of each component.

FR-005: Circular Dependency Detection

The container MUST detect circular dependencies during resolution and raise an error with the complete cycle path.

FR-006: Factory Support

The container MUST support factory callables as an alternative to class-based registration.

FR-007: Instance Registration

The container MUST support registering pre-created instances for testing and singleton scenarios.

FR-008: Container Hierarchy — REMOVED 2026-07-06

The container MUST support parent-child hierarchy for scoped resolution across container boundaries. Descoped under decision D4 — see the "Descoped (2026-07-06)" section below for the full rationale. The FR number is kept as a tombstone (not renumbered) so historical references stay stable.

FR-009: Context Propagation

The container MUST maintain ambient context values accessible during resolution.

FR-010: Thread Safety

The container MUST be thread-safe for concurrent registration and resolution operations.

FR-011: Fluent Builder API

The container builder MUST provide a fluent API for declarative container assembly.

FR-012: Named Container Registry

The system MUST maintain a registry of named containers for multi-tenant or multi-environment scenarios.

FR-013: Introspection Capabilities

The container MUST provide introspection methods to query registered components and their metadata.


Error Conditions

ID Condition Handling
ERR-001 Resolve unregistered interface Raise ValueError with message "No registration found for {interface.name}"
ERR-002 Circular dependency detected Raise CircularDependencyError with path showing full cycle
ERR-003 Implementation not subclass of interface Raise TypeError with message "{impl.name} does not implement {interface.name}"
ERR-004 Instance type does not implement interface Raise ValueError with message "Instance of {type(instance).name} does not implement {interface.name}"
ERR-005 Registration without implementation or factory Raise ValueError with message "Either implementation or factory must be provided"
ERR-006 Duplicate interface registration without allow_override Raise ValueError with message "Interface {interface.name} already registered"
ERR-007 Invalid scope value Raise ValueError with message "Invalid scope: {scope}. Must be SINGLETON, TRANSIENT, or REQUEST"
ERR-008 Factory returns wrong type Raise TypeError with message "Factory for {interface.name} returned {type(result).name}, expected {interface.name}"
ERR-009 Parent container not found for hierarchical resolve REMOVED 2026-07-06 — hierarchical resolve (FR-008) was descoped under D4; never implemented (no code path could raise this). See "Descoped" section.
ERR-010 Context key collision during resolution Raise ValueError with message "Context key '{key}' already exists"
ERR-011 Request scope without request context Raise RuntimeError with message "REQUEST scoped component requires active request context"
ERR-012 Async factory in synchronous resolve Raise TypeError with message "Async factory cannot be called in synchronous context"
ERR-013 Circular dependency in registration Raise ValueError with message "Circular dependency in registration: {interfaces}"

Descoped (2026-07-06)

Ruled under decision D4 (plans/decisions-2026-07-06.md; evidence: plans/evidence/d4-di-container.md). An adoption audit of every real composition root in the codebase found that 8 of the 9 (kernel/cli_main.py, kernel/cli.py, postman/cli.py, agents/runtime_main.py, knowledge_graph/server.py, the root cli.py, session/cli/main.py, harness/cli.py) never touch DIContainer at all — they hand-construct their components directly. The 9th (gateway/server.py) constructs exactly one DIContainer, registers exactly one instance through it, and wires every other dependency via 9 direct setter calls into gateway/rpc.py. This container was never the "universal composition backbone" the original spec described; that framing was aspirational, not descriptive.

The clearest evidence: this spec's flagship differentiator, container hierarchy (FR-008), was an unimplemented stub — ContainerRegistry.get_with_parent()'s entire body was container = cls.get (name) plus a comment ("Parent-child delegation would be implemented here") and a return container. No delegation logic existed. Its only callers were three unit tests exercising the stub itself; there were zero real (non-test) consumers. docs/rtm.json nonetheless marked its requirement "VERIFIED" — that RTM semantic means "a @traces decorator is present," not "the behavior was functionally tested," and this is exactly the false-confidence gap that prompted the D4 audit.

Rather than build out a never-used hierarchy feature to satisfy the letter of the original FR-008, this rewrite takes the honest path: descope it, and reframe the container's documented role to match what it actually, load-bearingly does — the plugin SDK's capability-gated resolve() touchpoint (plugin/context.py) and the WebSocket gateway's single composition root (gateway/server.py). Every other composition root continues to wire dependencies explicitly (constructor arguments, setter calls) per architecture-standards.md §composition — this was already true for 8 of 9 roots before this rewrite; it is now the documented, enforced contract rather than an unstated implementation detail. scripts/architecture_checker.py gained a standing rule (D4) that flags any new DIContainer(...) construction outside plugin/, gateway/, or di/ (test files are exempt — tests constructing components directly by design is the norm in this suite, not a violation).

Removed:

Not affected: FR-012 (Named Container Registry) — ContainerRegistry.register() / .get() / .has() / .shutdown_all() are unrelated to hierarchy and remain fully implemented; only the parent- delegation stub (get_with_parent()) was removed. US-006's "maintain named container map" and "register/retrieve by name" acceptance criteria are unaffected — only its "container hierarchy" bullet is descoped.

Also fixed during this pass (not a scope change, defect corrections):


API Contracts

DIContainer
Method Signature Description
register (interface, implementation, scope, config, dependencies, factory) -> self Register component
register_instance (interface, instance) -> self Register pre-created instance
resolve async (interface) -> T Resolve interface to instance
has (interface) -> bool Check if interface is registered
get_registry_info () -> dict Return all registration metadata
get_all_registrations () -> dict[type, ComponentRegistration] Return raw registration map
set_context (key, value) -> None Set ambient context value
get_context (key, default) -> Any Get ambient context value
ContainerBuilder
Method Signature Description
with_component (interface, implementation, scope, config) -> self Add registration
build () -> DIContainer Build and return container
ContainerRegistry
Method Signature Description
register (name, container) -> None Register named container
get (name) -> DIContainer Retrieve named container
has (name) -> bool Check if named container exists

Non-Functional Requirements

Performance
Reliability
Compatibility

Technical Design

Registration Flow
  1. Client calls register(interface, implementation, scope, config, factory)
  2. Validate implementation is subclass of interface (or factory provided)
  3. Validate scope is valid enum value
  4. Create ComponentRegistration with provided metadata
  5. Store in internal registry map under interface key
  6. If allow_override=False and interface exists, raise ERR-006
Resolution Flow
  1. Client calls await resolve(interface)
  2. Check if interface registered, raise ERR-001 if not
  3. Check resolution_stack for cycle, raise ERR-002 if detected
  4. Add interface to resolution_stack
  5. If singleton and instance exists in singletons cache, return it
  6. Get ComponentRegistration for interface
  7. If factory provided, call factory; else instantiate implementation
  8. For each init parameter, recursively resolve dependency
  9. Call component.initialize(config) if IComponent
  10. If singleton, store instance in singletons cache
  11. Remove interface from resolution_stack
  12. Return instance
Thread Safety Design

Examples

Basic Registration and Resolution
from orchestrata.di import DIContainer, ComponentScope

container = DIContainer()

class IService(Interface):
    pass

class ServiceImpl(IService):
    async def initialize(self, config):
        print("Initialized")

container.register(IService, ServiceImpl, ComponentScope.SINGLETON, {"timeout": 30})

service = await container.resolve(IService)
Using ContainerBuilder
from orchestrata.di import ContainerBuilder, ComponentScope

config = {
    "db": {"interface": "IDatabase", "impl": "PostgresDatabase", "scope": "SINGLETON"},
    "cache": {"interface": "ICache", "impl": "RedisCache", "scope": "TRANSIENT"}
}

builder = ContainerBuilder()
for name, cfg in config.items():
    builder.with_component(
        interface=cfg["interface"],
        implementation=cfg["impl"],
        scope=ComponentScope(cfg["scope"])
    )

container = builder.build()
Testing with Instance Registration
class MockService(IService):
    pass

mock = MockService()
container.register_instance(IService, mock)

resolved = await container.resolve(IService)
assert resolved is mock  # Returns the same mock instance

Security

Input Validation
Audit Logging
Access Control

Governance

Deprecation Policy
Breaking Changes

Visual Architecture

Class Diagram

Diagram source shown as text: Mermaid could not parse it (Error: Parse error on line 239:).

classDiagram
    class MultiAgentEditResult {
        +file_path: str
        +old_string: str
        +new_string: str
        +agent_results: dict[Any]
        +aggregated_result: dict[Any]
        +timestamp: datetime = Field()
        +success: bool = True
    }
    class GovernanceError {
    }
    class OrchestrataKernel {
        +__init__(self): None
        +_initialize_agents(self): None
        +get_agents_by_type(self, agent_type): list[Any]
        +get_agent_by_id(self, agent_id): Any
        +async broadcast_to_type(self, agent_type, message): list[Any]
        +async broadcast_to_all(self, message): list[Any]
        +async send_to_agent(self, agent_id, message, sender): Any
    }
    class UltimateOrchestrator {
        +__init__(self): None
        +async execute_multi_agent_edit(self, file_path, old_string, new_string): MultiAgentEditResult
        +_aggregate_agent_results(self, results): dict[Any]
        +async create_project(self, name, path, template): dict[Any]
        +async shutdown(self): None
    }
    class ThreadPoolTaskStatus {
        +PENDING = 'pending'
        +RUNNING = 'running'
        +COMPLETED = 'completed'
        +FAILED = 'failed'
        +CANCELLED = 'cancelled'
        +TIMEOUT = 'timeout'
    }
    class ThreadPoolTask {
        +task_id: str = field()
        +function: Any = None
        +args: tuple = field()
        +kwargs: dict[Any] = field()
        +status: ThreadPoolTaskStatus = .PENDING
        +created_at: datetime = field()
        +started_at: Any = None
        +completed_at: Any = None
        +result: Any = None
        +error: Any = None
        +traceback: Any = None
        +future: Any = None
        +metadata: dict[Any] = field()
    }
    class ThreadPoolManager {
        +__init__(self, max_workers, thread_name_prefix, initializer, initargs)
        +start(self): None
        +stop(self, wait, cancel_futures): None
        +submit(self, fn, *args, **kwargs): str
        +submit_many(self, functions, args_list, kwargs_list): list[str]
        +async submit_async(self, fn, *args, **kwargs): T
        +async submit_many_async(self, functions, args_list, kwargs_list): list[T]
        +async wait_for_task(self, task_id, timeout): Any
        +get_task_status(self, task_id): ThreadPoolTaskStatus
        +get_task_result(self, task_id, wait): Any
        +cancel_task(self, task_id): bool
        +get_stats(self): dict[Any]
        +_wrap_function(self, task_id, fn, *args, **kwargs): T
        +_update_task_from_future(self, task_id, future): None
        +task_context(self, task_id): Any
        +async async_task_context(self, task_id): Any
    }
    class ParallelTestExecutor {
        +__init__(self, thread_pool, max_workers)
        +run_tests_parallel(self, test_paths, pytest_args, timeout_per_test): dict[Any]
        +_run_single_test(self, test_path, pytest_args, timeout): dict[Any]
        +async run_tests_parallel_async(self, test_paths, pytest_args, timeout_per_test): dict[Any]
    }
    class LogLevel {
        +TRACE = 'TRACE'
        +DEBUG = 'DEBUG'
        +INFO = 'INFO'
        +WARNING = 'WARNING'
        +ERROR = 'ERROR'
        +CRITICAL = 'CRITICAL'
    }
    class LogContext {
        +__init__(self, logger, **context)
        +__enter__(self)
        +__exit__(self, exc_type, exc_val, exc_tb)
    }
    class LoggingContext {
        +request_id: Any = None
        +session_id: Any = None
        +trace_id: Any = None
        +span_id: Any = None
        +user_id: Any = None
        +user_email: Any = None
        +user_role: Any = None
        +component: Any = None
        +operation: Any = None
        +extra: dict[Any] = field()
        +created_at: datetime = field()
        ---
        +to_dict(self): dict[Any]
    }
    class ContextManager {
        +get_current_context(): LoggingContext
        +set_current_context(context): None
        +clear_context(): None
        +update_context(**kwargs): LoggingContext
        +create_child_context(**updates): LoggingContext
    }
    class AsyncContext {
        +__init__(self, **context_updates)
        +async __aenter__(self): LoggingContext
        +async __aexit__(self, exc_type, exc_val, exc_tb)
    }
    class Context {
        +__init__(self, **context_updates)
        +__enter__(self): LoggingContext
        +__exit__(self, exc_type, exc_val, exc_tb)
    }
    class AuditAction {
        +LOGIN = 'login'
        +LOGOUT = 'logout'
        +AUTH_FAILURE = 'auth_failure'
        +PERMISSION_CHECK = 'permission_check'
        +PERMISSION_DENIED = 'permission_denied'
        +READ = 'read'
        +CREATE = 'create'
        +UPDATE = 'update'
        +DELETE = 'delete'
        +EXPORT = 'export'
        +IMPORT = 'import'
        +CONFIG_CHANGE = 'config_change'
        +SERVICE_START = 'service_start'
        +SERVICE_STOP = 'service_stop'
        +BACKUP = 'backup'
        +RESTORE = 'restore'
        +USER_CREATE = 'user_create'
        +USER_UPDATE = 'user_update'
        +USER_DELETE = 'user_delete'
        +ROLE_ASSIGN = 'role_assign'
        +ROLE_REVOKE = 'role_revoke'
        +EXTERNAL_CALL = 'external_call'
        +WEBHOOK_RECEIVED = 'webhook_received'
        +API_CALL = 'api_call'
        +WORKFLOW_START = 'workflow_start'
        +WORKFLOW_COMPLETE = 'workflow_complete'
        +ESCALATION = 'escalation'
        +APPROVAL = 'approval'
        +REJECTION = 'rejection'
    }
    class AuditStatus {
        +SUCCESS = 'success'
        +FAILURE = 'failure'
        +PARTIAL = 'partial'
        +PENDING = 'pending'
    }
    class AuditEvent {
        +action: AuditAction
        +timestamp: datetime = field()
        +status: AuditStatus = .SUCCESS
        +actor_id: Any = None
        +actor_type: str = 'user'
        +actor_ip: Any = None
        +actor_user_agent: Any = None
        +resource_type: Any = None
        +resource_id: Any = None
        +resource_path: Any = None
        +operation_id: Any = None
        +operation_name: Any = None
        +operation_duration_ms: Any = None
        +before_state: Any = None
        +after_state: Any = None
        +changes: Any = None
        +request_id: Any = None
        +session_id: Any = None
        +trace_id: Any = None
        +span_id: Any = None
        +component: Any = None
        +severity: str = 'info'
        +details: Any = None
        +error_message: Any = None
        +error_stack: Any = None
        +event_hash: Any = None
        +previous_hash: Any = None
        ---
        +__post_init__(self)
        +calculate_hash(self): str
        +_json_serializer(self, obj)
        +to_dict(self): dict[Any]
        +verify_hash(self): bool
    }
    class AuditLogger {
        +__init__(self, component)
        +log_event(self, event): str
        +log_action(self, action, status, resource_type, resource_id, before_state, after_state, actor_id, actor_type, details, error_message, operation_duration_ms, severity): str
    }
    class AuditOperation {
        +action: Any
        +resource_type: Any = None
        +resource_id: Any = None
        +before_state: Any = None
        +actor_id: Any = None
        +actor_type: str = 'user'
        +component: Any = None
        +details: Any = None
        ---
        +__enter__(self)
        +__exit__(self, exc_type, exc_val, exc_tb)
    }
    class DomainEntity {
        +entity_id: str = Field()
        +is_active: bool = Field()
        ---
        +__init__(self, **data)
        +mark_inactive(self)
        +to_domain_dict(self): dict[Any]
        +get_business_key(self): str
        +__eq__(self, other): bool
        +__hash__(self): int
        +__repr__(self): str
    }
    class Node {
        +node_id: str = Field()
        +node_type: str = Field()
        +name: str = Field()
        +properties: dict[Any] = Field()
        +created_at: datetime = Field()
        +updated_at: datetime = Field()
        +version: int = Field()
        ---
        +to_graph_dict(self): dict[Any]
        +from_graph_dict(cls, data): Node
        +update_properties(self, **kwargs)
        +__str__(self): str
    }
    class Config {
        +arbitrary_types_allowed = True
        +json_encoders = {}
    }
    class TracingLevel {
        +OFF = 0
        +BASIC = 1
        +DETAILED = 2
        +DEBUG = 3
        ---
        +label(self): str
    }
    class TracingConfig {
        +model_config = ConfigDict()
        +enabled: bool = Field()
        +service_name: str = Field()
        +service_version: str = Field()
        +endpoint: Any = Field()
        +sample_rate: float = Field()
        +console_export: bool = Field()
        +tracing_level: TracingLevel = Field()
        ---
        +clamp_sample_rate(cls, v): float
    }
    class MetricsConfig {
        +__init__(self, enabled, port, host, include_system_metrics, system_metrics_interval)
    }
    class RetryStrategy {
        +__init__(self, max_retries, initial_delay, max_delay, backoff_factor, jitter)
        +calculate_delay(self, attempt): float
    }
    class CircuitState {
        +CLOSED = 'closed'
        +OPEN = 'open'
        +HALF_OPEN = 'half_open'
    }
    class CircuitBreaker {
        +__init__(self, failure_threshold, reset_timeout, half_open_max_requests, name)
        +is_closed(self): bool
        +is_open(self): bool
        +is_half_open(self): bool
        +record_success(self): None
        +record_failure(self): None
        +should_allow_request(self): bool
        +async execute(self, operation): T
    }
    class Bulkhead {
        +__init__(self, max_concurrent, max_queue_size, name)
        +async execute(self, operation): T
    }
    Node <|-- DomainEntity

Dependencies

Dependency Version Purpose
orchestrata-core >=1.0.0 IComponent, ComponentScope interfaces
threading stdlib Thread safety for container operations
contextvars stdlib Request scope context management

Testing

Unit Tests
Integration Tests
Coverage Targets

Integration Points

With orchestrata-core
With Package System
With Request Pipeline

Success Criteria

Must Have
Should Have
Metrics

Observability

Logging Requirements
  1. Component Logging
    • Log all major operations with appropriate log levels
    • Include correlation IDs for request tracing
    • Log performance metrics for critical operations
    • Log errors with full context for debugging
Metrics Requirements
  1. Performance Metrics
    • Operation duration histograms
    • Request/error rate counters
    • Resource utilization gauges
    • Business metrics relevant to domain
Tracing Requirements
  1. Distributed Tracing
    • Trace propagation across component boundaries
    • Span attributes for domain-specific context
    • Trace sampling based on operational needs
Alerting Requirements
  1. Critical Alerts
    • Error rate spikes
    • Performance degradation
    • Resource exhaustion
    • Business metric anomalies
Dashboard Requirements
  1. Operational Dashboard
    • Real-time system health
    • Performance trends
    • Error analysis
    • Resource monitoring

Revision History

Version Date Author Changes
1.0.0 2026-04-04 Mohamed Initial specification
1.0.1 2026-04-09 Mohamed Added missing sections per SPEC_STANDARD, expanded error conditions to 13, added functional requirements FR-001 to FR-013, added JSON schemas for data models
1.1.0 2026-07-06 Mohamed D4: descoped FR-008 (container hierarchy, get_with_parent()) and ERR-009 — unimplemented stub, zero real consumers, falsely RTM-VERIFIED; descoped with_components() batch registration (REQ-024-033) — bypassed register() validation via private _registry writes. Reframed the container's documented role as plugin SDK + gateway assembly (8 of 9 composition roots already wired explicitly). Fixed build()'s "independent container" doc/code mismatch and a shutdown() defect (SINGLETON components resolved via resolve() were never stopped). See "Descoped (2026-07-06)" section.