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 027 — ReflectionContext API Simplification

Field Value
ID TD-027
Title ReflectionContext API Simplification
Status Implemented
Owner Reflection maintainers
Audience Reflection and DI contributors, documentation team, test maintainers
Created 2025-10-22
Last Updated 2025-10-24
Reviewers TBD
Approvers TBD
Tracking Roadmap 002 • Phase A (API Simplification)
Related Reflection Modernization Roadmap, Reflection Architecture

1. 🧭 Summary

ReflectionContext currently exposes six facade objects (resolve, scan, hints, annotations, invoke, create) that together surface sixteen callable entry points. The indirection obscures the public contract, complicates documentation, and makes it difficult to ensure tests cover the intended workflows. This design collapses the facade layer and promotes the supported operations to first-class ReflectionContext methods with explicit names (resolve_module, scan_package, invoke, construct, etc.), paired with refreshed documentation and test coverage for the canonical use cases. While we remove the facade layer we also collapse the constructor seams: ReflectionContext instantiates its own scanner, type resolver, invoker, instance factory, type hints, annotations, and type checker—only cache, metrics, and the optional TypeHintConfig remain constructor parameters.

The change reduces accidental API surface, clarifies how strict mode is applied, and makes it trivial for downstream code (DI runtime, fixtures, user guides) to discover supported behaviors. Because the project remains in alpha, the plan removes the facades without a transitional alias while providing migration guidance and automated tests that lock in the captured workflows. The plan retires or reworks tests that previously exercised configurability through builder overrides so they rely on component-level fixtures. As of Phase 2A, the constructor directly owns all core collaborators; scan_types remains available as a compatibility helper for existing callers and is now complemented by dedicated TypeInfo utilities.

At a glance:

  • Replace facade properties with explicit ReflectionContext methods and update the global reflection singleton accordingly.
  • Internalize collaborator construction inside ReflectionContext (scanner, resolver, invoker, instance factory, type hints, annotations, type checker) while keeping cache, metrics, and type-hint configuration configurable.
  • Document and test the core workflows (resolution, scanning, type hints, annotations, invocation, instance creation) against the new API.
  • Produce contributor-facing guidance and changelog entries so downstream consumers can migrate confidently; the plan removes or reworks builder-oriented tests in favor of component fixtures.
  • Introduce dedicated, public TypeInfo utilities so contributors can enumerate metadata without relying on facade internals. scan_types remains available for compatibility but is no longer required by project tests.

2. 📚 Context and Problem Statement

ReflectionContext is designed with nested facade classes to group related operations. Those facades continue to expand organically, now providing sixteen total callable methods. The grouping no longer aligns with how contributors reason about the API:

  • Facade boundaries (e.g., resolve vs. scan) duplicate responsibilities—developers must remember whether dotted-path resolution is available via resolve.dotted or scan.dotted.
  • Documentation and architecture references list the facade names, but downstream code (tests, DI runtime) often requires deeper knowledge of the hierarchy.
  • Each facade creates a new object per context instance, increasing boilerplate and subtly complicating strict-mode handling.

Roadmap 002 Phase A calls for narrowing the public surface and documenting supported workflows. Removing facades in favor of named methods directly on ReflectionContext accomplishes this while keeping the underlying provider wiring intact.

2.1 Goals

  • Promote the supported reflection operations to explicit, well-described methods on ReflectionContext.
  • Ensure every described use case (resolution, scanning, type hints, annotations, invocation, instance creation) has dedicated integration coverage that exercises the new methods.
  • Provide migration guidance for downstream consumers, including DI runtime modules, fixtures, and user guides.
  • Simplify testing by replacing builder-driven configurability suites with focused component fixtures or removing redundant coverage.

2.2 Out of Scope

  • Metrics redesign (captured in Phase B of the roadmap).
  • Reworking provider seams (annotations, type hints, parsing, invocation) beyond changing the call sites.
  • Type checker redesign or exposure changes (Phase F).
  • Performance tuning beyond the minimal improvements gained from removing facade objects.

