"""Read model provider registry — K-Ext-1.""" from __future__ import annotations from dataclasses import dataclass from typing import Any, Callable from steering.eval_context import SteeringEvalContext ReadModelCompute = Callable[[SteeringEvalContext], Any] @dataclass(frozen=True) class ReadModelProvider: key: str requires_elements: frozenset[str] requires_data_slices: frozenset[str] compute: ReadModelCompute requires_any_element: frozenset[str] = frozenset() _PROVIDERS: list[ReadModelProvider] = [] def register_read_model(provider: ReadModelProvider) -> None: _PROVIDERS.append(provider) def compute_read_models(ctx: SteeringEvalContext) -> dict[str, Any]: """Run all matching read model providers for the initiative context.""" elements = ctx.steering_elements slices = frozenset(ctx.data_slices) result: dict[str, Any] = {} for provider in _PROVIDERS: if provider.requires_elements and not provider.requires_elements <= elements: continue if provider.requires_any_element and not ( provider.requires_any_element & elements ): continue if provider.requires_data_slices and not provider.requires_data_slices <= slices: continue result[provider.key] = provider.compute(ctx) return result def _register_builtin_read_models() -> None: from steering.read_models.epic_rollup import compute_epic_rollup from steering.read_models.execution_graph import compute_execution_graph_read_model from steering.read_models.planning_debt import compute_planning_debt_read_model from steering.read_models.tech_debt import compute_tech_debt_summary register_read_model( ReadModelProvider( key="epic_rollup", requires_elements=frozenset({"backlog_epic_hierarchy"}), requires_data_slices=frozenset({"backlog"}), compute=lambda ctx: compute_epic_rollup( backlog_items=ctx.backlog_items, actions=ctx.actions, ), ) ) register_read_model( ReadModelProvider( key="planning_debt", requires_elements=frozenset({"gate_fulfillment"}), requires_data_slices=frozenset({"roadmap", "actions"}), compute=compute_planning_debt_read_model, ) ) register_read_model( ReadModelProvider( key="execution_graph", requires_elements=frozenset(), requires_any_element=frozenset({"critical_path", "gate_fulfillment"}), requires_data_slices=frozenset({"actions"}), compute=compute_execution_graph_read_model, ) ) register_read_model( ReadModelProvider( key="tech_debt_summary", requires_elements=frozenset(), requires_data_slices=frozenset({"backlog", "actions"}), compute=compute_tech_debt_summary, ) ) _register_builtin_read_models()