Tests on main had drifted from intentional substrate changes that
weren't propagated to their fixtures or pinned values. Categories:
1. PackMutationProposal missing source= arg (3 tests across
test_mutation_proposal_type, test_provenance, test_expert_demo_runnable):
add ProposalSource(kind="operator", source_id="", emitted_at_revision="test")
to the shared fixture. test_expert_demo_runnable also retargets the
"unpromoted domain" example from systems_software (now promoted) to
arithmetic (real but unpromoted).
2. Pack content grew (test_en_core_meta_v1_pack 73→77 entries, 49→53 verbs;
test_en_core_spatial_v1_pack 24→25 entries adding "places" plural surface):
bump expected counts; allow new provenance shapes from the
adr-0085-style-v2 review (including the seed:core_meta/seed:core_spatial
author-time typos on two entries each — documented inline rather than
masked).
3. Registry self-documenting "add names to the set" failures
(test_lane_sha_verifier: add curriculum_loop_closure;
test_register_runtime_threading: add gloss_aware_cause_surface,
pack_grounded_unknown_surface, teaching_grounded_surface_transitive).
4. Gloss content was seeded where tests pinned None
(test_pack_resolver_glosses TestMissingGlossesIsBackCompat): switch
the no-glosses pack from en_core_relations_v1 (since glossed) to
en_minimal_v1 (still gloss-free); narrow resolve_gloss probe to that
pack so other packs' glosses can't shadow.
5. Entry-id renumber from cognition-pack expansion
(test_language_pack_cache): en-core-cog-085 → en-core-cog-091.
6. Holdout tests fail without CORE_HOLDOUT_KEY or local plaintext
(test_eval_holdout_split + test_transitive_surface): add
_requires_holdout skip-marker mirroring _decrypt_holdout's contract;
gate the transitive_surface holdout iteration on the same check.
7. Byte-identity surface guards regressed after the gloss-aware
composer landed (test_realizer_guard_holdout, test_prompt_diversity_runner,
test_register_substantive_consumption): re-pin to current surfaces
("Light is a visible medium that reveals truth." replaces "Light is a
source of revelation that makes things knowable.", etc.). The guard's
regression-catching role is preserved by pinning current output going
forward; the new gloss-driven phrasings are visibly more grounded.
Touched 14 test files: 176 passed, 4 skipped (holdout-gated), 0 failed
on a targeted re-run. No production code touched.
204 lines
8.7 KiB
Python
204 lines
8.7 KiB
Python
"""Prompt-diversity lane runner — contract pins.
|
|
|
|
Pins the v1 contract surface so future composer changes (ADR-0085) and
|
|
surface-vs-envelope work cannot silently break the measurement
|
|
instrument the contract is built around.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from evals.framework import get_lane, run_lane
|
|
from evals.prompt_diversity.runner import (
|
|
_classify_response_shape,
|
|
_surface_has_audit_leak,
|
|
)
|
|
|
|
|
|
_PUBLIC_V1 = Path(__file__).resolve().parents[1] / "evals" / "prompt_diversity" / "public" / "v1" / "cases.jsonl"
|
|
|
|
|
|
def _load_public_cases() -> list[dict]:
|
|
return [json.loads(line) for line in _PUBLIC_V1.read_text().splitlines() if line.strip()]
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Shape classifier
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
|
|
class TestShapeClassifier:
|
|
@pytest.mark.parametrize(
|
|
"surface,expected_shape,want",
|
|
[
|
|
("Knowledge is justified true belief.", "predicate_identity", True),
|
|
("light reveals truth, which grounds knowledge", "explanation", True),
|
|
("First, observe. Then, infer.", "sequence", True),
|
|
("wisdom contrasts with judgment", "two_subject_contrast", True),
|
|
("No session evidence yet.", "honest_disclosure", True),
|
|
("knowledge, evidence, inference", "narrative", True),
|
|
# Mismatch direction — chain-walk shape miscast as definition
|
|
("light reveals truth, which grounds knowledge", "predicate_identity", False),
|
|
],
|
|
)
|
|
def test_shape_classifier(self, surface: str, expected_shape: str, want: bool) -> None:
|
|
assert _classify_response_shape(surface, expected_shape) is want
|
|
|
|
def test_unknown_shape_defaults_pass(self) -> None:
|
|
# Neutral pass for unknown shapes — protects against new
|
|
# categories being penalised before the classifier is taught.
|
|
assert _classify_response_shape("any text", "brand_new_shape") is True
|
|
|
|
def test_empty_surface_fails(self) -> None:
|
|
assert _classify_response_shape("", "predicate_identity") is False
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Audit-leak detector
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
|
|
class TestAuditLeak:
|
|
@pytest.mark.parametrize(
|
|
"surface,is_leak",
|
|
[
|
|
("light — teaching-grounded (cognition_chains_v1): ...", True),
|
|
("light — pack-grounded (en_core_cognition_v1): ...", True),
|
|
("No session evidence yet.", True),
|
|
# Bare semantic-domain tag in user surface
|
|
("cognition.illumination", True),
|
|
("logos.core; cognition.truth.", True),
|
|
# Clean user-facing surfaces
|
|
("Light is the medium by which what exists becomes visible.", False),
|
|
("Knowledge requires evidence.", False),
|
|
("", False),
|
|
],
|
|
)
|
|
def test_leak_detection(self, surface: str, is_leak: bool) -> None:
|
|
assert _surface_has_audit_leak(surface) is is_leak
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Gloss-quote detector
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
|
|
class TestGlossQuote:
|
|
"""The detector is exact-substring against the pack's gloss text,
|
|
not a fuzzy window. The pack-grounded composer emits gloss text
|
|
verbatim, so substring match is the right signal: zero false
|
|
positives, zero false negatives on brief-style short glosses where
|
|
a 4-token window would be impossible (e.g. ``person`` → ``"person
|
|
with a child"`` has only 3 tokens ≥4 chars).
|
|
"""
|
|
|
|
def _make(self, surface: str, terms: tuple[str, ...]) -> bool:
|
|
from evals.prompt_diversity.runner import _surface_quotes_gloss
|
|
return _surface_quotes_gloss(surface, terms)
|
|
|
|
def test_quoted_short_gloss_detected(self) -> None:
|
|
# ``light`` gloss is ``"a visible medium that reveals truth"`` —
|
|
# 6 tokens, only 4 are ≥4 chars; the old 4-token window would
|
|
# barely fit. ``parent`` gloss is ``"person with a child"`` —
|
|
# 4 tokens, 3 are ≥4 chars; the old window could never match.
|
|
# Substring match handles both natively.
|
|
assert self._make(
|
|
"Parent is person with a child. pack-grounded (en_core_relations_v1).",
|
|
("parent",),
|
|
) is True
|
|
assert self._make(
|
|
"Light is a visible medium that reveals truth. pack-grounded (en_core_cognition_v1).",
|
|
("light",),
|
|
) is True
|
|
|
|
def test_unquoted_surface_returns_false(self) -> None:
|
|
# Chain-walk surface for the same lemma must NOT count as
|
|
# gloss-quoted — it shares vocabulary but doesn't quote the
|
|
# gloss itself.
|
|
assert self._make(
|
|
"light — teaching-grounded (cognition_chains_v1): cognition.illumination; logos.core.",
|
|
("light",),
|
|
) is False
|
|
|
|
def test_unknown_term_returns_false(self) -> None:
|
|
assert self._make("anything", ("nonsense_lemma_42",)) is False
|
|
|
|
def test_empty_terms_returns_false(self) -> None:
|
|
assert self._make("anything", ()) is False
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# End-to-end run on the v1 public split
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
|
|
class TestPublicV1:
|
|
"""Pins the v1 contract surface against the public split.
|
|
|
|
The lane has NO numeric pass thresholds at v1 by design (the
|
|
contract is explicit about this). The ONLY hard gate is
|
|
``versor_closure_rate == 1.00``. Everything else is baseline
|
|
distribution we measure against, not score for.
|
|
"""
|
|
|
|
@pytest.fixture(scope="class")
|
|
def lane_result(self) -> object:
|
|
lane = get_lane("prompt_diversity")
|
|
return run_lane(lane, version="v1", split="public")
|
|
|
|
def test_lane_discoverable(self) -> None:
|
|
lane = get_lane("prompt_diversity")
|
|
assert "v1" in lane.versions
|
|
|
|
def test_all_cases_run(self, lane_result) -> None: # type: ignore[no-untyped-def]
|
|
cases = _load_public_cases()
|
|
assert lane_result.metrics["total"] == len(cases)
|
|
assert len(lane_result.case_details) == len(cases)
|
|
|
|
def test_versor_closure_invariant(self, lane_result) -> None: # type: ignore[no-untyped-def]
|
|
# The only numeric pass threshold at v1. Per ADR / contract:
|
|
# the algebra invariant must hold for every case the pipeline
|
|
# accepts.
|
|
assert lane_result.metrics["versor_closure_rate"] == 1.0
|
|
|
|
def test_all_metrics_in_unit_interval(self, lane_result) -> None: # type: ignore[no-untyped-def]
|
|
for key in (
|
|
"intent_accuracy",
|
|
"versor_closure_rate",
|
|
"response_shape_fit",
|
|
"audit_in_surface_rate",
|
|
"gloss_quote_rate",
|
|
):
|
|
value = lane_result.metrics[key]
|
|
assert 0.0 <= value <= 1.0, f"{key} out of unit interval: {value}"
|
|
|
|
def test_breakdown_groups_present(self, lane_result) -> None: # type: ignore[no-untyped-def]
|
|
# Per-cell breakdown by (question_shape, sophistication, domain)
|
|
# — the contract's "how to read the output" instructs callers
|
|
# to look at distributions, not just aggregates.
|
|
breakdown = lane_result.metrics["breakdown"]
|
|
assert isinstance(breakdown, dict)
|
|
assert breakdown, "breakdown is empty — runner did not aggregate cells"
|
|
# Every cell must carry the four moveable per-cell metrics.
|
|
for shape_cells in breakdown.values():
|
|
for soph_cells in shape_cells.values():
|
|
for cell in soph_cells.values():
|
|
assert {"n", "intent_accuracy", "response_shape_fit", "audit_in_surface_rate", "gloss_quote_rate"} <= cell.keys()
|
|
|
|
def test_baseline_diversity(self, lane_result) -> None: # type: ignore[no-untyped-def]
|
|
# The lane's STATED failure mode (contract §When it has failed
|
|
# and why): "if the distribution looks identical to the
|
|
# cognition lane (i.e. the suite isn't actually diverse)".
|
|
# Concretely: at least 5 distinct question_shape values and
|
|
# at least 3 distinct domain values must appear in the case
|
|
# details — otherwise the suite is overfitting the same way
|
|
# the cognition lane does.
|
|
details = lane_result.case_details
|
|
shapes = {d["question_shape"] for d in details}
|
|
domains = {d["domain"] for d in details}
|
|
assert len(shapes) >= 5, f"only {len(shapes)} question shapes — suite is not diverse"
|
|
assert len(domains) >= 3, f"only {len(domains)} domains — suite is not diverse"
|