2.3 Assumptions and Constraints

  • The project remains in alpha; breaking API changes can ship without a deprecation period, provided migration steps remain clearly described.
  • Downstream consumers rely on the behaviors covered by existing integration tests (tests/integration/reflection/test_context_api.py, test_context_ergonomics.py, etc.), which this plan updates as part of the work.
  • We must maintain existing strict-mode semantics (strict defaults to True unless overridden) and cache behavior.
  • The global reflection singleton in loryth.common.reflection.__init__ must use the new API without exposing the old facades.

3. 🧩 Design Overview

Phase A removes the facade layer and exposes a curated set of methods on ReflectionContext. Each method name clearly signals the operation it performs (resolve_module, scan_package, hints_for_callable, invoke, construct, etc.). Internally, the context constructs its own collaborators—package scanner, runtime type resolver, callable invoker, instance factory, type-hint provider, annotation provider, and type checker—leaving only cache and metrics as optional constructor inputs. Tests that previously relied on bespoke injections through the builder convert to component-level fixtures or retire when redundant.

The migration unfolds in staged increments:

  1. Introduce explicit methods that wrap the existing provider interactions, mirroring the logic currently living inside the facade classes.
  2. Internalize collaborator construction and eliminate the seams from ReflectionContext.__init__, while providing targeted fixtures for tests that still need custom behavior.
  3. Delete facade classes and properties, ensuring no dormant API remnants remain.
  4. Update documentation and tests to reference the new methods, adding a contributor guide section that lists the supported workflows and expectations.

The result is a discoverable, self-contained API surface that matches the architecture reference and accelerates upcoming metrics and seam rationalization work.

3.1 Key Components

Component Purpose
ReflectionContext Hosts the new first-class methods for resolution, scanning, metadata retrieval, and invocation.
Internal collaborator factories Default constructors for scanner, invoker, instance factory, type resolver, type hints, annotations, and type checker sit under ReflectionContext control.
Test fixtures (tests/integration/reflection/test_context_api.py, component-specific fixtures) Exercise the renamed APIs and validate supported use cases without relying on builder overrides.
Documentation (reflection-architecture.md, roadmap phase A section) Receives updates outlining the new public surface and migration steps.

3.2 Architecture

graph TD
    RC[ReflectionContext] --> IR[ImportResolver]
    RC --> TH[TypeHintProvider]
    RC --> AP[AnnotationProvider]
    RC -.internal.-> SC[PackageScanner]
    RC -.internal.-> TR[RuntimeTypeResolver]
    RC -.internal.-> INV[SafeCallableInvoker]
    RC -.internal.-> IF[TypeInstanceFactory]
    RC -.internal.-> TC[TypeChecker]

    classDef primary fill:none,stroke:#1976D2,stroke-width:2px,color:#1976D2;
    class RC primary;

Solid edges denote externally swappable dependencies (import resolver, type-hint provider, annotation provider). Dashed edges represent internally constructed components that are no longer passed through the constructor.

3.3 Interface Impact (Before → After)

Area Before (legacy state) After (use instead)
Module resolution ctx.resolve.module("pkg.mod") ctx.resolve_module("pkg.mod")
Package resolution ctx.resolve.package("pkg") ctx.resolve_package("pkg")
Type resolution ctx.resolve.type("pkg.Type") ctx.resolve_type("pkg.Type")
Function resolution ctx.resolve.function("pkg.fn") ctx.resolve_function("pkg.fn")
Dotted lookup (strict) ctx.resolve.dotted("pkg.symbol", strict=True) ctx.resolve("pkg.symbol", strict=True)
Package scanning ctx.scan.package("pkg", recursive=True) ctx.scan_package("pkg", recursive=True)
Module scanning ctx.scan.module("pkg.mod") ctx.scan_module("pkg.mod")
Type scanning ctx.scan.types([Type]) Removed — prefer TypeInfo utilities
Dotted scanning ctx.scan.dotted("pkg") Use resolve(...) + scan_package/scan_module
Callable hints ctx.hints.for_callable(callable_info) ctx.hints_for_callable(callable_info)
Type hints ctx.hints.for_type(type_info) ctx.hints_for_type(type_info)
Field hints ctx.hints.for_field(field_info) ctx.hints_for_field(field_info)
Property hints ctx.hints.for_property(prop_info) ctx.hints_for_property(prop_info)
Annotation retrieval ctx.annotations.for_member(member_info) ctx.annotations(member_info)
Callable invocation ctx.invoke(callable_info, *args, **kwargs) or ctx.invoke.call(...) ctx.invoke(callable_info, *args, strict=None, **kwargs)
Constructor invocation ctx.invoke.construct(type_info, *args, **kwargs) ctx.construct(type_info, *args, strict=None, **kwargs)
Instance creation ctx.create.instance(type_info, *args, **kwargs) ctx.create_instance(type_info, *args, strict=None, **kwargs)

