One call-time resolver (env PORTFOLIO_SHARED_ROOT, default the in-repo shared/) consumed by both MAF-side readers of the shared core: persona._example_path() (the _EXAMPLE_PATH monkeypatch seam is kept) and simulation._default_bundle_dir() (replaces the _BUNDLE_DIR module global). De-risks the S4 extraction: re-pointing the commons becomes an env var, not a code change. Override test proves the marker follows a tmp copy of the whole shared tree; both detach points proven RED. Suite 155->157. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AaQCFnfsh3tfq1VfzdJpoi
56 lines
2.4 KiB
Python
56 lines
2.4 KiB
Python
"""Loader for the shared expert-reviewer persona artifact (målbilde §8).
|
|
|
|
The persona lives in ``shared/skills/expert-reviewer/`` as a framework-neutral Agent Skill (SKILL.md
|
|
+ a canonical example verdict). ``shared/`` stays pure DATA — this loader is the MAF-side reader of
|
|
it; the Claude-SDK sibling reads the same JSON with its own loader. This is what de-stubs the
|
|
offline simulation: its persona judgement is sourced from the artifact instead of a hardcoded
|
|
literal, so the shared persona is genuinely consumed (and cannot rot silently).
|
|
|
|
Fail-fast on purpose: the example is REQUIRED input (contrast the tolerant async verdict inbox,
|
|
``verdicts.load_verdicts_from_dir``). A missing or malformed file raises rather than degrading —
|
|
a broken shared artifact must surface, not pass silently.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
|
|
from portfolio_optimiser.shared_root import shared_root
|
|
|
|
# Test seam: when set (monkeypatched), wins over the resolver. Read at CALL time inside the loader
|
|
# (never frozen into a default argument), which is what makes the simulation's persona genuinely
|
|
# artifact-driven.
|
|
_EXAMPLE_PATH: Path | None = None
|
|
|
|
_EXAMPLE_SUBPATH = Path("skills") / "expert-reviewer" / "references" / "example-verdict.json"
|
|
|
|
|
|
def _example_path() -> Path:
|
|
"""The persona example's location: the test seam if set, else resolved under ``shared_root()``
|
|
(env ``PORTFOLIO_SHARED_ROOT`` re-points it — the S4 extraction seam)."""
|
|
return _EXAMPLE_PATH if _EXAMPLE_PATH is not None else shared_root() / _EXAMPLE_SUBPATH
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PersonaExample:
|
|
"""The expert-reviewer persona's canonical example verdict: the judgement the persona renders for
|
|
the reference measure. ``marker`` is a substring of ``rationale`` (the realization-rate payload
|
|
the simulation traces across runs)."""
|
|
|
|
decision: str
|
|
rationale: str
|
|
marker: str
|
|
|
|
|
|
def load_persona_example() -> PersonaExample:
|
|
"""Read the persona's canonical example verdict. Fail-fast: a missing file raises
|
|
``FileNotFoundError`` and a missing key raises ``KeyError`` (required input). Resolves the
|
|
path at call time (``_example_path``) so both the test seam and the env override are live."""
|
|
data = json.loads(_example_path().read_text(encoding="utf-8"))
|
|
return PersonaExample(
|
|
decision=data["decision"],
|
|
rationale=data["rationale"],
|
|
marker=data["marker"],
|
|
)
|