Skip to content

ARCHIVED: This technical design was archived on 2025-12-22 when the reflection module was removed from the codebase. The DI module now uses Python standard library introspection (inspect, typing.get_type_hints()) instead of the custom reflection system.

๐Ÿ“˜ Technical Design 032 โ€” ReflectionContext Integration & MemberBuilder Migration

Field Value
ID TD-032
Title ReflectionContext Integration โ€” Wire Engine + Strategies, Remove MemberBuilder
Status Draft
Owner Reflection maintainers
Audience Reflection/DI contributors, existing consumers of ReflectionContext
Created 2025-11-15
Last Updated 2025-11-15 (Initial draft)
Reviewers TBD
Approvers TBD
Tracking Phase C.3 of 002-reflection-modernization-roadmap.md
Related TD-033 โ€” ReflectionEngine & HandleRegistry, TD-031 โ€” Handle Generation Strategy Protocol, TD-027 โ€” ReflectionContext API

๐Ÿงญ 1. Summary

TD-033 and TD-031 built the foundation (Engine + Registry + Strategies). TD-032 wires everything into ReflectionContext and removes MemberBuilder:

  • ReflectionContext integration: Update constructor to use ReflectionEngine instead of MemberBuilder
  • Component migration: Update all reflection components (ImportResolver, Scanner, TypeResolver) to use engine
  • API refinement: Split clear_caches() into clear_computation_cache() + clear_handle_registry()
  • MemberBuilder removal: Delete legacy builder after migration complete
  • Full regression validation: All 791 existing tests pass with zero public API changes

At a glance: - Phase C.1 (TD-033): Built engine + registry โœ… - Phase C.2 (TD-031): Built strategies + specs โœ… - Phase C.3 (This TD): Wire engine into ReflectionContext, remove MemberBuilder - Zero breaking changes to public API - All existing tests pass without modification - Performance within 10% of baseline

๐Ÿ“š 2. Context and Problem Statement

2.1 Current State (After TD-033 + TD-031)

graph TB
    subgraph "After TD-031"
        Engine["ReflectionEngine<br/>(strategy-based)"]
        Registry["HandleRegistry"]
        Strategies["7 Core Mappers"]
    end

    subgraph "Existing (Unchanged)"
        Context["ReflectionContext<br/>(uses MemberBuilder)"]
        ImportResolver["PythonImportResolver<br/>(uses MemberBuilder)"]
        Scanner["PackageScanner<br/>(uses MemberBuilder)"]
        TypeResolver["RuntimeTypeResolver<br/>(uses MemberBuilder)"]
    end

    subgraph "Legacy"
        MemberBuilder["MemberBuilder<br/>(~450 lines)"]
    end

    Context --> MemberBuilder
    ImportResolver --> MemberBuilder
    Scanner --> MemberBuilder
    TypeResolver --> MemberBuilder

    Engine -.->|Not yet wired| Context
    Registry -.->|Not yet used| Context

    classDef new fill:none,stroke:#4CAF50,stroke-width:2px,color:#4CAF50;
    classDef existing fill:none,stroke:#FF9800,stroke-width:2px,color:#FF9800;
    classDef legacy fill:none,stroke:#F44336,stroke-width:2px,color:#F44336,stroke-dasharray: 5 5;

    class Engine,Registry,Strategies new;
    class Context,ImportResolver,Scanner,TypeResolver existing;
    class MemberBuilder legacy;

Problem: Engine + strategies exist but aren't used. ReflectionContext still uses MemberBuilder.

2.2 Target State (After TD-032)

