The first consumer of the P1-A VERIFIED contract. Given an R2 problem (prose) + the INDEPENDENTLY hand-authored gold SETUP, it builds a VerificationProof and runs it through the contract. NOT a serving-time VERIFIED producer: it depends on the eval-lane gold setup (off-serving only), cannot move any serving metric, cannot emit served [verified]. Serving-time independence (a non-gold second reader) is a separate, larger rung, explicitly deferred. evals/constraint_oracle/verified_producer.py (placed in evals/, not core/, to avoid a backwards core->evals import: it needs the reader+solver (generate), the canonical signature + gold (evals), and the contract (core) — clean direction evals->core+generate): - verify_r2(text, gold_setup) -> R2VerificationOutcome. - Two independent reads: primary = read_constraint_problem(text) (lineage constraint_reader_v1); independent = the gold-authored setup (lineage constraint_gold_author_v1). Convergence = equal constraint_setup_signature digests (the SAME function for both reads). The gold ANSWER never enters — only the gold STRUCTURE, through the signature. - A wrong reader structure diverges from the gold-authored structure -> reads_disagree -> NOT_VERIFIED (the poison). Reader refusal -> no proof + a limitation. Solver boundary -> boundary_clear False -> NOT_VERIFIED. VERIFIED state/claim reached ONLY via disclosure_for_verification. On the real r2_gold lane (13 records): VERIFIED=7 (exactly the 7 'solved'), NOT_VERIFIED=6 (3 solver_refuses + 3 reader_refuses) — wrong=0. 11 tests: integration over real gold (verifies >=1, never on divergence); all 7 solved verify; the poison (divergent independent read -> reads_disagree); reader-refusal -> no proof + limitation; solver-boundary -> not verified; distinct/named lineages; the sanctioned route; all digests populated; gold answer structurally cannot enter; off-serving. NOTE (bound_slots_digest): the P1-A VerificationProof (merged, unchanged per scope) has no bound_slots_digest field; the "bound to stated quantities" obligation is carried by derivation_digest (hashed over the canonical constraints + solution). Flag to extend. Off-serving; smoke 90/0.
202 lines
8.2 KiB
Python
202 lines
8.2 KiB
Python
"""P1-B — tests for the gold-setup-backed OFF-SERVING R2 verification producer.
|
|
|
|
The producer is NOT a serving-time VERIFIED producer: it uses the eval-lane gold SETUP
|
|
(never the gold answer) as the independent read. These tests prove it constructs real
|
|
VerificationProof objects that survive the P1-A contract on real R2 problems, AND that a
|
|
wrong/divergent read never verifies (wrong=0 for the producer).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ast
|
|
import inspect
|
|
import pathlib
|
|
|
|
from core.epistemic_disclosure.disclosure_claim import DisclosureClaim
|
|
from core.epistemic_disclosure.verified_contract import (
|
|
REASON_BOUNDARY_FIRED,
|
|
REASON_READS_DISAGREE,
|
|
VerificationVerdict,
|
|
disclosure_for_verification,
|
|
)
|
|
from core.epistemic_state import EpistemicState
|
|
from evals.constraint_oracle.runner import _load_r2_gold, gold_to_problem
|
|
from evals.constraint_oracle.signature import constraint_setup_signature
|
|
from evals.constraint_oracle.verified_producer import (
|
|
CONSTRAINT_GOLD_AUTHOR_LINEAGE,
|
|
CONSTRAINT_READER_LINEAGE,
|
|
verify_r2,
|
|
)
|
|
from generate.constraint_comprehension.model import ConstraintProblem
|
|
from generate.constraint_comprehension.reader import read_constraint_problem
|
|
from generate.meaning_graph.reader import Refusal
|
|
|
|
|
|
def _gold():
|
|
return _load_r2_gold()
|
|
|
|
|
|
def _solved_record():
|
|
return next(r for r in _gold() if r.get("expect") == "solved")
|
|
|
|
|
|
# --- the producer verifies real problems, and NEVER on a divergent read ------------- #
|
|
|
|
def test_integration_real_gold_verifies_some_and_never_on_divergence():
|
|
records = _gold()
|
|
assert records
|
|
# 'reader_refuses' fixtures carry no setup fields (there is no canonical setup for a
|
|
# problem the reader must refuse). The reader refuses first, so gold_setup is unused on
|
|
# that path — a placeholder keeps the loop uniform.
|
|
placeholder = gold_to_problem(_solved_record())
|
|
verified = 0
|
|
for rec in records:
|
|
has_setup = "unknowns" in rec
|
|
gold = gold_to_problem(rec) if has_setup else placeholder
|
|
outcome = verify_r2(rec["text"], gold)
|
|
primary = read_constraint_problem(rec["text"])
|
|
|
|
if isinstance(primary, Refusal):
|
|
assert outcome.proof is None, rec["id"]
|
|
assert outcome.limitation is not None, rec["id"]
|
|
assert outcome.result.verdict is VerificationVerdict.NOT_VERIFIED, rec["id"]
|
|
continue
|
|
|
|
if not has_setup:
|
|
# reader unexpectedly read a reader_refuses case; it must still not verify
|
|
assert outcome.result.verdict is VerificationVerdict.NOT_VERIFIED, rec["id"]
|
|
continue
|
|
|
|
reads_converge = (
|
|
constraint_setup_signature(primary) == constraint_setup_signature(gold)
|
|
)
|
|
if outcome.result.verdict is VerificationVerdict.VERIFIED:
|
|
verified += 1
|
|
# wrong=0 for the producer: a VERIFIED proof must have converged reads, a real
|
|
# back-substitution, and the routed verified state/claim.
|
|
assert reads_converge, rec["id"]
|
|
assert outcome.proof is not None and outcome.proof.back_substitution_digest != ""
|
|
assert (outcome.epistemic_state, outcome.disclosure_claim) == (
|
|
EpistemicState.VERIFIED,
|
|
DisclosureClaim.VERIFIED,
|
|
)
|
|
elif not reads_converge:
|
|
assert REASON_READS_DISAGREE in outcome.result.failed_checks, rec["id"]
|
|
|
|
assert verified >= 1, "producer should verify at least one real R2 problem"
|
|
|
|
|
|
def test_all_solved_records_verify():
|
|
"""Every gold 'solved' record (reader reads it, solver solves it) produces a VERIFIED
|
|
proof — the producer's positive headline on real problems."""
|
|
solved = [r for r in _gold() if r.get("expect") == "solved"]
|
|
assert solved
|
|
for rec in solved:
|
|
outcome = verify_r2(rec["text"], gold_to_problem(rec))
|
|
assert outcome.result.verdict is VerificationVerdict.VERIFIED, rec["id"]
|
|
assert outcome.disclosure_claim is DisclosureClaim.VERIFIED, rec["id"]
|
|
|
|
|
|
# --- the poison: a wrong / divergent independent read ------------------------------- #
|
|
|
|
def test_divergent_independent_read_is_reads_disagree():
|
|
records = _gold()
|
|
readable = next(
|
|
r for r in records if not isinstance(read_constraint_problem(r["text"]), Refusal)
|
|
)
|
|
primary_sig = constraint_setup_signature(read_constraint_problem(readable["text"]))
|
|
divergent_setup = next(
|
|
gold_to_problem(r)
|
|
for r in records
|
|
if constraint_setup_signature(gold_to_problem(r)) != primary_sig
|
|
)
|
|
outcome = verify_r2(readable["text"], divergent_setup)
|
|
assert outcome.result.verdict is VerificationVerdict.NOT_VERIFIED
|
|
assert REASON_READS_DISAGREE in outcome.result.failed_checks
|
|
assert outcome.disclosure_claim is not DisclosureClaim.VERIFIED
|
|
|
|
|
|
# --- reader refusal / solver boundary ----------------------------------------------- #
|
|
|
|
def test_reader_refusal_yields_no_proof_and_a_limitation():
|
|
outcome = verify_r2("", gold_to_problem(_gold()[0])) # empty text → reader refuses
|
|
assert outcome.proof is None
|
|
assert outcome.result.verdict is VerificationVerdict.NOT_VERIFIED
|
|
assert outcome.limitation is not None
|
|
assert outcome.epistemic_state is not EpistemicState.VERIFIED
|
|
|
|
|
|
def test_solver_boundary_records_do_not_verify():
|
|
refusers = [r for r in _gold() if r.get("expect") == "solver_refuses"]
|
|
assert refusers
|
|
for rec in refusers:
|
|
outcome = verify_r2(rec["text"], gold_to_problem(rec))
|
|
assert outcome.result.verdict is VerificationVerdict.NOT_VERIFIED, rec["id"]
|
|
if outcome.proof is not None:
|
|
# reader matched gold but solver refused → boundary fired
|
|
assert (
|
|
REASON_BOUNDARY_FIRED in outcome.result.failed_checks
|
|
or REASON_READS_DISAGREE in outcome.result.failed_checks
|
|
), rec["id"]
|
|
|
|
|
|
# --- the contract's independence + the sanctioned route ----------------------------- #
|
|
|
|
def test_lineages_are_distinct_and_named():
|
|
assert CONSTRAINT_READER_LINEAGE != CONSTRAINT_GOLD_AUTHOR_LINEAGE
|
|
assert CONSTRAINT_GOLD_AUTHOR_LINEAGE == "constraint_gold_author_v1"
|
|
|
|
|
|
def test_produced_proof_uses_distinct_lineages():
|
|
rec = _solved_record()
|
|
outcome = verify_r2(rec["text"], gold_to_problem(rec))
|
|
assert outcome.proof is not None
|
|
assert outcome.proof.primary_reader_lineage != outcome.proof.independent_reader_lineage
|
|
|
|
|
|
def test_verified_state_and_claim_route_only_through_the_contract_gate():
|
|
rec = _solved_record()
|
|
outcome = verify_r2(rec["text"], gold_to_problem(rec))
|
|
assert outcome.result.verdict is VerificationVerdict.VERIFIED
|
|
assert (outcome.epistemic_state, outcome.disclosure_claim) == disclosure_for_verification(
|
|
outcome.result
|
|
)
|
|
|
|
|
|
def test_all_proof_digests_populated_on_verified():
|
|
rec = _solved_record()
|
|
outcome = verify_r2(rec["text"], gold_to_problem(rec))
|
|
p = outcome.proof
|
|
assert p is not None
|
|
for digest in (
|
|
p.source_problem_digest,
|
|
p.primary_read_digest,
|
|
p.independent_read_digest,
|
|
p.derivation_digest,
|
|
p.back_substitution_digest,
|
|
):
|
|
assert digest, "every replay-critical digest must be populated on a VERIFIED proof"
|
|
|
|
|
|
# --- the gold ANSWER can never enter the proof -------------------------------------- #
|
|
|
|
def test_gold_answer_cannot_enter_the_proof():
|
|
"""Structural: the producer takes only (text, ConstraintProblem), and ConstraintProblem
|
|
carries no answer field — so the gold answer cannot be an input to verification."""
|
|
params = list(inspect.signature(verify_r2).parameters)
|
|
assert params == ["text", "gold_setup"]
|
|
assert "answer" not in ConstraintProblem.__dataclass_fields__
|
|
assert "gold" not in ConstraintProblem.__dataclass_fields__
|
|
|
|
|
|
# --- hygiene ------------------------------------------------------------------------ #
|
|
|
|
def test_producer_is_off_serving():
|
|
import evals.constraint_oracle.verified_producer as mod
|
|
|
|
module_path = mod.__file__
|
|
assert module_path is not None
|
|
src = pathlib.Path(module_path).read_text()
|
|
mods = [n.module or "" for n in ast.walk(ast.parse(src)) if isinstance(n, ast.ImportFrom)]
|
|
forbidden = [m for m in mods if "generate.derivation" in m or "reliability_gate" in m]
|
|
assert forbidden == []
|