3.4 Removal/Deprecation Policy

  • Alpha removal: The plan removes facade classes and properties immediately with no deprecation window.
  • Migration support: Documentation, changelog entries, and test updates provide clear guidance. Any external modules in this repository (DI bootstrap, fixtures) will be migrated in the same PR.
  • Future compatibility: Downstream packages outside the repo must adopt the new method names; no shim layer will be provided.

4. 📐 Implementation Details

4.1 Resolution API

ReflectionContext will surface direct resolution helpers while preserving strict-mode semantics. Callers can pass dotted strings or native Python objects where indicated.

class ReflectionContext:
    def resolve_module(self, module_path: str) -> ModuleInfo: ...
    def resolve_package(self, package_path: str) -> PackageInfo: ...
    def resolve_type(self, target: str | type[Any]) -> TypeInfo: ...
    def resolve_function(self, dotted_path: str) -> FunctionInfo: ...
    def resolve(self, path: str, *, strict: bool | None = None) -> MemberInfo | None: ...

Key behaviors:

  • resolve replaces resolve.dotted; strict defaults continue to follow _effective_strict.
  • resolve_type accepts either a dotted path or a concrete type[Any], returning the cached TypeInfo.

4.2 Scanning API

Scanning entry points remain focused on packages and modules, accepting either dotted strings or pre-built MemberInfo instances. Dotted scanning use cases are satisfied by calling resolve followed by the appropriate scan. The legacy scan_types helper continues to ship temporarily while we introduce focused TypeInfo utilities and migrate downstream callers; its removal is tracked under the Remaining Work section and earmarked for Phase 2B.

class ReflectionContext:
    def scan_package(
        self,
        package: str | PackageInfo,
        *,
        recursive: bool = True,
        public_only: bool = True,
        kinds: set[str] | None = None,
        name_contains: str | None = None,
    ) -> Iterable[MemberInfo]: ...

    def scan_module(
        self,
        module: str | ModuleInfo,
        *,
        public_only: bool = True,
        kinds: set[str] | None = None,
        name_contains: str | None = None,
    ) -> Iterable[MemberInfo]: ...

The design ultimately drops scan_types and scan_dotted. During the transition, contributors should:

  • Prefer scan_module/scan_package for module-level enumeration (accepting strings or MemberInfo instances, so dotted inputs remain ergonomic) and treat scan_types as a compatibility shim slated for removal.
  • Introduce lightweight utilities in loryth.common.reflection (outside ReflectionContext) to extract common views from TypeInfo (e.g., iter_methods(type_info)), keeping reflection context focused on discovery rather than convenience iteration once we retire scan_types.

4.3 Metadata API (Type Hints & Annotations)

Metadata retrieval remains accessible but is renamed for clarity.

class ReflectionContext:
    def hints_for_callable(self, callable_info: CallableInfo) -> Mapping[str, Any]: ...
    def hints_for_type(self, type_info: TypeInfo) -> Mapping[str, Any]: ...
    def hints_for_field(self, field: FieldInfo) -> Any | None: ...
    def hints_for_property(self, prop: PropertyInfo) -> Any | None: ...

    def annotations(self, member: MemberInfo) -> Sequence[AnnotationInfo]: ...

annotations consolidates former annotations.for_member. The method delegates to the configured annotation provider and maintains strict mode behavior (none).

4.4 Invocation & Instance Construction

Invocation entry points are renamed to align with their purpose while retaining strict-mode support and cache priming.

class ReflectionContext:
    def invoke(
        self,
        callable_info: CallableInfo,
        *args: Any,
        strict: bool | None = None,
        **kwargs: Any,
    ) -> Any: ...

    def construct(
        self,
        type_info: TypeInfo,
        *args: Any,
        strict: bool | None = None,
        **kwargs: Any,
    ) -> Any: ...

    def create_instance(
        self,
        type_info: TypeInfo,
        *args: Any,
        strict: bool | None = None,
        **kwargs: Any,
    ) -> Any: ...