graph TB
    subgraph "Integrated"
        Context["ReflectionContext<br/>(uses Engine)"]
        ImportResolver["PythonImportResolver<br/>(uses Engine)"]
        Scanner["PackageScanner<br/>(uses Engine)"]
        TypeResolver["RuntimeTypeResolver<br/>(uses Engine)"]
    end

    subgraph "Foundation"
        Engine["ReflectionEngine<br/>(strategy-based)"]
        Registry["HandleRegistry"]
        Strategies["7 Core Mappers"]
    end

    Context --> Engine
    ImportResolver --> Engine
    Scanner --> Engine
    TypeResolver --> Engine

    Engine --> Registry
    Engine --> Strategies

    MemberBuilder["โŒ MemberBuilder<br/>(REMOVED)"]

    classDef integrated fill:none,stroke:#4CAF50,stroke-width:2px,color:#4CAF50;
    classDef foundation fill:none,stroke:#1976D2,stroke-width:2px,color:#1976D2;
    classDef removed fill:none,stroke:#F44336,stroke-width:2px,color:#F44336,stroke-dasharray: 5 5;

    class Context,ImportResolver,Scanner,TypeResolver integrated;
    class Engine,Registry,Strategies foundation;
    class MemberBuilder removed;

2.3 Goals

  1. Wire ReflectionEngine into ReflectionContext: Replace MemberBuilder with engine in constructor
  2. Update reflection components: ImportResolver, Scanner, TypeResolver delegate to engine
  3. Refine cache API: Split clear_caches() into two methods (computation vs identity)
  4. Remove MemberBuilder: Delete class after all migrations complete
  5. Zero public API changes: Existing code continues to work without modification
  6. Full regression validation: All 791 tests pass

2.4 Out of Scope

  • Lazy loading (Phase C.4)
  • Public engine exposure (future decision; engine remains internal for now)
  • New ReflectionContext APIs (only refining clear_caches())

2.5 Assumptions

  • TD-033 complete: Engine + registry functional
  • TD-031 complete: Strategies produce outputs matching MemberBuilder
  • Test suite unchanged: All 791 tests must pass without modification (except clear_caches() calls)

โš™๏ธ 3. Detailed Design

3.1 ReflectionContext Integration

Before (Current)

class ReflectionContext:
    def __init__(
        self,
        *,
        cache: HierarchicalCache | None = None,
        metrics: ReflectionMetrics | None = None,
        type_hint_config: TypeHintConfig | None = None,
    ) -> None:
        self._cache = cache or ConcurrentHierarchicalCache()
        self._metrics = metrics or ReflectionMetricsCollector()

        # Legacy: MemberBuilder
        self._member_builder = MemberBuilder(cache=self._cache)
        self._type_resolver = RuntimeTypeResolver(builder=self._member_builder)
        self._import_resolver = PythonImportResolver(builder=self._member_builder, metrics=metrics)
        self._scanner = PackageScanner(imports=self._import_resolver, builder=self._member_builder, metrics=metrics)

After (TD-032)

from loryth.common.reflection.engine import ReflectionEngine

class ReflectionContext:
    def __init__(
        self,
        *,
        cache: HierarchicalCache | None = None,
        metrics: ReflectionMetrics | None = None,
        type_hint_config: TypeHintConfig | None = None,
    ) -> None:
        self._cache = cache or ConcurrentHierarchicalCache()
        self._metrics = metrics or ReflectionMetricsCollector()

        # NEW: ReflectionEngine (strategy-based)
        self._engine = ReflectionEngine(cache=self._cache)
        self._type_resolver = RuntimeTypeResolver(engine=self._engine)
        self._import_resolver = PythonImportResolver(engine=self._engine, metrics=metrics)
        self._scanner = PackageScanner(imports=self._import_resolver, engine=self._engine, metrics=metrics)

        # ... rest unchanged ...

3.2 Component Migration

3.2.1 RuntimeTypeResolver

# Before
class RuntimeTypeResolver(TypeResolver):
    def __init__(self, builder: MemberBuilder) -> None:
        self._builder = builder

    def of_type(self, t: type[Any]) -> TypeHandle:
        return self._builder.build_type(t)

# After
class RuntimeTypeResolver(TypeResolver):
    def __init__(self, engine: ReflectionEngine) -> None:
        self._engine = engine

    def of_type(self, t: type[Any]) -> TypeHandle:
        return self._engine.resolve_type(t)

    def of_module(self, m: ModuleType) -> ModuleHandle:
        return self._engine.resolve_module(m)

