The final AGI-spine step (A INSTRUMENT → B WIRE → C DEEPEN → D CLOSE → E ESTIMATION).
The engine may now SERVE a DISCLOSED estimate for a query it would otherwise refuse —
but only for a predicate-class that has measured itself reliable, and never as fact.
This executes the ADR-0206 §5 cognition-path widening: the bridge's LICENSE node
(reliability_gate.license_for), previously "built — not yet called from serving", is now
called. govern_response returns APPROXIMATE iff a genuine licensed Action.SERVE
LicenseDecision is passed (STRICT for every other input — so every existing serving call
site is byte-identical); shape_surface DISCLOSES the estimate as "[approximate] …".
Mechanism:
- generate/determine/estimate.py — a BLIND converse-guesser: told p(a,b), asked p(b,a),
it commits the converse. It never reads the pack's symmetry metadata; whether the guess
is right is MEASURED, not assumed.
- evals/determination_estimation/ — the gold lane: run_practice (sealed, ADR-0199) folds
the converse-guesser over symmetric (sibling_of) vs directed (parent_of) cases, scored
against the pack's graph.edge.symmetric truth (gold independent of the solver). The gate
DISCRIMINATES: sibling_of earns SERVE (660 correct → Wilson floor 0.990046 ≥ θ_SERVE),
parent_of does not (660 wrong → 0.0). The license is earned by VOLUME — 657 perfect
commits is the exact θ_SERVE=0.99 threshold (656 is below).
- generate/determine/data/estimation_ledger.json — the ratified committed ledger,
hash-verified on load (a hand-edited ledger raises RatifiedLedgerError); it IS the
deterministic sealed-practice output (a GSM8K-style --check test pins this).
- chat/runtime.py — when a converse query is refused and the class holds a SERVE license,
the disclosed estimate is surfaced through the bridge (gated by config.estimation_enabled,
default OFF; only meaningful with accrue_realized_knowledge).
Invariants:
- wrong=0 by construction — an estimate is ALWAYS disclosed ([approximate]), never a silent
commit (UNVERIFIED_POSSIBLE is never in APPROXIMATE's admissible set), and only a genuine
ratified license widens (a forged {"licensed":True} dict / a PROPOSE license / an
unlicensed SERVE all stay STRICT). Defense-in-depth: type-gate ∧ admissible-set ∧
hardcoded disclosed state.
- never self-authored — ceilings stay at safe defaults (θ_SERVE=0.99); the engine cannot
raise its own bar. The ledger is sealed practice, hash-verified.
- session/serving only — no corpus/pack/identity/proposal/vault mutation; the HITL teaching
path is untouched. Deterministic; no clock/random.
- byte-identical for every non-E turn (the 2643 govern_response call passes no license).
Out of scope (separate ADR-0206 §5 PRs): the math-serving seam (select_self_verified,
touches the sealed metric), SITUATE (stakes), and the live FEED-BACK loop.
Verified green: smoke (90), architectural invariants (56), response_governance (321,
incl. the new license-gated widening test), the determination-estimation lane (12), and
the B/D/determine regression net. Four-lens adversarial review (disclosure/wrong=0,
calibration integrity, byte-identity, boundary/determinism): all held. Design:
docs/analysis/E-estimation-design-2026-06-06.md.
124 lines
4.7 KiB
Python
124 lines
4.7 KiB
Python
"""Gold lane for E — calibrating the converse-guess per predicate.
|
|
|
|
The blind converse-solver commits "the converse holds" for every problem; the
|
|
``GoldTether`` scores it against the *pack's own* symmetry declaration
|
|
(``graph.edge.symmetric`` vs ``graph.edge.directed`` in the relational predicates
|
|
lexicon) — a truth source independent of the solver (ADR-0199 L-2). Folding
|
|
``run_practice`` over the cases yields a committed ``ClassTally`` per predicate whose
|
|
Wilson floor the reliability gate reads: a symmetric predicate earns SERVE; a directed
|
|
one does not.
|
|
|
|
The lane is sized to the SERVE volume floor, not the bar to the lane: a perfect record
|
|
clears θ_SERVE=0.99 only at ``n/(n+z²) ≥ 0.99`` (z=2.576) ⇒ ``n ≥ 657``. Deterministic
|
|
synthetic entities; no clock, no randomness.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from core.learning_arena.protocols import BaseAttempt, DomainProblem, Problem
|
|
from generate.determine.estimate import converse_class_name
|
|
|
|
_LEXICON = (
|
|
Path(__file__).resolve().parents[2]
|
|
/ "language_packs"
|
|
/ "data"
|
|
/ "en_core_relational_predicates_v1"
|
|
/ "lexicon.jsonl"
|
|
)
|
|
|
|
#: Cases per predicate-class. ≥657 lets a perfect (symmetric) record clear the
|
|
#: θ_SERVE=0.99 Wilson floor; the same count on a directed class proves the gate
|
|
#: discriminates (its converse-guess is wrong every time → reliability 0).
|
|
CASES_PER_CLASS = 660
|
|
|
|
#: One symmetric + one directed predicate — the minimal discriminating pair. The
|
|
#: lane stays small and the falsification is unambiguous (licensed vs refused).
|
|
LICENSED_PREDICATE = "sibling_of" # graph.edge.symmetric → converse true
|
|
REFUSED_PREDICATE = "parent_of" # graph.edge.directed → converse false
|
|
|
|
|
|
def load_symmetric_predicates() -> frozenset[str]:
|
|
"""The predicates the pack declares symmetric (the GOLD truth, not the solver's)."""
|
|
out: set[str] = set()
|
|
for line in _LEXICON.read_text(encoding="utf-8").splitlines():
|
|
if not line.strip():
|
|
continue
|
|
entry = json.loads(line)
|
|
if "graph.edge.symmetric" in entry.get("semantic_domains", []):
|
|
out.add(entry["lemma"])
|
|
return frozenset(out)
|
|
|
|
|
|
class ConverseSolver:
|
|
"""The blind converse-guesser as a ``DomainSolver``: always commit "converse holds".
|
|
|
|
It reads no symmetry metadata — exactly the serving-side estimator's move
|
|
(``generate.determine.estimate``). Its per-class precision is therefore an honest
|
|
measurement of how often the converse-guess is right, never a peek at the truth.
|
|
"""
|
|
|
|
domain_id = "determination_estimation"
|
|
|
|
def attempt(self, problem: DomainProblem) -> BaseAttempt:
|
|
predicate, a, b = problem.payload["predicate"], problem.payload["a"], problem.payload["b"]
|
|
# Told p(a, b); guess the converse p(b, a) holds. Always commits.
|
|
return BaseAttempt(
|
|
committed=True,
|
|
answer={"predicate": predicate, "subject": b, "object": a, "holds": True},
|
|
reason="estimate_converse",
|
|
case_id=problem.problem_id,
|
|
)
|
|
|
|
|
|
class SymmetryGoldTether:
|
|
"""Tier-1 truth: the converse holds iff the pack declares the predicate symmetric."""
|
|
|
|
domain_id = "determination_estimation"
|
|
|
|
def __init__(self, symmetric: frozenset[str] | None = None) -> None:
|
|
self._symmetric = symmetric if symmetric is not None else load_symmetric_predicates()
|
|
|
|
def is_correct(self, attempt: BaseAttempt, problem: DomainProblem) -> bool:
|
|
return bool(attempt.answer["holds"]) is (problem.payload["predicate"] in self._symmetric)
|
|
|
|
def gold_answer(self, problem: DomainProblem) -> bool:
|
|
return problem.payload["predicate"] in self._symmetric
|
|
|
|
|
|
def generate_gold_problems(
|
|
predicate: str, n: int = CASES_PER_CLASS
|
|
) -> tuple[Problem, ...]:
|
|
"""``n`` deterministic converse-query problems for ``predicate``.
|
|
|
|
Each is "told ``p(a_i, b_i)``, asked ``p(b_i, a_i)``" over distinct synthetic
|
|
entities, tallied under the predicate's converse class.
|
|
"""
|
|
cls = converse_class_name(predicate)
|
|
return tuple(
|
|
Problem(
|
|
problem_id=f"{predicate}-{i:04d}",
|
|
class_name=cls,
|
|
payload={"predicate": predicate, "a": f"{predicate}_a{i}", "b": f"{predicate}_b{i}"},
|
|
)
|
|
for i in range(n)
|
|
)
|
|
|
|
|
|
def all_gold_problems() -> tuple[Problem, ...]:
|
|
"""The full lane: the licensed (symmetric) + refused (directed) classes."""
|
|
return generate_gold_problems(LICENSED_PREDICATE) + generate_gold_problems(REFUSED_PREDICATE)
|
|
|
|
|
|
__all__ = [
|
|
"CASES_PER_CLASS",
|
|
"ConverseSolver",
|
|
"LICENSED_PREDICATE",
|
|
"REFUSED_PREDICATE",
|
|
"SymmetryGoldTether",
|
|
"all_gold_problems",
|
|
"generate_gold_problems",
|
|
"load_symmetric_predicates",
|
|
]
|