Notes:

  • invoke prepares type-hint caches when strict mode is true before delegating to the invoker.
  • construct and create_instance remain aliases; create_instance continues to call into construct for ergonomics.

4.5 Internal Restructuring

Core steps

  • Remove nested facade classes (_ResolveFacade, _ScanFacade, _HintsFacade, _AnnotationsFacade, _InvokeFacade, _CreateFacade) and inline their logic into the new top-level methods.
  • Reuse _prepare_type_hints_cache, _effective_strict, and the extracted _build_scan_predicate helper to keep strict-mode and filtering behaviour intact.
  • Simplify ReflectionContext.__init__ so the context instantiates scanner, type resolver, callable invoker, instance factory, type-hint provider, annotation provider, and type checker internally while still honoring optional cache, metrics, and type-hint configuration parameters.
class ReflectionContext:
    def __init__(
        self,
        *,
        cache: HierarchicalCache | None = None,
        metrics: ReflectionMetrics | None = None,
        type_hint_config: TypeHintConfig | None = None,
    ) -> None:
        cache = cache or ConcurrentHierarchicalCache()
        metrics = metrics or ReflectionMetricsCollector()
        type_hint_config = type_hint_config or TypeHintConfig()

        self._cache = cache
        self._metrics = metrics
        self._member_builder = MemberBuilder(cache=self._cache)
        self._import_resolver = PythonImportResolver(
            builder=self._member_builder,
            metrics=self._metrics,
        )
        self._type_resolver = RuntimeTypeResolver(builder=self._member_builder)
        self._scanner = PackageScanner(
            imports=self._import_resolver,
            builder=self._member_builder,
            metrics=self._metrics,
        )
        self._invoker = SafeCallableInvoker(metrics=self._metrics)
        self._instance_factory = TypeInstanceFactory(invoker=self._invoker)
        self._type_hints = HighFidelityTypeHintProvider(
            import_resolver=self._import_resolver,
            metrics=self._metrics,
            cache=self._cache,
            strict=type_hint_config.strict,
            allow_module_level_fallback=type_hint_config.allow_module_level_fallback,
            cache_key_strategy=type_hint_config.cache_key_strategy,
        )
        self._annotations = DecoratorAnnotationProvider()
        self._type_checker = TypeChecker()

Next actions

  • Remove ReflectionContextBuilder (and ReflectionContext.derive() where applicable), migrating remaining tests and documentation to constructor + fixture-based customization.
  • Update the reflection singleton in loryth.common.reflection.__init__ to construct the context directly without involving the builder.
  • Deliver the new TypeInfo utility helpers that replace scan_types, then delete the legacy helper and migrate dependent call sites.

4.6 Documentation and Test Updates

  • Integration tests: Update tests/integration/reflection/test_context_api.py, test_context_ergonomics.py, test_context_workflows.py, and related fixtures to call the new methods. As part of the update, audit each builder-dependent test and categorize it as delete (pure configurability coverage) or refactor (replace with component-specific fixtures). See §7 for guidance.
  • Regression tests: Ensure strict-mode scenarios (test_api_conformance_flags.py, test_error_payloads_schema.py) cover both resolve and invoke semantics using the new constructor-driven context.
  • Documentation: Refresh docs/contributors/reflection-architecture.md and the roadmap Phase A section to list the new method names and explain the constructor simplification. Add a contributor guide snippet demonstrating side-by-side migration examples.
  • Changelog: Add an entry describing the API change (facade removal, constructor seam removal, new methods).

4.7 TypeInfo Utilities

New helper functions live in loryth.common.reflection.type_info_utils and are re-exported from the reflection package:

from loryth.common.reflection import (
    iter_public_fields,
    iter_public_methods,
    iter_public_properties,
)

The helpers provide ergonomic, public-API alternatives to scan_types, enabling tests and consumers to enumerate TypeInfo metadata without reintroducing facade seams. Dedicated unit coverage lives in tests/unit/reflection/type_info/test_type_info_utils.py.

