fix(realize): lookback hardening — defensive admissibility re-assertion + coverage
From the mandated lookback audit of the composed R0→R1→R1c→OOV surface:
- wrong=0 HAZARD (medium): realize_quantitative trusted equation admissibility it
never checked. `comprehend_quantitative` runs real check_admissibility, but the
type does not enforce it, so a future non-reader constructor could slip a
dimensionally-incoherent 'pending'/'refused' equation into the held self (then
surfaced as-told by DETERMINE). Now RE-ASSERTS admitted-status defensively ->
NotRealized("unadmitted_equation"); docstring corrected to match (no longer claims
the type guarantees it). Bite test via a hand-built pending-equation graph.
- Defensive: wrap binding_graph hashing -> NotRealized("unhashable_structure") so a
future numeric field is a clean refusal, not an uncaught TypeError mid-write.
- Coverage (the obligations now bite, not decoration): unadmitted_equation,
not_a_quant_comprehension, no_bound_fact, grounding_failed (monkeypatched probe),
cross-substrate coexistence (meaning_graph + binding_graph in one vault — recall
isolates, structure_keys differ), and the negated_relation refusal (hand-built,
since the reader encodes declarative negation in the PREDICATE not rel.negated).
- Drift: R0 idempotency test now names the actual dedup key (structure_key, not
content_hash); scope doc notes #591 made OOV placement injective (the §0/§1
non-injectivity finding describes the pre-#591 substrate).
Note: the lookback flagged negated_relation as a high reachable-untested hazard, but
verification showed the reader never sets rel.negated=True for declaratives (it
refuses "X is not a Y" or uses some_not/disjoint predicates) — so it is a defensive
branch, tested via a hand-built graph. Green: 35 realize + ruff clean.
This commit is contained in:
parent
be2d4b487e
commit
6816e6220c
4 changed files with 127 additions and 8 deletions
|
|
@ -47,6 +47,14 @@ realized metadata, layered in `generate/realize/`.
|
|||
|
||||
## 1. OOV-grounding: the prior claim is refuted — it is deterministic, not non-deterministic
|
||||
|
||||
> **Update (#591 shipped):** the §0/§1 non-injectivity finding below
|
||||
> (`zelophehad`/`photosynthesis`/`unbridgeable` → one root-versor; field-metric
|
||||
> recall "degenerate") describes the **pre-#591** substrate. #591 (`ingest/gate.py`
|
||||
> content-derived `_token_spin_delta`, merged) makes OOV placement **injective by
|
||||
> token content** (verified live: those three now produce three distinct versors), so
|
||||
> the gate-lift no longer rests only on structural recall — though structural recall
|
||||
> remains what *carries* correctness.
|
||||
|
||||
R0's recorded finding ("OOV grounding is NON-deterministic across reboots") is
|
||||
**wrong** and is corrected here. Evidence (`/tmp/oov_proc.py`, faithful in-tree,
|
||||
`no_load_state`):
|
||||
|
|
|
|||
|
|
@ -56,11 +56,13 @@ def realize_quantitative(
|
|||
) -> Realized | NotRealized:
|
||||
"""Realize an arithmetic comprehension's binding_graph into ``ctx``'s vault.
|
||||
|
||||
Eligibility: a ``QuantComprehension`` (not a ``Refusal``). Factless / malformed
|
||||
arithmetic prose is already a ``Refusal`` upstream (``comprehend_quantitative``),
|
||||
so the only wrong=0 guard needed here is the ``Refusal`` type check — every
|
||||
``QuantComprehension`` carries an admissibility-checked binding graph. SPECULATIVE
|
||||
always (COHERENT is never a default); dedup by the span-free structure_key.
|
||||
Eligibility: a ``QuantComprehension`` (not a ``Refusal``) whose every equation is
|
||||
``admitted``. Every ``QuantComprehension`` PRODUCED BY ``comprehend_quantitative``
|
||||
carries an admissibility-checked graph (real ``check_admissibility``; a ``Refusal``
|
||||
short-circuits unreadable input) — but the type does NOT enforce it, so this
|
||||
function RE-ASSERTS admitted-status defensively, keeping the wrong=0 floor
|
||||
independent of the caller. SPECULATIVE always (COHERENT is never a default); dedup
|
||||
by the span-free structure_key.
|
||||
"""
|
||||
if isinstance(comprehension, Refusal):
|
||||
return NotRealized("refusal")
|
||||
|
|
@ -70,6 +72,13 @@ def realize_quantitative(
|
|||
bg = comprehension.binding_graph
|
||||
if not bg.facts:
|
||||
return NotRealized("no_bound_fact") # defensive — the reader guarantees >=1 fact
|
||||
# wrong=0 defense: realize ONLY a fully-admitted binding graph. The model permits a
|
||||
# structurally-valid graph to carry a 'pending'/'refused' equation, so re-assert
|
||||
# admitted-status here — a future non-reader constructor cannot slip a
|
||||
# dimensionally-incoherent equation into the held self (it would otherwise be
|
||||
# surfaced as-told by DETERMINE).
|
||||
if any(e.admissibility_status != "admitted" for e in bg.equations):
|
||||
return NotRealized("unadmitted_equation")
|
||||
|
||||
# Placement: the asked entity's field point. Symbolic/OOV, so deterministic-
|
||||
# GIVEN-session-state, NOT subject-determined; the structural key carries
|
||||
|
|
@ -83,8 +92,14 @@ def realize_quantitative(
|
|||
versor = np.asarray(field_state.F, dtype=np.float32)
|
||||
|
||||
structure_canonical = bg.to_canonical_string()
|
||||
content_hash = sha256_of(structure_canonical)
|
||||
structure_key = _binding_graph_structure_key(bg)
|
||||
# ``sha256_of`` rejects floats (canonical-JSON contract). The binding-graph fields
|
||||
# fed here are str by the model's contract today; wrap defensively so a future
|
||||
# numeric field is a clean refusal, never an uncaught TypeError mid-write.
|
||||
try:
|
||||
content_hash = sha256_of(structure_canonical)
|
||||
structure_key = _binding_graph_structure_key(bg)
|
||||
except (TypeError, ValueError):
|
||||
return NotRealized("unhashable_structure")
|
||||
status = EpistemicStatus.SPECULATIVE
|
||||
source_spans = [f.source_span.to_canonical_string() for f in bg.facts] or [
|
||||
s.source_span.to_canonical_string() for s in bg.symbols
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ def test_single_in_vocab_declarative_is_realized(vocab_persona) -> None:
|
|||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Idempotency — a re-told fact does not grow the vault (dedup by content_hash)
|
||||
# Idempotency — a re-told fact does not grow the vault (dedup by structure_key)
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
|
||||
|
|
@ -100,10 +100,35 @@ def test_retold_fact_is_idempotent(vocab_persona) -> None:
|
|||
second = _realize("Truth is a concept.", ctx)
|
||||
assert isinstance(first, Realized) and first.created is True
|
||||
assert isinstance(second, Realized) and second.created is False # dedup hit
|
||||
# dedup is on the span-free structure_key (content_hash coincides only because the
|
||||
# surface is identical here); name the actual dedup key.
|
||||
assert second.record.structure_key == first.record.structure_key
|
||||
assert second.record.content_hash == first.record.content_hash
|
||||
assert len(ctx.vault._metadata) == 1 # NOT grown
|
||||
|
||||
|
||||
def test_negated_relation_realizes_nothing(vocab_persona) -> None:
|
||||
# The reader encodes declarative negation in the PREDICATE (some_not/disjoint), so
|
||||
# rel.negated=True is reachable only via a hand-built graph — but the defensive
|
||||
# refusal must bite: a negated relation ("X is NOT a Y") must never be realized as
|
||||
# a positive fact.
|
||||
from generate.meaning_graph.model import Entity, MeaningGraph, MeaningSpan, Relation
|
||||
from generate.meaning_graph.reader import Comprehension
|
||||
|
||||
span = MeaningSpan(source_id="input", start=0, end=18, text="truth not concept")
|
||||
graph = MeaningGraph(
|
||||
entities=(
|
||||
Entity(entity_id="truth", name="truth", span=span),
|
||||
Entity(entity_id="concept", name="concept", span=span),
|
||||
),
|
||||
relations=(Relation(predicate="member", arguments=("truth", "concept"), span=span, negated=True),),
|
||||
)
|
||||
ctx = _ctx(vocab_persona)
|
||||
res = realize_comprehension(Comprehension(meaning_graph=graph, queries=()), ctx)
|
||||
assert isinstance(res, NotRealized) and res.reason == "negated_relation"
|
||||
assert len(ctx.vault._metadata) == 0
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Status firewall — SPECULATIVE is candidate memory, NOT evidence
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
|
|
|||
|
|
@ -9,14 +9,18 @@ versor — and reboot-stability rests on the Shape B+ snapshot of the exact byte
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import replace
|
||||
|
||||
import pytest
|
||||
|
||||
from algebra.versor import versor_condition
|
||||
from chat.runtime import ChatRuntime
|
||||
from generate.meaning_graph.reader import comprehend
|
||||
from generate.quantitative_comprehension import comprehend_quantitative
|
||||
from generate.realize import (
|
||||
NotRealized,
|
||||
Realized,
|
||||
realize_comprehension,
|
||||
realize_quantitative,
|
||||
recall_realized,
|
||||
)
|
||||
|
|
@ -158,3 +162,70 @@ def test_binding_graph_record_not_admitted_as_evidence(vocab_persona) -> None:
|
|||
query = ctx.vault._versors[res.record.vault_index]
|
||||
coherent = ctx.vault.recall(query, top_k=5, min_status=EpistemicStatus.COHERENT)
|
||||
assert not any(h["metadata"].get("kind") == "realized" for h in coherent)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# wrong=0 defense — an UNADMITTED equation never enters the held self
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
|
||||
def test_unadmitted_equation_realizes_nothing(vocab_persona) -> None:
|
||||
# The model permits a structurally-valid graph to carry a 'pending'/'refused'
|
||||
# equation. realize_quantitative RE-ASSERTS admitted-status, so it cannot be
|
||||
# slipped a dimensionally-incoherent equation by a non-reader constructor.
|
||||
ctx = _ctx(vocab_persona)
|
||||
good = comprehend_quantitative(_SUM) # has admitted equations
|
||||
pending = tuple(replace(e, admissibility_status="pending") for e in good.binding_graph.equations)
|
||||
tampered = replace(good, binding_graph=replace(good.binding_graph, equations=pending))
|
||||
res = realize_quantitative(tampered, ctx)
|
||||
assert isinstance(res, NotRealized) and res.reason == "unadmitted_equation"
|
||||
assert len(ctx.vault._metadata) == 0
|
||||
|
||||
|
||||
def test_not_a_quant_comprehension_realizes_nothing(vocab_persona) -> None:
|
||||
ctx = _ctx(vocab_persona)
|
||||
res = realize_quantitative(object(), ctx) # neither Refusal nor QuantComprehension
|
||||
assert isinstance(res, NotRealized) and res.reason == "not_a_quant_comprehension"
|
||||
assert len(ctx.vault._metadata) == 0
|
||||
|
||||
|
||||
def test_factless_binding_graph_realizes_nothing(vocab_persona) -> None:
|
||||
ctx = _ctx(vocab_persona)
|
||||
good = comprehend_quantitative(_FACT)
|
||||
factless = replace(good, binding_graph=replace(good.binding_graph, facts=()))
|
||||
res = realize_quantitative(factless, ctx)
|
||||
assert isinstance(res, NotRealized) and res.reason == "no_bound_fact"
|
||||
assert len(ctx.vault._metadata) == 0
|
||||
|
||||
|
||||
def test_grounding_failure_realizes_nothing(vocab_persona) -> None:
|
||||
# A degenerate placement raises inside probe_ingest; it must be caught -> no write.
|
||||
ctx = _ctx(vocab_persona)
|
||||
|
||||
def _boom(_tokens):
|
||||
raise RuntimeError("degenerate grounding")
|
||||
|
||||
ctx.probe_ingest = _boom # type: ignore[method-assign]
|
||||
res = realize_quantitative(comprehend_quantitative(_FACT), ctx)
|
||||
assert isinstance(res, NotRealized) and res.reason == "grounding_failed"
|
||||
assert len(ctx.vault._metadata) == 0
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Cross-substrate coexistence — meaning_graph + binding_graph in ONE vault
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
|
||||
def test_meaning_graph_and_binding_graph_coexist_and_recall_isolates(vocab_persona) -> None:
|
||||
ctx = _ctx(vocab_persona)
|
||||
realize_comprehension(comprehend("Truth is a concept."), ctx) # meaning_graph member
|
||||
_realize(_FACT, ctx) # binding_graph (alice)
|
||||
assert len(ctx.vault._metadata) == 2
|
||||
# structure_kind partitions; subject= (a meaning_graph notion) never matches a
|
||||
# binding_graph record (its relation_arguments is empty).
|
||||
mg = recall_realized(ctx, structure_kind="meaning_graph")
|
||||
bg = recall_realized(ctx, structure_kind="binding_graph")
|
||||
assert len(mg) == 1 and len(bg) == 1
|
||||
assert recall_realized(ctx, subject="truth") == mg
|
||||
assert recall_realized(ctx, subject="alice") == () # binding_graph not matched by subject=
|
||||
assert mg[0].structure_key != bg[0].structure_key # cross-kind collision-free
|
||||
|
|
|
|||
Loading…
Reference in a new issue