core/evals/determination_closure/runner.py
Shay 8edafd04ac feat(determine): idle deductive consolidation — the loop learns from determined facts (Step D)
CLOSE the autonomous-loop spine: when idle, the engine consolidates each
soundly-derived determination back into the held self, so the next determine()
reaches it directly and can chain one hop further. The directly-answerable set
climbs monotonically across idle ticks to the deductive-closure fixed point.

Mechanism — generate/determine/consolidate.py::consolidate_once runs ONE
semi-naive layer of the member/subset deductive closure (member∘subset → member,
subset∘subset → subset; NEVER member∘member — instance-of is not transitive).
Each one-hop conclusion not yet realized is VERIFIED by the sound+complete
proof_chain ROBDD (reusing C's single verifier _verify_subsumption) and written
back via generate/realize::realize_derived as a SPECULATIVE realized record
carrying derived-provenance (premise structure_keys + rule + the ENTAILED
verdict). idle_tick gains a consolidation pass gated by the new
config.consolidate_determinations (default OFF); IdleTickResult.facts_consolidated
reports the layer.

Invariants held:
- wrong=0 — every consolidated fact is a sound-rule conclusion confirmed by the
  sound+complete decider; member∘member is structurally unreachable (a member fact
  is only ever extended by a subset edge). _verify_subsumption now refuses a
  mislabeled/wrong-arity path (belt-and-suspenders now that consolidation is a
  second caller), so the fallacy cannot be laundered through a corrupted chain.
- honesty — a fact derived from SPECULATIVE premises stays SPECULATIVE / as-told;
  the soundness of the inference never upgrades the standing of the premises.
  COHERENT is never minted.
- teaching-safety — SESSION memory (immediate), an extension of the realize path;
  NOT corpus mutation and NOT coupled to proposals. The HITL path is untouched.
- determinism/replay — pure function of the realized set; sorted write order;
  derived structure_key identical to a told fact's; provenance round-trips through
  the Shape B+ snapshot (consolidated facts resume the SAME life across reboot).
- no new normalization — writes reuse the INV-21-allowed vault writer;
  algebra/versor.py keeps closure.

Falsification — evals/determination_closure: a frozen replay seeds a deep is-a
chain and runs idle ticks; asserts the closure climbs monotonically to a complete
fixed point (no-op final tick), wrong=0 (member∘member canary never derived, no
fabricated membership), all derived facts SPECULATIVE, and every derived record
re-verifies ENTAILED from its recorded premises.

Verified green: smoke, runtime, cognition, architectural invariants, plus the new
D unit + lane tests and the determine/realize/persistence regression net. Five-lens
adversarial review: 4 lenses held; the 5th (normalization) was a misattribution
(pre-existing vault reproject, triggered identically by the merged realize path,
on sanctioned null-vector storage). Design + findings: docs/analysis/
D-close-consolidation-design-2026-06-06.md.
2026-06-06 12:28:09 -07:00

253 lines
9.6 KiB
Python

"""Determination-closure lane — the falsification for Step D (CLOSE).
Proves — deterministically, not by assertion — that idle consolidation makes the engine
LEARN from its determined facts: the set it can answer DIRECTLY climbs monotonically
across idle ticks to the deductive-closure fixed point, and a further tick is a no-op.
The soak seeds a deep is-a chain (``member(rex, c0)`` + ``subset(c0, c1) … subset(cₙ₋₁,
cₙ)``) through the REAL comprehend → realize path, then runs ``consolidate_once``
repeatedly. After each tick it records the directly-realized closure size. The lane also
carries the wrong=0 canary (a ``member ∘ member`` trap that must NEVER be derived) and
the provenance replay obligation (every derived record must re-verify ENTAILED from its
recorded premises — so the soundness claim can MEANINGFULLY FAIL).
Determinism: no clock, no LLM, no metric call; a pure function of the seeded chain.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass
from typing import Any
from chat.runtime import ChatRuntime
from generate.determine.consolidate import consolidate_once
from generate.determine.determine import Determined, _verify_subsumption
from generate.meaning_graph.reader import comprehend
from generate.realize import RealizedRecord, realize_comprehension, recall_realized
from session.context import SessionContext
_HIGH_REPROJECT = 10**9
#: A deep is-a chain whose every ``All <plural[i]> are <plural[i+1]>`` parses as a
#: ``subset`` edge (singular lemmas: dog → mammal → … → item). The seed subject ``rex``
#: is a member of ``dog``; the chain gives 9 subset edges, so the closure reaches 10
#: classes from the one told membership.
_CHAIN_PLURALS = (
"dogs",
"mammals",
"animals",
"creatures",
"beings",
"mortals",
"things",
"entities",
"objects",
"items",
)
#: Singular lemmas the reader produces for the chain (what realized facts are keyed on).
_CHAIN_LEMMAS = (
"dog",
"mammal",
"animal",
"creature",
"being",
"mortal",
"thing",
"entity",
"object",
"item",
)
_SEED_SUBJECT = "rex"
#: The wrong=0 canary: ``member(dog, kingdom)`` is a membership ABOUT the class ``dog``.
#: With ``member(rex, dog)`` also held, an unsound ``member ∘ member`` rule would derive
#: ``member(rex, kingdom)``. The sound closure must NEVER produce it.
_CANARY_CLASS_MEMBERSHIP = ("Dog", "kingdom") # → member(dog, kingdom)
_CANARY_FORBIDDEN = (_SEED_SUBJECT, "kingdom") # member(rex, kingdom) must never appear
@dataclass(frozen=True, slots=True)
class TickRecord:
"""One idle consolidation tick's outcome."""
tick: int
member_closure_size: int # directly-realized member(rex, ·) facts
considered: int
consolidated: int
at_fixed_point: bool
def _fresh_context() -> SessionContext:
rt = ChatRuntime(no_load_state=True)
return SessionContext(
vocab=rt._context.vocab,
persona=rt._context.persona,
vault_reproject_interval=_HIGH_REPROJECT,
)
def _tell(ctx: SessionContext, text: str) -> None:
realize_comprehension(comprehend(text), ctx)
def seed_chain(ctx: SessionContext, depth: int) -> None:
"""Seed ``member(rex, c0)`` + ``depth`` subset edges + the canary, via the REAL
comprehend → realize path. ``depth`` is clamped to the available chain length."""
depth = max(1, min(depth, len(_CHAIN_PLURALS) - 1))
_tell(ctx, f"{_SEED_SUBJECT.capitalize()} is a {_CHAIN_LEMMAS[0]}.")
for i in range(depth):
_tell(ctx, f"All {_CHAIN_PLURALS[i]} are {_CHAIN_PLURALS[i + 1]}.")
# Canary membership about the class (member ∘ member trap).
_tell(ctx, f"{_CANARY_CLASS_MEMBERSHIP[0]} is a {_CANARY_CLASS_MEMBERSHIP[1]}.")
def _member_closure(ctx: SessionContext) -> tuple[str, ...]:
"""The objects ``rex`` is DIRECTLY realized to be a member of (sorted)."""
return tuple(
sorted(
f.relation_arguments[1]
for f in recall_realized(ctx, subject=_SEED_SUBJECT, predicate="member")
)
)
def _order_subset_path(
start: str, premises: tuple[RealizedRecord, ...]
) -> tuple[RealizedRecord, ...]:
"""Order subset premise records into a path from ``start`` (forward walk)."""
by_src: dict[str, RealizedRecord] = {
p.relation_arguments[0]: p for p in premises if len(p.relation_arguments) == 2
}
path: list[RealizedRecord] = []
cur = start
while cur in by_src:
edge = by_src.pop(cur)
path.append(edge)
cur = edge.relation_arguments[1]
return tuple(path)
def reverify_derived(ctx: SessionContext, record: RealizedRecord) -> bool:
"""Re-verify a derived record ENTAILED from its RECORDED premises (the provenance
replay obligation). Re-fetches each premise by ``structure_key`` and re-runs the
SAME sound+complete decider — so a record whose stored premises do not actually
entail it fails loudly."""
if not record.derived or record.derivation is None:
return False
premises: list[RealizedRecord] = []
for key in record.derivation.premise_structure_keys:
hits = recall_realized(ctx, structure_key=key)
if len(hits) != 1:
return False # a premise vanished or is ambiguous → cannot stand
premises.append(hits[0])
subject, target = record.relation_arguments
member_premises = tuple(p for p in premises if p.relation_predicate == "member")
subset_premises = tuple(p for p in premises if p.relation_predicate == "subset")
if record.relation_predicate == "member":
if len(member_premises) != 1:
return False
member_fact = member_premises[0]
path = _order_subset_path(member_fact.relation_arguments[1], subset_premises)
verdict = _verify_subsumption(
"member", subject, target, member_fact=member_fact, subset_path=path
)
else:
path = _order_subset_path(subject, subset_premises)
verdict = _verify_subsumption(
"subset", subject, target, member_fact=None, subset_path=path
)
return isinstance(verdict, Determined)
def run(depth: int = 9) -> dict[str, Any]:
"""Soak the closure and return the falsification report.
Runs to the fixed point (bounded by ``depth + 2`` ticks — a safety backstop, never
reached by a converging closure), then computes the falsification verdicts.
"""
ctx = _fresh_context()
seed_chain(ctx, depth)
effective_depth = max(1, min(depth, len(_CHAIN_PLURALS) - 1))
trajectory: list[TickRecord] = []
pre = len(_member_closure(ctx))
trajectory.append(TickRecord(0, pre, 0, 0, False))
max_ticks = effective_depth + 2
tick = 0
while tick < max_ticks:
tick += 1
result = consolidate_once(ctx)
trajectory.append(
TickRecord(
tick=tick,
member_closure_size=len(_member_closure(ctx)),
considered=result.considered,
consolidated=result.consolidated,
at_fixed_point=result.at_fixed_point,
)
)
if result.at_fixed_point:
break
sizes = [t.member_closure_size for t in trajectory]
# Monotone non-decreasing across all ticks; strictly increasing on every tick that
# consolidated something (a real layer must enlarge the directly-answerable set).
monotone = all(b >= a for a, b in zip(sizes, sizes[1:]))
strict_on_work = all(
t.member_closure_size > trajectory[i].member_closure_size
for i, t in enumerate(trajectory[1:])
if t.consolidated > 0
)
converged = trajectory[-1].at_fixed_point
# rex reaches every class on the chain it was seeded into (c0 told + c1..c_depth).
final_closure = list(_member_closure(ctx))
expected_closure = sorted(_CHAIN_LEMMAS[: effective_depth + 1])
closure_complete = final_closure == expected_closure
# wrong=0: the member ∘ member canary is never derived, and no class outside the
# chain ever appears in rex's closure.
forbidden_present = _CANARY_FORBIDDEN[1] in final_closure
chain_set = set(_CHAIN_LEMMAS[: effective_depth + 1])
no_fabricated = set(final_closure) <= chain_set
# Provenance replay: every derived record re-verifies ENTAILED from its premises.
derived_records = [
r
for r in recall_realized(ctx)
if r.derived and r.relation_predicate in ("member", "subset")
]
provenance_replay_ok = all(reverify_derived(ctx, r) for r in derived_records)
all_derived_speculative = all(
r.epistemic_status == "speculative" for r in derived_records
)
falsification_met = (
monotone
and strict_on_work
and converged
and closure_complete
and not forbidden_present
and no_fabricated
and provenance_replay_ok
and all_derived_speculative
)
return {
"depth": effective_depth,
"trajectory": [asdict(t) for t in trajectory],
"member_closure_sizes": sizes,
"final_member_closure": final_closure,
"expected_member_closure": expected_closure,
"derived_record_count": len(derived_records),
"verdicts": {
"monotone": monotone,
"strict_increase_on_consolidating_tick": strict_on_work,
"converged_to_fixed_point": converged,
"closure_complete": closure_complete,
"canary_member_member_never_derived": not forbidden_present,
"no_fabricated_membership": no_fabricated,
"provenance_replay_ok": provenance_replay_ok,
"all_derived_speculative": all_derived_speculative,
},
"falsification_met": falsification_met,
}