5. ⚙️ Semantics

  • Strict mode: resolve defaults to strict (True) and raises ResolveError when a symbol cannot be located; passing strict=False returns None. Invocation APIs follow the same _effective_strict semantics as today.
  • Idempotent metadata: Methods returning ModuleInfo, TypeInfo, or annotations reuse cached instances; repeated calls do not rebuild metadata.
  • Scanning inputs: scan_package and scan_module accept either dotted strings or pre-resolved PackageInfo/ModuleInfo. When provided a string, the context resolves and then scans, ensuring metrics and caching behave identically to the original facade logic.
  • Invocation defaults: invoke, construct, and create_instance all pre-hydrate type-hint caches when strict mode applies and emit the same metrics events (scheduled for Phase B refinement).
  • Utility expectations: Any new TypeInfo helpers we introduce outside ReflectionContext must be pure functions that operate on existing MemberInfo objects without mutating them.

6. 🧪 Examples

6.1 Quickstart: Resolve and Scan

Start with the constructor-only API to resolve symbols, scan packages, and access metadata without using facades.

from loryth.common.reflection import ReflectionContext

ctx = ReflectionContext()

type_info = ctx.resolve_type("tests.resources.sample_module.ExampleClass")
module_info = ctx.resolve_module("tests.resources.sample_module")
maybe_member = ctx.resolve("tests.resources.sample_module.MISSING", strict=False)

for member in ctx.scan_package("tests.resources", recursive=False):
    print(member.qualname)

package_info = ctx.resolve_package("tests.resources")
public_classes = [
    m for m in ctx.scan_package(package_info, public_only=True) if m.kind.value == "class"
]

hints = ctx.hints_for_callable(type_info.ctor)  # type: ignore[arg-type]
annotations = ctx.annotations(type_info)

result = ctx.invoke(type_info.methods[0], 1, 2, strict=True)
instance = ctx.create_instance(type_info, 5, strict=False)

6.2 Inspect Metadata with TypeInfo Utilities

Pair the new TypeInfo helpers with the simplified context to enumerate public methods while reusing cached metadata.

from loryth.common.reflection import iter_public_methods

type_info = ctx.resolve_type("tests.resources.sample_module.ExampleClass")
method_names = [method.name for method in iter_public_methods(type_info)]

6.3 Strict Resolution with Targeted Scans

Show how strict resolution feeds directly into module-level scans for service discovery workflows.

service_info = ctx.resolve_type("myapp.services.UserService")
module_members = list(ctx.scan_module(service_info.module, name_contains="Service"))
assert any(member.qualname == service_info.qualname for member in module_members)

7. 🧪 Test Matrix

7.1 Scenarios

7.1.1 Resolve module/type/package/function via dotted string

Methods - resolve_module - resolve_type - resolve_package - resolve_function - resolve

Current Coverage - tests/integration/reflection/context/test_context_resolution.py - tests/integration/reflection/context/test_context_workflows.py - tests/integration/reflection/test_dotted_path_resolution.py

Actions - Refactor to fixture-driven tests with AAA structure and tolerant/strict assertions. - Validate identity reuse for concrete types and tolerant fallbacks in test_context_resolution.py.

7.1.2 Resolve using concrete type[Any]

Methods - resolve_type

Current Coverage - tests/integration/reflection/context/test_context_resolution.py

Actions - Maintain identity reuse assertions (is comparisons) alongside descriptive docstrings.

7.1.3 Package/module scanning (string and MemberInfo inputs)

Methods - scan_package - scan_module

Current Coverage - tests/integration/reflection/context/test_context_scanning.py - tests/integration/reflection/test_scan_filters_conformance.py

Actions - Ensure test_context_scanning.py covers PackageInfo reuse, AAA structure, and fixture-driven inputs. - Keep legacy scanning conformance tests aligned with public APIs while avoiding scan_types.

7.1.4 Type member introspection helpers

Methods - Utilities operating on TypeInfo (planned as needed)

Current Coverage - tests/unit/reflection/type_info/test_type_info_utils.py

Actions - Ship helper utilities with dedicated fixtures under tests/unit/reflection/type_info/ in line with TD-024.

7.1.5 Type-hint retrieval (callable/type/field/property)

Methods - hints_for_callable - hints_for_type - hints_for_field - hints_for_property

