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.
78 lines
3.2 KiB
Python
78 lines
3.2 KiB
Python
"""E — ESTIMATION (roadmap Step 5): the calibrated converse-guess.
|
|
|
|
DETERMINE refuses a symmetric-converse query as *sound-but-incomplete*: told
|
|
``p(a, b)``, asked ``p(b, a)``, it answers only the stored direction (see
|
|
``generate.meaning_graph.relational``). E adds a single **defeasible** estimator on
|
|
top of that refusal — the converse-guess: "``p(a, b)`` is told, so guess ``p(b, a)``
|
|
holds too." It is **blind** (it never reads the pack's symmetry metadata), so it is
|
|
*correct* on a symmetric predicate and *wrong* on a directed one. Whether the engine
|
|
is allowed to SERVE the guess is decided NOT here but by the reliability gate
|
|
(ADR-0175 ``license_for(SERVE)``) over a committed per-predicate ``ClassTally`` — the
|
|
engine serves the guess only for predicate-classes it has measured itself reliable on,
|
|
and even then the surface is DISCLOSED ``[approximate]`` (ADR-0206 ``shape_surface``),
|
|
never asserted as fact.
|
|
|
|
wrong=0: this module only *proposes a candidate*; it commits nothing and asserts
|
|
nothing. The gate decides licensing; disclosure marks every served estimate. A wrong
|
|
estimate is therefore always a DISCLOSED wrong, never a silent one.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from generate.realize import recall_realized
|
|
from session.context import SessionContext
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class ConverseEstimate:
|
|
"""A defeasible converse-guess candidate for a ``p(subject, object)`` query.
|
|
|
|
``answer`` is always ``True`` — the estimator's only move is "the converse
|
|
relation holds". ``basis="estimate_converse"`` keeps it distinct from a
|
|
determination's ``as_told``/``verified``: this was guessed, not grounded.
|
|
``told_structure_key`` ties the guess to the realized fact it generalized,
|
|
so the served estimate is replayable to its evidence.
|
|
"""
|
|
|
|
predicate: str
|
|
subject: str
|
|
object: str
|
|
answer: bool
|
|
basis: str
|
|
told_structure_key: str
|
|
|
|
|
|
#: The capability-axis id a converse-guess is tallied under — the predicate itself.
|
|
#: Each predicate earns (or fails to earn) its own SERVE license independently.
|
|
def converse_class_name(predicate: str) -> str:
|
|
return f"converse:{predicate}"
|
|
|
|
|
|
def estimate_converse(
|
|
ctx: SessionContext, predicate: str, subject: str, target: str
|
|
) -> ConverseEstimate | None:
|
|
"""Return a converse-guess for ``p(subject, target)`` iff the converse was told.
|
|
|
|
The estimator fires only when a realized ``p(target, subject)`` exists (the
|
|
stored direction DETERMINE already declined to generalize). It returns the
|
|
candidate WITHOUT committing it — the caller (the reliability-gated serving
|
|
wire) decides whether the predicate-class is licensed to serve it, disclosed.
|
|
Returns ``None`` when there is no told converse to generalize from.
|
|
"""
|
|
told = recall_realized(ctx, subject=target, predicate=predicate)
|
|
grounding = next((f for f in told if f.relation_arguments == (target, subject)), None)
|
|
if grounding is None:
|
|
return None
|
|
return ConverseEstimate(
|
|
predicate=predicate,
|
|
subject=subject,
|
|
object=target,
|
|
answer=True,
|
|
basis="estimate_converse",
|
|
told_structure_key=grounding.structure_key,
|
|
)
|
|
|
|
|
|
__all__ = ["ConverseEstimate", "converse_class_name", "estimate_converse"]
|