"""Library-level MAF tool carriers over the framework-neutral ``okf`` readers. One named carrier per reader whose answer a hypothesiser needs to be able to ASK for. The gate on this module is the carrier, not the vocabulary it carries: an accessor that returns three tokens which nothing propagates is a vocabulary, not a seam. **Import direction is one-way and load-bearing:** this module imports ``okf``, never the reverse. ``okf`` is MAF-free by invariant (``test_okf_is_maf_free``, D7 vendor-neutrality) and this module is not, so a back-edge would drag ``agent_framework`` into the portable context layer. **Not the MCP seam.** ``mcp_tools`` is the opt-in EXTERNAL seam with its own allowlist and announcement rules; a local library primitive placed there would sit behind an egress contract it has no business in. These carriers make no network call and announce nothing. **Deliberately NOT wired into ``run_project``.** Wiring a carrier into the run surface would move the byte-pinned demo transcript, which nothing asks for, and would make the state reachable only by reading a run's output rather than by calling a function — the ``evidence_for`` rule, unchanged. """ from __future__ import annotations from typing import Any from agent_framework import FunctionTool, tool from portfolio_optimiser import okf from portfolio_optimiser.retrieval import safe_resolve def adjudication_payload(bundle_dir: str, concept: str) -> dict[str, Any]: """The carrier's answer: the concept asked about, AND the state read for it. Both halves are the point. A payload naming the concept without its state would leave ``adjudication_for`` perfectly correct and the hypothesiser none the wiser, which is exactly the silent gap this seam closes. Path-safe via ``safe_resolve`` (fail-closed), so a concept name escaping the bundle raises rather than reading an arbitrary file.""" resolved = safe_resolve(bundle_dir, concept) return {"concept": concept, "adjudication": okf.adjudication_for(resolved)} def make_adjudication_tool(bundle_dir: str) -> FunctionTool: """The ONE named carrier exposing a concept's adjudication state to a hypothesiser.""" @tool( name="concept_adjudication_state", description=( "Report whether a concept in the knowledge base has been adjudicated. Returns " "`proposed`, `adjudicated`, or `unknown` when the concept does not say — `unknown` " "means the state was not learned, never that adjudication did not happen." ), ) def concept_adjudication_state(concept: str) -> dict[str, Any]: return adjudication_payload(bundle_dir, concept) return concept_adjudication_state