Current Coverage - tests/integration/reflection/context/test_context_metadata.py - tests/integration/reflection/context/test_context_workflows.py - tests/regression/reflection/test_property_precedence_rules.py

Actions - Supply callable/type metadata through shared fixtures; document strict/tolerant expectations in the context suite. - Keep regression coverage compatible without relying on builder overrides.

7.1.6 Annotation retrieval

Methods - annotations

Current Coverage - tests/integration/reflection/context/test_context_metadata.py

Actions - Rely on shared fixtures and public APIs for annotation assertions.

7.1.7 Strict invocation success/failure

Methods - invoke - construct - create_instance

Current Coverage - tests/integration/reflection/context/test_context_invocation.py - tests/integration/reflection/context/test_strict_invocation_behavior.py - tests/integration/reflection/test_api_conformance_flags.py - tests/unit/reflection/test_error_payloads_schema.py

Actions - Assert strict/tolerant behavior with shared fixtures while routing invocation payload tests through factory-driven contexts.

7.1.8 Cache clearing and context overrides

Methods - clear_caches - Test-only fixtures providing collaborator overrides

Current Coverage - tests/integration/reflection/context/test_cache_management.py

Actions - Validate cache clearing through public metrics and namespace eviction without touching internals.

7.2 Coverage Tracker (Phase 2E Target)

Scenario (§7.1.x) Primary Suites Notes
7.1.1 Resolve dotted inputs tests/integration/reflection/context/test_context_resolution.py, tests/integration/reflection/context/test_context_workflows.py, tests/integration/reflection/test_dotted_path_resolution.py Fixture-driven AAA tests with tolerant/strict assertions
7.1.2 Resolve concrete types tests/integration/reflection/context/test_context_resolution.py Identity reuse stays covered via is assertions
7.1.3 Package/module scanning tests/integration/reflection/context/test_context_scanning.py, tests/integration/reflection/test_scan_filters_conformance.py String vs MemberInfo inputs remain covered without scan_types
7.1.4 TypeInfo utilities tests/unit/reflection/type_info/test_type_info_utils.py Pure helpers operating on TypeInfo with localized fixtures
7.1.5 Type-hint retrieval tests/integration/reflection/context/test_context_metadata.py, tests/integration/reflection/context/test_context_workflows.py, tests/regression/reflection/test_property_precedence_rules.py Behavioral assertions only; uses reflection_context_factory
7.1.6 Annotation retrieval tests/integration/reflection/context/test_context_metadata.py Sequence assertions built on shared fixtures
7.1.7 Strict invocation paths tests/integration/reflection/context/test_context_invocation.py, tests/integration/reflection/context/test_strict_invocation_behavior.py, tests/integration/reflection/test_api_conformance_flags.py, tests/unit/reflection/test_error_payloads_schema.py Strict failure and tolerant success scenarios stay backed by shared fixtures
7.1.8 Cache management tests/integration/reflection/context/test_cache_management.py, tests/fixtures/reflection_common.py fixtures Cache eviction exercises public calls and metrics hooks

7.3 Phase 2E Additions Delivered

  • Strict/Tolerant Invocation Matrix — tests/integration/reflection/context/test_strict_invocation_behavior.py exercises failure and tolerant success paths via shared fixtures.
  • Cache Management Coverage — tests/integration/reflection/context/test_cache_management.py validates clear_caches() (global and namespaced) using public metrics hooks.
  • TypeInfo Utilities Suite — src/loryth/common/reflection/type_info_utils.py ships with unit coverage under tests/unit/reflection/type_info/test_type_info_utils.py.
  • Refactored Context API Tests — Context-focused suites reside in tests/integration/reflection/context/, each targeting a specific workflow (resolution, scanning, metadata, invocation, end-to-end).
  • Annotation & Metrics Enhancements — Metadata workflows employ shared fixtures and document tolerant semantics; cache suite covers metrics behavior.
  • Regression Alignment — Existing regression suites remain green and no longer rely on builder overrides; further clean-up is deferred to Phase 3.

