feat(s53): load_dimension fail-fast loader (MAF-free, mirrors load_goal_config)

New load_dimension(str | Path) in dimension.py: is_file() -> FileNotFoundError,
then Dimension.model_validate_json -> pydantic.ValidationError on malformed shape.
Fail-fast because dimension config is authoritative startup input (contrast the
tolerant verdict-inbox RAW layer). Stdlib + pydantic only — stays MAF-free
(test_okf_is_maf_free AST guard green). Exported from __init__.py in both the
import block and __all__. RED-first: 3 loader tests failed on the missing import,
green after. tests/test_dimension.py + test_okf.py: 29 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KNNiJRk1sSwxgVLS5AobT1
This commit is contained in:
Kjell Tore Guttormsen 2026-07-23 21:31:32 +02:00
commit 124b7aedde
3 changed files with 59 additions and 2 deletions

View file

@ -1,7 +1,7 @@
"""portfolio-optimiser — generic MAF framework for per-project cost-savings optimization."""
from portfolio_optimiser.contracts import GoalConfig, GoalContract, load_goal_config
from portfolio_optimiser.dimension import Dimension, admits
from portfolio_optimiser.dimension import Dimension, admits, load_dimension
from portfolio_optimiser.ledger import (
LedgerEntry,
RealizationRefused,
@ -37,6 +37,7 @@ __all__ = [
# Fase 1 domain model
"Dimension",
"admits",
"load_dimension",
"SavingsLedger",
"LedgerEntry",
"realize",

View file

@ -14,6 +14,8 @@ at least one affected code must match an allowed prefix.
from __future__ import annotations
from pathlib import Path
from pydantic import BaseModel, Field
@ -42,3 +44,25 @@ def admits(*, measure_type: str, codes: frozenset[str], dimension: Dimension) ->
return any(
code.startswith(prefix) for code in codes for prefix in dimension.allowed_code_prefixes
)
def load_dimension(path: str | Path) -> Dimension:
"""Fail-fast standalone loader for a dimension scope config (mirrors ``load_goal_config``).
A dimension config is *authoritative startup input*, so loading is fail-fast: a
missing file raises ``FileNotFoundError`` and malformed/invalid content raises
``pydantic.ValidationError``. This is the deliberate contrast to the tolerant
verdict-inbox RAW layer (``load_verdicts_from_dir``), which skips bad files rather
than raising startup scope must never be silently degraded.
Stdlib + ``pydantic`` only: ``dimension.py`` is in ``_MAF_FREE_MODULES`` and the AST
guard (``tests/test_okf.py::test_okf_is_maf_free``) fails on any ``agent_framework``/
``mcp`` import here.
:raises FileNotFoundError: ``path`` does not point at an existing file.
:raises pydantic.ValidationError: the JSON is malformed or violates the ``Dimension`` schema.
"""
p = Path(path)
if not p.is_file():
raise FileNotFoundError(f"dimension config not found: {str(path)!r}")
return Dimension.model_validate_json(p.read_text(encoding="utf-8"))