3.2.2 PythonImportResolver

# Before
class PythonImportResolver(ImportResolver):
    def __init__(self, builder: MemberBuilder, metrics: ReflectionMetrics) -> None:
        self._builder = builder
        self._metrics = metrics

    def resolve_type(self, dotted_path: str) -> TypeHandle:
        module_name, _, type_name = dotted_path.rpartition(".")
        module = importlib.import_module(module_name)
        type_ = getattr(module, type_name)
        return self._builder.build_type(type_)

# After
class PythonImportResolver(ImportResolver):
    def __init__(self, engine: ReflectionEngine, metrics: ReflectionMetrics) -> None:
        self._engine = engine
        self._metrics = metrics

    def resolve_type(self, dotted_path: str) -> TypeHandle:
        module_name, _, type_name = dotted_path.rpartition(".")
        module = importlib.import_module(module_name)
        type_ = getattr(module, type_name)
        return self._engine.resolve_type(type_)

3.2.3 PackageScanner

# Before
class PackageScanner(Scanner):
    def __init__(self, imports: ImportResolver, builder: MemberBuilder, metrics: ReflectionMetrics) -> None:
        self._imports = imports
        self._builder = builder
        self._metrics = metrics

# After
class PackageScanner(Scanner):
    def __init__(self, imports: ImportResolver, engine: ReflectionEngine, metrics: ReflectionMetrics) -> None:
        self._imports = imports
        self._engine = engine
        self._metrics = metrics

    # Uses engine.find_handles_by_prefix() for graph queries

3.3 Cache API Refinement

Before (Confusing)

def clear_caches(self, namespace: tuple[str, ...] | None = None) -> None:
    """Clear caches (destroys identity AND performance cache)."""
    if namespace is None:
        self._cache.clear()
    else:
        self._cache.clear(namespace)

After (Clear Semantics)

def clear_computation_cache(self, namespace: tuple[str, ...] | None = None) -> None:
    """Clear performance cache (type hints, annotations).

    Preserves handle identity (registry unchanged).
    Use for invalidating cached computations without destroying handles.

    :param namespace: Optional namespace to limit clearing scope.
    :type namespace: tuple[str, ...] | None
    :rtype: None

    Example::

        >>> # Clear cached type hints only
        >>> context.clear_computation_cache(("type_hints",))
    """
    self._engine.clear_cache(namespace)

def clear_handle_registry(self, prefix: str | None = None) -> None:
    """Clear handle registry (destroys identity).

    Use for test isolation when you need to reset reflection state.
    All handles will be rebuilt on next resolution.

    :param prefix: Optional prefix to limit clearing scope.
    :type prefix: str | None
    :rtype: None

    Example::

        >>> # Test isolation: clear everything
        >>> context.clear_handle_registry()

        >>> # Clear specific module
        >>> context.clear_handle_registry("my_app.models")
    """
    self._engine.clear_registry(prefix)

def clear_caches(self, namespace: tuple[str, ...] | None = None) -> None:
    """Clear both computation cache and handle registry.

    DEPRECATED: Use clear_computation_cache() or clear_handle_registry() for clarity.
    This method clears BOTH for backward compatibility.

    :param namespace: Optional namespace to limit clearing scope.
    :type namespace: tuple[str, ...] | None
    :rtype: None
    """
    # Clear both (backward compatibility)
    self.clear_computation_cache(namespace)
    if namespace is None:
        self.clear_handle_registry()
    else:
        # Convert tuple namespace to prefix string
        prefix = ".".join(str(n) for n in namespace)
        self.clear_handle_registry(prefix)

๐Ÿงช 4. Test Strategy

4.1 Test Matrix

Test Category Coverage Target Key Validation
Integration Tests 100% ReflectionContext โ†’ Engine โ†’ Strategies workflow
Component Migration 100% ImportResolver, Scanner, TypeResolver use engine
Regression Validation 100% All 791 existing tests pass without modification
Cache API 100% clear_computation_cache() vs clear_handle_registry() semantics
Performance Baseline ยฑ10% Engine-based resolution within 10% of MemberBuilder