7.4 Fixture & Utility Consolidation Plan

  • Promote reflection_context_factory to the canonical entry for integration suites while avoiding bespoke constructors in favour of shared fixtures.
  • Extend tests/fixtures/reflection_common.py with helper factories (for example, type_info_for) wrapping the new TypeInfo utilities while keeping behaviour observable and public.
  • Introduce tests/unit/reflection/type_info/conftest.py to share reusable TypeInfo samples across the utility tests without reintroducing builder seams.
  • Keep context-focused tests under tests/integration/reflection/context/ to eliminate inline fixture duplication.

7.5 Naming & Structure Improvements

  • File Reorganization — Keep context-specific suites in tests/integration/reflection/context/, including the strict/tolerant and cache-management modules.
  • Test Naming — Maintain the test_<component>_<behavior> convention with descriptive docstrings across the reorganized files.
  • Unit Suite Layout — Use tests/unit/reflection/type_info/ for the utility helpers; additional metrics-specific moves remain optional Phase 3 clean-up.
  • Shared Resources — Draw integration tests from centralized fixtures while tracking broader resource consolidation in Phase 3.

8. ✅ Acceptance Criteria

  • The plan removes all facade properties from ReflectionContext; the new method set matches §4 without omissions.
  • Existing integration/regression tests compile and pass with the renamed methods, augmented per the test matrix.
  • Contributor documentation (reflection-architecture.md, roadmap Phase A entry) reflects the revised API.
  • Changelog entry highlights the breaking change and new method names.
  • No remaining repository references to ctx.resolve.*, ctx.scan.*, ctx.hints.*, ctx.annotations.*, ctx.invoke.*, or ctx.create.* facade patterns (verified via repository search).
  • The plan removes ReflectionContextBuilder and ReflectionContext.derive(); all usages migrate to direct constructor + fixtures.
  • Test coverage aligns with the scenarios captured in the test matrix (§7).

9. 🚀 Rollout and Migration

9.1 Implementation Phases

Phase 1: API Surface Extraction

  • Add direct ReflectionContext helpers (resolve_module, scan_package, etc.) and document preferred usage.
  • Result: Downstream code adopts the simplified API without breaking existing paths.

Phase 2A: Resolver Defaults

  • Internalize default import/type resolver creation when constructor overrides are not provided and accept optional cache/metrics parameters on the constructor.
  • Introduce a shared reflection_context_factory / build_reflection_context helper so regression and integration suites no longer depend on builder overrides.
  • Outcome: ReflectionContext owns import/type resolution while cache/metrics remain optional parameters and tests cover the default-construction path.

Phase 2B: Scanner & Invocation Defaults

  • Internalize package scanner, callable invoker, and instance factory creation; replace builder overrides with explicit fixtures and validate strict-mode metrics behaviour across the integration suites.

Phase 2C: Metadata Provider Defaults

  • Ensure the constructor owns type-hint and annotation providers plus type checker; guide tests to use constructor knobs and fixtures, leaving no mutable collaborator seams.

Phase 2D: Builder Retirement & Constructor Simplification

  • Remove ReflectionContextBuilder, update the global singleton to instantiate ReflectionContext directly, and finish all active documentation/examples to highlight the constructor-only API (historical docs remain preserved).

Phase 2E: Test Matrix Coverage

  • Implement or update the test suites enumerated in §7 so each scenario in the matrix has focused coverage against the simplified API, including strict/tolerant paths and cache behavior.
  • Outcome: The defined behaviors stay protected by automated tests aligned with the new method surface before repository-wide cleanup.

Phase 3: Repository Adoption & Cleanup

  • Sweep the repository for legacy facade/builder usage, delete or refactor configurability tests per §7, and update changelog/documentation.

9.3 Migration Strategy

For Framework Maintainers 1. Execute Phase 2 sub-phases sequentially: internalize each collaborator default, migrate affected tests to component fixtures, complete the dedicated §7 test-matrix coverage pass, and drop redundant configurability suites. 2. Once the constructor owns the collaborators, remove ReflectionContextBuilder, limit ReflectionContext.__init__ to cache/metrics overrides, and update documentation/examples. 3. Finish with a repository sweep (Phase 3) replacing facade/builder usage, updating changelog entries, and confirming the test matrix is fully exercised.

For Downstream Consumers - Replace facade calls with the new method names listed in §3.3. - Instantiate ReflectionContext directly, optionally passing cache/metrics; other collaborators are no longer overridable. - Use component-level fixtures (e.g., stub scanners) when custom behaviour is required for tests.

