"""Research method registry for Phase 1 framework selection.""" from __future__ import annotations from dataclasses import dataclass from pathlib import Path from typing import Any import yaml REPO_ROOT = Path(__file__).resolve().parents[2] DEFAULT_METHOD_CONFIG = REPO_ROOT / "configs" / "research_methods.yaml" @dataclass(frozen=True) class ResearchMethod: key: str name: str best_for: list[str] structure_principle: str task_axes: list[str] framework_sections: list[str] class ResearchMethodRegistry: def __init__(self, path: Path | None = None) -> None: self.path = path or DEFAULT_METHOD_CONFIG self._data = self._load() def _load(self) -> dict[str, Any]: if not self.path.exists(): raise FileNotFoundError(f"research method config not found: {self.path}") data = yaml.safe_load(self.path.read_text(encoding="utf-8")) or {} if not isinstance(data, dict) or "methods" not in data: raise ValueError(f"invalid research method config: {self.path}") return data @property def default_method(self) -> str: return (self._data.get("defaults") or {}).get("method", "mckinsey_market") def list_names(self) -> list[str]: return sorted((self._data.get("methods") or {}).keys()) def get(self, key: str | None = None) -> ResearchMethod: selected = key or self.default_method methods = self._data.get("methods") or {} if selected not in methods: raise KeyError(f"unknown research_method: {selected}") item = methods[selected] or {} return ResearchMethod( key=selected, name=item.get("name", selected), best_for=list(item.get("best_for") or []), structure_principle=item.get("structure_principle", ""), task_axes=list(item.get("task_axes") or []), framework_sections=list(item.get("framework_sections") or []), )