4.2 Migration Validation Checklist

  • โœ… ReflectionContext constructor updated to create Engine
  • โœ… RuntimeTypeResolver delegates to engine.resolve_type()
  • โœ… PythonImportResolver delegates to engine.resolve_type()
  • โœ… PackageScanner uses engine for module resolution
  • โœ… Cache API split into clear_computation_cache() + clear_handle_registry()
  • โœ… All 791 tests pass without modification
  • โœ… MemberBuilder removed from codebase
  • โœ… Performance validated (ยฑ10% of baseline)

โœ… 5. Acceptance Criteria

Phase C.3a: ReflectionContext Integration

  • โœ… ReflectionContext.__init__() creates ReflectionEngine instead of MemberBuilder
  • โœ… clear_computation_cache() added
  • โœ… clear_handle_registry() added
  • โœ… clear_caches() marked deprecated, clears both for compatibility
  • โœ… Unit tests: cache clearing semantics validated

Phase C.3b: Component Migration

  • โœ… RuntimeTypeResolver updated to use engine
  • โœ… PythonImportResolver updated to use engine
  • โœ… PackageScanner updated to use engine
  • โœ… Unit tests: each component tested with engine

Phase C.3c: Regression Validation

  • โœ… All 791 existing tests pass without modification
  • โœ… Integration tests: full workflows (resolve, scan, invoke, construct)
  • โœ… Golden fixtures: outputs match MemberBuilder baseline
  • โœ… Edge cases: circular imports, forward refs, deep nesting

Phase C.3d: MemberBuilder Removal

  • โœ… MemberBuilder class deleted from member_builder.py
  • โœ… All imports of MemberBuilder removed
  • โœ… Cache-based identity lookups replaced with registry lookups
  • โœ… All 791 tests still pass after removal
  • โœ… Performance benchmarks: ยฑ10% of MemberBuilder baseline

๐Ÿ“‹ 6. Implementation Phases

Phase C.3a: ReflectionContext Integration (โณ Pending)

Tasks: 1. Update ReflectionContext.__init__(): create ReflectionEngine 2. Add clear_computation_cache() method 3. Add clear_handle_registry() method 4. Update clear_caches() to call both (backward compatibility) 5. Write unit tests: cache clearing semantics

Acceptance: ReflectionContext uses engine; cache API refined


Phase C.3b: Component Migration (โณ Pending)

Tasks: 1. Update RuntimeTypeResolver: replace builder with engine 2. Update PythonImportResolver: replace builder with engine 3. Update PackageScanner: replace builder with engine 4. Write unit tests: each component with engine

Acceptance: All components delegate to engine


Phase C.3c: Regression Validation (โณ Pending)

Tasks: 1. Run full test suite (791 tests) 2. Fix any test failures (should be minimal or zero) 3. Write integration tests: full workflows 4. Validate golden fixtures: outputs match MemberBuilder 5. Performance benchmarks: compare before/after

Acceptance: All 791 tests pass; performance acceptable


Phase C.3d: MemberBuilder Removal (โณ Pending)

Tasks: 1. Delete reflection/member_builder.py 2. Remove all imports of MemberBuilder 3. Remove cache-based identity lookups (replaced by registry) 4. Run full test suite again (ensure no regressions) 5. Final performance validation

Acceptance: MemberBuilder removed; all tests pass; performance validated

๐Ÿ“ 7. Dependencies

Requires: - TD-033 complete: Engine + registry functional - TD-031 complete: Strategies produce correct outputs

Blocks: - Phase C.4 (Lazy Loading): Can't add lazy loading until migration complete

๐Ÿ“– 8. Changelog

  • 2025-11-15 โ€” Initial draft created
  • ReflectionContext integration plan
  • Component migration strategy
  • Cache API refinement design
  • MemberBuilder removal plan
  • Status: Draft (awaiting TD-033 + TD-031 completion)

End of TD-032