9.4 Remaining Work Summary

  • Phase A scope is complete. Repository-wide adoption tasks (changelog updates, downstream migration guidance, optional scan_types deprecation) are tracked as part of Roadmap 002 Phase A.3.

9.5 Phase 2E Verification Plan

  • Run .venv/bin/python3.13 -m pytest tests/integration/reflection tests/unit/reflection to validate the refreshed suites; add targeted -k strict runs where helpful.
  • Execute rg "scan_types" after migrations to confirm remaining references are either transitional (noted) or ready for Phase 3 cleanup.
  • Re-read TD-027 §7–§9 post-implementation to ensure test coverage notes, fixtures, and rollout text remain synchronized with the codebase.
  • Capture any lingering historical-doc references or downstream adoption tasks and funnel them into Phase 3 tracking before closing Phase 2E.
  • Track .venv/bin/python3.13 -m pytest tests/integration/reflection tests/unit/reflection as the verification run and record results once executed.

10. 📊 Progress Status

  • Phase A.1 — API Surface Extraction: ✅ Completed (2025-10-20); direct helpers shipped and docs highlight new entry points.
  • Phase A.2A — Resolver Defaults: ✅ Completed (2025-10-22); constructor now owns resolver defaults and tests use the shared factory fixtures.
  • Phase A.2B — Scanner & Invocation Defaults: ✅ Completed (2025-10-23); constructor wires scanner/invoker/instance factory and tests rely on fixture helpers instead of builder overrides.
  • Phase A.2C — Metadata Provider Defaults: ✅ Completed (2025-10-23); constructor owns type-hint/annotation providers and type checker, leaving no mutable seams.
  • Phase A.2D — Builder Retirement: ✅ Completed (2025-10-24); constructor-only API and documentation updates landed, with historical docs intentionally preserved.
  • Phase A.2E — Test Matrix Coverage: ✅ Completed (2025-10-24); scenario-focused suites, strict/tolerant workflows, cache coverage, and TypeInfo utilities now guard the simplified API.
  • Phase A.3 — Adoption & Cleanup: 🔄 Not Started; repository sweep, test deletions/refactors, and changelog/documentation updates outstanding.

11. ⚠️ Risks and Mitigations

Risk Impact Mitigation
External consumers (outside repo) still rely on facades Runtime failures after upgrade Publish migration guide in changelog and release notes
Missed facade method during migration Incomplete API, broken tests Use interface impact table as checklist; run rg "ctx\.(resolve|scan|hints|annotations|invoke|create)\." to ensure no residual usage
Naming confusion for invocation methods Developer friction Clearly document invoke, construct, create_instance semantics; include examples for strict mode
Potential regressions in strict-mode handling Incorrect type checking behavior Add focused tests for strict vs. tolerant invocation paths; reuse regression suites
Builder removal breaks configurability-only tests False failures / lost coverage Classify tests per §7 (delete vs refactor) and add component fixtures where behaviour remains valuable

12. Open Questions

  • Should resolve_type support call sites that already have a TypeInfo instance (pass-through), or should those callers rely on existing helpers (of_type)? Pending review feedback.
  • When we re-introduce extension seams post Phase A, what is the minimal public configuration surface we want to expose (e.g., explicit factories vs. lightweight hooks)?

13. 📖 Glossary

  • ReflectionContext — Core orchestration class that wires import resolution, scanning, metadata providers, and invocation helpers.
  • Strict mode — Default behavior where resolution/invocation raises errors on mismatches; configurable per call via the strict parameter.
  • MemberInfo — Base metadata type describing reflected modules, packages, types, callables, and fields.
  • TypeInfo utilities — Planned helper functions that operate on TypeInfo instances (e.g., iterating methods) without requiring facade support.

14. 📎 Appendix

14.1 Quality Audit Findings (TD-024 Alignment)

  • Context-level integration tests now live under tests/integration/reflection/context/ with AAA structure, shared fixtures, and no scan_types dependencies.
  • Strict/tolerant invocation scenarios are covered in test_strict_invocation_behavior.py, while payload schemas reuse the reflection_context_factory fixture.
  • Cache management behavior is validated through the new integration suite, ensuring no private cache state checks remain.