feat(persona): load the falsification skill from commons at call time

[skip-docs] — the invariant row for this plan lands in Step 13, after the mutations.

Amendment 2 in the same commit: the framework-neutrality sweep covered only
expert-reviewer, so the skill that arrived by subtree pull had no framework guard
anywhere. GUARDED_SKILL_DIRS names both, and a fail-closed coverage arm turns red
when a future pull brings a third skill that is not listed.

Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-02 21:31:32 +02:00
commit 488a0f2b6c
3 changed files with 276 additions and 3 deletions

View file

@ -54,3 +54,87 @@ def load_persona_example() -> PersonaExample:
rationale=data["rationale"],
marker=data["marker"],
)
# --- The falsification-reviewer skill (Step 12, D4) ----------------------------------------------
# A SECOND shared Agent Skill, read the same way and for the same reason: it is authored in commons
# and arrives by ``git subtree pull``, so a loader that consumes it is what keeps it from rotting
# silently. Same call-time seam, same fail-fast discipline as the persona example above.
_FALSIFICATION_EXAMPLE_PATH: Path | None = None
_FALSIFICATION_SUBPATH = (
Path("skills") / "falsification-reviewer" / "references" / "example-evidence.json"
)
def _falsification_example_path() -> Path:
"""The worked example's location: the test seam if set, else resolved under ``shared_root()``."""
if _FALSIFICATION_EXAMPLE_PATH is not None:
return _FALSIFICATION_EXAMPLE_PATH
return shared_root() / _FALSIFICATION_SUBPATH
@dataclass(frozen=True)
class FalsificationConcept:
"""One concept the worked example judged, with what the readers should derive from its bytes.
``frontmatter_verbatim`` is AUTHORITATIVE and is the only field a consumer should materialise
from. The example also ships a line-oriented ``frontmatter`` projection, which is informative
only: by construction it cannot carry a block form, so materialising from it would turn the
unreadable case into an absent one the tolerant read that widens the answer.
"""
concept_id: str
#: The document's frontmatter block exactly as written, newlines and indentation included.
frontmatter_verbatim: str
state: str
reason: str | None
items_seen: int
trust_tier: str
adjudication: str
#: Whether the verdict was allowed to rest on this concept.
relied_on: bool
@dataclass(frozen=True)
class FalsificationExample:
"""The falsification reviewer's worked example: one claim, the concepts consulted, the verdict.
``judgement`` is ``undecided`` in the shipped example, and deliberately not ``survived``: the
one concept that could have carried a refutation was unreadable, so the claim was never
actually attacked. Returning ``survived`` there would convert a gap in the knowledge base into
support for the claim the inversion this role exists to prevent.
"""
claim: str
judgement: str
#: Who refuted the claim, or ``None`` when nobody did. A refutation that names no refuter is
#: not a refutation (the ``verified``-without-an-actor rule, one layer up).
refuter: str | None
concepts: tuple[FalsificationConcept, ...]
def load_falsification_example() -> FalsificationExample:
"""Read the falsification skill's worked example. Fail-fast: a missing file raises
``FileNotFoundError`` and a missing key raises ``KeyError`` (required input, contrast the
tolerant Step-7 verdict inbox). Resolves the path at CALL time."""
data = json.loads(_falsification_example_path().read_text(encoding="utf-8"))
return FalsificationExample(
claim=data["claim"],
judgement=data["judgement"],
refuter=data["refuter"],
concepts=tuple(
FalsificationConcept(
concept_id=c["concept_id"],
frontmatter_verbatim=c["frontmatter_verbatim"],
state=c["evidence"]["state"],
reason=c["evidence"]["reason"],
items_seen=c["evidence"]["items_seen"],
trust_tier=c["derived"]["trust_tier"],
adjudication=c["derived"]["adjudication"],
relied_on=c["relied_on"],
)
for c in data["concepts"]
),
)