core/evals/setup_oracle/runner.py
Shay 59974865ef feat(comprehension): question target in the graph (PR-1) + setup-oracle lane (grade the reading)
Two coupled, additive, off-serving changes toward the typed math-comprehension organ.
No serving path touched; the relational_metric answer lane stays 15/15 wrong=0.

PR-1 — QuantQuery → BoundUnknown. comprehend_quantitative now emits the question
target as a BoundUnknown INSIDE the binding-graph (symbol_id, state_index="terminal",
question_form "count"|"total", expected_unit), so the graph is a real question-bearing
mathematical object and its canonical serialization carries the target. The external
QuantQuery is RETAINED, consistent-by-construction, so the two consumers
(to_relational_metric, realize/quantitative) are byte-identical; a follow-up rewires
them onto graph.unknowns and drops the duplicate field.

Setup-oracle lane (evals/setup_oracle) — grade the READING, not the answer. The
relational_metric lane scores answers, which can bless a semantically-wrong derivation
that coincidentally lands on the right number (the exact hazard the held-out
measurements + the 2/87 resolve_pooled probe exposed). The setup-oracle compares the
reader's comprehended STRUCTURE — a span-free signature of facts + typed equations +
the BoundUnknown target — against the INDEPENDENT gold structure (the relational_metric
cases' own relations/query, authored separately from the binding-graph reader). A
structural mismatch is setup_wrong, the wrong=0-critical count, even when the answer
would be right. v1 grades structure (units deferred — covered by admissibility). The
reader reads all 15 cases with the gold structure (setup_wrong=0); a meaningful-fail
test proves the oracle catches a right-answer/wrong-structure reading (it is not
decoration). `python -m evals.setup_oracle` exits nonzero iff setup_wrong > 0.

This is the measurement rig BEFORE investing in frame families: setup_wrong=0 is the
gate; serving must not move while setup_wrong > 0. It is the first milestone of the
math-comprehension organ, not a path to "solve GSM8K".

Verified: setup-oracle 15/15 setup_correct wrong=0; quantitative + setup-oracle unit
tests (17); realize-binding-graph + binding-graph + architectural invariants (183).
2026-06-06 16:40:15 -07:00

81 lines
2.9 KiB
Python

"""Setup-oracle runner — grade the reader's comprehended structure vs gold structure.
For each relational_metric case: comprehend the prose into a binding-graph, project it
to relations + read its question target, and compare the (relations, target) SIGNATURE
to the case's INDEPENDENT gold (`relations` + `query`). A structural mismatch is
``setup_wrong`` — the wrong=0-critical count — even if the answer would be right.
This is a STRICTER gate than the relational_metric (answer) lane: it requires the
reader to have read the problem the way the gold says it reads, not merely to land on
the gold number. It is the gate every future frame family must pass before serving.
"""
from __future__ import annotations
from typing import Any
from evals.relational_metric.runner import _load_cases
from evals.setup_oracle.signature import (
gold_unknown_signature,
reader_unknown_signature,
relation_signature,
)
from generate.meaning_graph.reader import Refusal
from generate.quantitative_comprehension import comprehend_quantitative, to_relational_metric
def run() -> dict[str, Any]:
"""Score the reader's setup against the independent gold setup, structure-only."""
cases = _load_cases()
setup_correct = setup_wrong = setup_refused = 0
wrongs: list[dict[str, Any]] = []
for case in cases:
comp = comprehend_quantitative(case["text"])
if isinstance(comp, Refusal):
setup_refused += 1
continue
projected = to_relational_metric(comp)
if projected is None:
setup_refused += 1
continue
reader_relations, _reader_query = projected
reader_sig = relation_signature(reader_relations)
gold_sig = relation_signature(case["relations"])
reader_unk = reader_unknown_signature(comp.binding_graph)
gold_unk = gold_unknown_signature(case["relations"], case["query"])
if reader_sig == gold_sig and reader_unk == gold_unk:
setup_correct += 1
else:
setup_wrong += 1
wrongs.append(
{
"id": case.get("id"),
"relations_match": reader_sig == gold_sig,
"target_match": reader_unk == gold_unk,
"reader_relations": reader_sig,
"gold_relations": gold_sig,
"reader_target": reader_unk,
"gold_target": gold_unk,
}
)
return {
"lane": "setup_oracle",
"grades": "structure-only (facts + equations + question target); units deferred",
"total": len(cases),
"setup_correct": setup_correct,
"setup_wrong": setup_wrong,
"setup_refused": setup_refused,
"wrongs": wrongs,
"counts": {
"setup_correct": setup_correct,
"setup_wrong": setup_wrong,
"setup_refused": setup_refused,
},
}
__all__ = ["run"]