feat(reader): add overlaps_event finite-verb reader surface (B3) (#783)
Add a closed finite-verb relational surface to comprehend_relational, narrowly: declarative <A> overlaps <B>, interrogative Does <A> overlap <B>?, predicate overlaps_event ONLY (closed finite-verb table, default-off). Kept SEPARATE from the copula-connective grammar (byte-unchanged): the other connectives (before/after/during/inside/adjacent/…) still REQUIRE the copula, so "Monday before Friday." stays a refusal — no connective bypass (adversarial hazard #2). Fail-closed slot gate: each finite-verb argument slot must be EXACTLY ONE content token (after article stripping). An enumerated adverb/negation blocklist is unbounded and leaks — the adversarial audit found "Meeting never overlaps lunch." committed as a POSITIVE overlaps_event(meeting_never, lunch), plus almost/sometimes/trailing-qualifier/ second-verb compound fabrications in BOTH slots and the query path. The single-token gate closes the whole COMPOUND-fabrication class (any extra token refuses); the _FINITE_VERB_MODIFIERS and _CONNECTIVE_TOKENS checks add precise reasons for common bare-modifier / second-verb slots. Multi-word entities in the finite-verb surface are deferred (they need a positive content lexicon, which OOV entities preclude). A lone bare token remains an entity (the reader's universal OOV single-token contract — the copula and general readers behave identically), not a compound fabrication. Hazards pinned (adversarial audit hazard #1 + #2): adverb absorption, negation-as- positive ('never'), interrogative double-verb, trailing qualifier, and the copula-bypass firewall. Measurement: migrated ref-009 "Sunrise overlaps dawn." (the documented coverage gap) to a positive rel-018 — comprehension_relational_predicate 17->18, wrong_total 0, breadth still 11 (coverage added, not a new domain), baseline digest re-frozen. Refusal floor rose 9 -> 24 (finite-verb confusers replace the one migrated input). The #775 inference and #781 transitive lane SHA pins are UNTOUCHED. determine.py is untouched (reader-only): no answer=False, no FrameVerdict, INV-30 unaffected. Verified in the worktree: finite-verb + reader + reader-lane + #775 inference lane + #781 transitive lane + #779 OWA floor + capability baseline/index + INV-30/29/21 + full smoke — all green. Two read-only adversarial passes: the first found 4 real slot- fabrication blockers, closed by the single-token gate; a black-box probe of 24 adversarial inputs confirms the compound-fabrication class is fully closed.
This commit is contained in:
parent
01966ef92e
commit
0be18ebdb9
7 changed files with 366 additions and 22 deletions
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"capability_score": 0.953968,
|
"capability_score": 0.953968,
|
||||||
"coverage_geomean": 0.953968,
|
"coverage_geomean": 0.953968,
|
||||||
"coverage_micro": 0.993983,
|
"coverage_micro": 0.99399,
|
||||||
"accuracy_micro": 1.0,
|
"accuracy_micro": 1.0,
|
||||||
"breadth": 11,
|
"breadth": 11,
|
||||||
"min_domain_coverage": 0.833333,
|
"min_domain_coverage": 0.833333,
|
||||||
"wrong_total": 0,
|
"wrong_total": 0,
|
||||||
"assert_mode_valid": true,
|
"assert_mode_valid": true,
|
||||||
"deterministic_digest": "c064abc9d13780682632423756a7fab58c8f043ad752f6e136edd305f31ab8b1",
|
"deterministic_digest": "85c873626423c6b151a7d61bebb0cd5b7caff7dcc7e897188609e5ce3313feac",
|
||||||
"domains": [
|
"domains": [
|
||||||
{
|
{
|
||||||
"domain": "comprehension_propositional",
|
"domain": "comprehension_propositional",
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"domain": "comprehension_relational_predicate",
|
"domain": "comprehension_relational_predicate",
|
||||||
"correct": 17,
|
"correct": 18,
|
||||||
"wrong": 0,
|
"wrong": 0,
|
||||||
"refused": 0,
|
"refused": 0,
|
||||||
"coverage": 1.0,
|
"coverage": 1.0,
|
||||||
|
|
|
||||||
|
|
@ -15,3 +15,4 @@
|
||||||
{"id": "rel-015", "text": "Recess is during morning.", "predicate": "during_event", "subject": "recess", "object": "morning"}
|
{"id": "rel-015", "text": "Recess is during morning.", "predicate": "during_event", "subject": "recess", "object": "morning"}
|
||||||
{"id": "rel-016", "text": "Zorptak is the parent of Quxley.", "predicate": "parent_of", "subject": "zorptak", "object": "quxley"}
|
{"id": "rel-016", "text": "Zorptak is the parent of Quxley.", "predicate": "parent_of", "subject": "zorptak", "object": "quxley"}
|
||||||
{"id": "rel-017", "text": "Glarn is less than Brimble.", "predicate": "less_than", "subject": "glarn", "object": "brimble"}
|
{"id": "rel-017", "text": "Glarn is less than Brimble.", "predicate": "less_than", "subject": "glarn", "object": "brimble"}
|
||||||
|
{"id": "rel-018", "text": "Sunrise overlaps dawn.", "predicate": "overlaps_event", "subject": "sunrise", "object": "dawn"}
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,19 @@
|
||||||
{"id": "ref-006", "text": "Alice is not the parent of Bob.", "why": "negation is out of grammar — never reads as a positive"}
|
{"id": "ref-006", "text": "Alice is not the parent of Bob.", "why": "negation is out of grammar — never reads as a positive"}
|
||||||
{"id": "ref-007", "text": "Alice greets Bob.", "why": "no relational connective / no copula"}
|
{"id": "ref-007", "text": "Alice greets Bob.", "why": "no relational connective / no copula"}
|
||||||
{"id": "ref-008", "text": "The weather is nice today.", "why": "no relational template at all"}
|
{"id": "ref-008", "text": "The weather is nice today.", "why": "no relational template at all"}
|
||||||
{"id": "ref-009", "text": "Sunrise overlaps dawn.", "why": "verb-form relation, no copula — the honest overlaps_event coverage gap"}
|
{"id": "ref-010", "text": "Alice overlaps with the team and Bob.", "why": "finite-verb overlap with coordinated/leftover structure (with … and …) — must not fabricate a compound entity"}
|
||||||
|
{"id": "ref-011", "text": "A overlaps B and C.", "why": "coordinated object slot — 'and' is a parse leak, must refuse"}
|
||||||
|
{"id": "ref-012", "text": "A nearly overlaps B.", "why": "adverb-modified overlap — 'nearly' must not be absorbed into a fabricated entity a_nearly"}
|
||||||
|
{"id": "ref-013", "text": "A completely overlaps B.", "why": "adverb-modified overlap — 'completely' is a finite-verb modifier, must refuse"}
|
||||||
|
{"id": "ref-014", "text": "Does A nearly overlap B?", "why": "adverb-modified overlap in the query form — must refuse"}
|
||||||
|
{"id": "ref-015", "text": "A does not overlap B.", "why": "negation is out of grammar — never reads as a positive overlap"}
|
||||||
|
{"id": "ref-016", "text": "A borders B.", "why": "finite verb not in the closed table — only 'overlaps' is admitted"}
|
||||||
|
{"id": "ref-017", "text": "Monday before Friday.", "why": "connective without copula — admitting the finite verb must NOT let other connectives bypass the copula"}
|
||||||
|
{"id": "ref-018", "text": "The cat inside the house.", "why": "connective without copula — inside_of still requires the copula"}
|
||||||
|
{"id": "ref-019", "text": "The room adjacent the hall.", "why": "connective without copula — adjacent_to still requires the copula"}
|
||||||
|
{"id": "ref-020", "text": "A overlaps B overlaps C.", "why": "a second finite verb — leftover relational structure, must refuse"}
|
||||||
|
{"id": "ref-021", "text": "A overlaps B then C.", "why": "sequencing modifier 'then' must not be absorbed into a fabricated entity b_then_c"}
|
||||||
|
{"id": "ref-022", "text": "Meeting never overlaps lunch.", "why": "negation 'never' (NOT a reserved word) must not be glued into meeting_never and committed as a POSITIVE overlaps_event — the worst wrong=0 breach the audit found"}
|
||||||
|
{"id": "ref-023", "text": "Alice almost overlaps Bob.", "why": "an adverb outside the enumerated modifier set — the single-token slot gate must refuse, not fabricate alice_almost"}
|
||||||
|
{"id": "ref-024", "text": "Meeting overlaps lunch today.", "why": "trailing temporal qualifier must not be glued into lunch_today — a 2-token slot refuses"}
|
||||||
|
{"id": "ref-025", "text": "Does A overlap B overlap C?", "why": "interrogative double-verb — the base form 'overlap' is not in the connective net; the single-token slot gate refuses b_overlap_c"}
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,14 @@ Transitive (direct + through-inverse), asymmetric self-converse (`less_than`,
|
||||||
`equal_to`), object mismatch, and ungrounded. A `Determined` on any of these is a
|
`equal_to`), object mismatch, and ungrounded. A `Determined` on any of these is a
|
||||||
wrong=0 breach — the lane test fails loudly.
|
wrong=0 breach — the lane test fails loudly.
|
||||||
|
|
||||||
## Known coverage gap (honest)
|
## Resolved coverage gap (B3)
|
||||||
|
|
||||||
`overlaps_event` (symmetric, pack-declared `graph.edge.symmetric`) is **excluded** from
|
`overlaps_event` (symmetric, pack-declared `graph.edge.symmetric`) was previously
|
||||||
the positive gold: the relational reader requires a copula ("X is _connective_ Y"), but
|
**excluded** from the positive gold: the relational reader required a copula
|
||||||
`overlaps` is a finite verb — "The meeting overlaps the lunch" refuses
|
("X is _connective_ Y"), but `overlaps` is a finite verb. The closed finite-verb reader
|
||||||
(`no_relational_template`) and "is overlaps" is ungrammatical. This is a **reader
|
surface (B3 — `<A> overlaps <B>` / `Does <A> overlap <B>?`, see
|
||||||
surface limitation, not a determination gap** (the symmetric rule would fire if a fact
|
`generate/meaning_graph/relational.py::_read_finite_verb_clause`) now reads it, so a
|
||||||
were stored). A finite-verb relational surface is brief-pack follow-up work.
|
stored overlap and its symmetric converse determine (proven in
|
||||||
|
`tests/test_relational_finite_verb.py`). The reader-surface coverage lives in
|
||||||
|
`evals/relational`; adding a symmetric `overlaps_event` case to THIS inference lane is
|
||||||
|
optional follow-up.
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,18 @@ relational set for DIRECT entailment.
|
||||||
|
|
||||||
Fail-closed (wrong=0 at the comprehension layer):
|
Fail-closed (wrong=0 at the comprehension layer):
|
||||||
|
|
||||||
- a clause is read ONLY when it matches ``<A> is [the] <connective> <B>`` with the
|
- a clause is read ONLY by one of two CLOSED surfaces: the copula-connective grammar
|
||||||
copula sitting STRUCTURALLY adjacent to the connective, AND the connective's lemma
|
``<A> is [the] <connective> <B>`` (the copula sitting STRUCTURALLY adjacent to the
|
||||||
is present in the LOADED pack (``pack_lemmas``), AND neither argument slot carries
|
connective), OR the closed finite-verb surface ``<A> overlaps <B>`` /
|
||||||
leftover relational/reserved vocabulary. A non-matching surface, a dangling copula,
|
``Does <A> overlap <B>?`` (no copula; ``overlaps_event`` ONLY — see
|
||||||
a negated form, an argument slot holding a connective token (a trailing qualifier
|
``_read_finite_verb_clause``). In BOTH the lemma must be present in the LOADED pack
|
||||||
like "… of Dan during school" — unparsed structure, NOT an entity), or a lemma
|
(``pack_lemmas``) AND neither argument slot may carry leftover relational/reserved
|
||||||
absent from the pack all REFUSE — never guess, never field-vote. Deterministic.
|
vocabulary or an adverb modifier. A non-matching surface, a dangling copula, a
|
||||||
|
connective WITHOUT its copula ("Monday before Friday."), a negated form, an
|
||||||
|
adverb-modified overlap ("A nearly overlaps B"), an argument slot holding a connective
|
||||||
|
token (a trailing qualifier like "… of Dan during school" — unparsed structure, NOT an
|
||||||
|
entity), or a lemma absent from the pack all REFUSE — never guess, never field-vote.
|
||||||
|
Deterministic.
|
||||||
- only the PREDICATE is closed-vocabulary; the two arguments may be OOV (arbitrary
|
- only the PREDICATE is closed-vocabulary; the two arguments may be OOV (arbitrary
|
||||||
identifiers), grounded downstream by the OOV substrate. A clean multi-word entity
|
identifiers), grounded downstream by the OOV substrate. A clean multi-word entity
|
||||||
("north station") canonicalizes per the join contract; an argument that still holds
|
("north station") canonicalizes per the join contract; an argument that still holds
|
||||||
|
|
@ -174,6 +179,32 @@ _CONNECTIVE_TOKENS = frozenset(tok for key in _CONNECTIVE_TO_LEMMA for tok in ke
|
||||||
|
|
||||||
_MAX_CONNECTIVE_LEN = max(len(k) for k in _CONNECTIVE_TO_LEMMA)
|
_MAX_CONNECTIVE_LEN = max(len(k) for k in _CONNECTIVE_TO_LEMMA)
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Finite-verb surface (B3) — a verb-form relation with NO copula. CLOSED and
|
||||||
|
# default-off, kept SEPARATE from the copula-connective grammar above: the ONLY
|
||||||
|
# finite verb admitted is ``overlaps`` (``overlaps_event``). The other connectives
|
||||||
|
# (before / after / during / inside / adjacent …) are UNCHANGED — they still REQUIRE
|
||||||
|
# the copula, so "Monday before Friday." stays a refusal (no connective bypass).
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
#: Declarative finite-verb token: ``<A> overlaps <B>``.
|
||||||
|
_FINITE_VERB_DECLARATIVE = "overlaps"
|
||||||
|
#: Interrogative finite-verb token (base form, with the ``does`` auxiliary):
|
||||||
|
#: ``Does <A> overlap <B>?``.
|
||||||
|
_FINITE_VERB_INTERROGATIVE = "overlap"
|
||||||
|
#: The pack lemma the finite-verb surface mints. Already in ``RELATIONAL_PREDICATES`` via
|
||||||
|
#: the ``("overlaps",)`` connective entry, so the predicate universe is UNCHANGED.
|
||||||
|
_FINITE_VERB_LEMMA = "overlaps_event"
|
||||||
|
|
||||||
|
#: Adverb / sequencing tokens that must NEVER be absorbed into a finite-verb argument
|
||||||
|
#: slot. A finite-verb GUARD, not a general grammar: an adverb-modified or sequenced
|
||||||
|
#: overlap REFUSES rather than fabricate an entity (``a_nearly``, ``b_then_c``). These are
|
||||||
|
#: NOT in ``reader._RESERVED``, so ``_chunk`` would otherwise silently absorb them — the
|
||||||
|
#: exact wrong=0 hazard the adversarial audit flagged.
|
||||||
|
_FINITE_VERB_MODIFIERS = frozenset(
|
||||||
|
{"nearly", "completely", "partially", "mostly", "barely", "then"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def load_relational_pack_lemmas() -> frozenset[str]:
|
def load_relational_pack_lemmas() -> frozenset[str]:
|
||||||
"""The lemma set of the loaded relational-predicates pack — the fail-closed
|
"""The lemma set of the loaded relational-predicates pack — the fail-closed
|
||||||
|
|
@ -264,6 +295,88 @@ def comprehend_relational(
|
||||||
return Comprehension(meaning_graph=graph, queries=tuple(queries))
|
return Comprehension(meaning_graph=graph, queries=tuple(queries))
|
||||||
|
|
||||||
|
|
||||||
|
def _read_finite_verb_clause(
|
||||||
|
toks: list[str],
|
||||||
|
question: bool,
|
||||||
|
pack_lemmas: frozenset[str],
|
||||||
|
span: MeaningSpan,
|
||||||
|
claim,
|
||||||
|
relations: list[tuple[str, tuple[str, ...], MeaningSpan, bool]],
|
||||||
|
queries: list[Query],
|
||||||
|
) -> bool:
|
||||||
|
"""Read the closed finite-verb surface (``overlaps_event`` ONLY), or return ``False``
|
||||||
|
when the clause is NOT a finite-verb form (the caller then tries the copula grammar).
|
||||||
|
|
||||||
|
Two surfaces: declarative ``<A> overlaps <B>`` and interrogative
|
||||||
|
``Does <A> overlap <B>?`` (base form + the ``does`` auxiliary). Once a clause IS
|
||||||
|
recognized as a finite-verb form it EMITS or REFUSES — it never falls through, so a
|
||||||
|
malformed overlap cannot reach the copula grammar. Fail-closed: emits only when the
|
||||||
|
lemma is in the loaded pack AND each argument slot is, after article stripping, EXACTLY
|
||||||
|
ONE content token. That single-token gate is the load-bearing backstop — it closes the
|
||||||
|
UNBOUNDED adverb / negation / trailing-qualifier / second-verb class a blocklist
|
||||||
|
cannot (so ``meeting never overlaps lunch`` does NOT become a positive
|
||||||
|
``overlaps_event(meeting_never, lunch)``). The ``_FINITE_VERB_MODIFIERS`` and
|
||||||
|
``_CONNECTIVE_TOKENS`` checks add precise refusal reasons for the common bare-modifier
|
||||||
|
/ second-verb slots; ``_chunk`` then validates the token. Scoped to ``overlaps`` only,
|
||||||
|
so the other connectives keep requiring the copula.
|
||||||
|
"""
|
||||||
|
if question:
|
||||||
|
# interrogative ``does <A> overlap <B>`` (the '?' terminator is already stripped).
|
||||||
|
if not toks or toks[0] != "does" or _FINITE_VERB_INTERROGATIVE not in toks[1:]:
|
||||||
|
return False # not the finite-verb query form — fall through
|
||||||
|
verb_idx = toks.index(_FINITE_VERB_INTERROGATIVE, 1)
|
||||||
|
left, right = toks[1:verb_idx], toks[verb_idx + 1 :]
|
||||||
|
else:
|
||||||
|
# declarative ``<A> overlaps <B>``.
|
||||||
|
if _FINITE_VERB_DECLARATIVE not in toks:
|
||||||
|
return False # not the finite-verb declarative form — fall through
|
||||||
|
verb_idx = toks.index(_FINITE_VERB_DECLARATIVE)
|
||||||
|
left, right = toks[:verb_idx], toks[verb_idx + 1 :]
|
||||||
|
|
||||||
|
# From here the clause IS a finite-verb form — emit or REFUSE, never fall through.
|
||||||
|
if _FINITE_VERB_LEMMA not in pack_lemmas:
|
||||||
|
raise _Reject("relational_lemma_not_in_pack", _FINITE_VERB_LEMMA)
|
||||||
|
|
||||||
|
subject_toks = _strip_article_edges(left)
|
||||||
|
object_toks = _strip_article_edges(right)
|
||||||
|
if not subject_toks or not object_toks:
|
||||||
|
raise _Reject("incomplete_relation", " ".join(toks))
|
||||||
|
|
||||||
|
slot_toks = (*subject_toks, *object_toks)
|
||||||
|
# adverb / sequencing guard — a precise refusal for the common modifiers when one is a
|
||||||
|
# BARE slot ("Nearly overlaps dawn."); these are NOT in reader._RESERVED.
|
||||||
|
if any(t in _FINITE_VERB_MODIFIERS for t in slot_toks):
|
||||||
|
raise _Reject("finite_verb_modifier", " ".join(toks))
|
||||||
|
# leftover relational structure (a second finite verb / any connective token), reusing
|
||||||
|
# the copula path's fabrication net: "A overlaps B overlaps C", trailing qualifiers.
|
||||||
|
if any(t in _CONNECTIVE_TOKENS for t in slot_toks):
|
||||||
|
raise _Reject("extra_relational_structure", " ".join(toks))
|
||||||
|
|
||||||
|
# POSITIVE fail-closed slot gate — the backstop a blocklist cannot provide. Each
|
||||||
|
# finite-verb slot must be EXACTLY ONE content token (after article stripping). An
|
||||||
|
# enumerated adverb/negation list is UNBOUNDED and leaks ("almost", "sometimes",
|
||||||
|
# "never", a trailing qualifier "lunch today", a second verb "overlap"): the
|
||||||
|
# adversarial audit found each glued silently into a fabricated id (e.g.
|
||||||
|
# ``overlaps_event(meeting_never, lunch)`` — a negated sentence committed as a POSITIVE
|
||||||
|
# belief). Requiring a single token closes the whole class — any extra token leaves a
|
||||||
|
# 2+-token slot and REFUSES. Multi-word entities in the finite-verb surface are
|
||||||
|
# deferred (they need a positive content lexicon, which OOV entities preclude).
|
||||||
|
if len(subject_toks) != 1 or len(object_toks) != 1:
|
||||||
|
raise _Reject("finite_verb_unclean_slot", " ".join(toks))
|
||||||
|
|
||||||
|
# _chunk canonicalizes and validates the single token (rejects a non-identifier).
|
||||||
|
subject = _chunk(subject_toks, " ".join(toks))
|
||||||
|
obj = _chunk(object_toks, " ".join(toks))
|
||||||
|
claim(subject, span)
|
||||||
|
claim(obj, span)
|
||||||
|
|
||||||
|
if question:
|
||||||
|
queries.append(Query(_FINITE_VERB_LEMMA, (subject, obj), span))
|
||||||
|
else:
|
||||||
|
relations.append((_FINITE_VERB_LEMMA, (subject, obj), span, False))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _read_relational_clause(
|
def _read_relational_clause(
|
||||||
clause: str,
|
clause: str,
|
||||||
question: bool,
|
question: bool,
|
||||||
|
|
@ -273,11 +386,22 @@ def _read_relational_clause(
|
||||||
relations: list[tuple[str, tuple[str, ...], MeaningSpan, bool]],
|
relations: list[tuple[str, tuple[str, ...], MeaningSpan, bool]],
|
||||||
queries: list[Query],
|
queries: list[Query],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Read one ``<A> is [the] <connective> <B>`` clause; mutate accumulators or REFUSE."""
|
"""Read one clause into accumulators or REFUSE. Two SEPARATE branches: the closed
|
||||||
|
finite-verb surface (``<A> overlaps <B>`` / ``Does <A> overlap <B>?``) is tried first;
|
||||||
|
everything else falls to the copula-connective grammar (``<A> is [the] <connective>
|
||||||
|
<B>``), which is unchanged — the other connectives still require the copula."""
|
||||||
toks = clause.strip().lower().split()
|
toks = clause.strip().lower().split()
|
||||||
if not toks:
|
if not toks:
|
||||||
raise _Reject("empty_clause", clause)
|
raise _Reject("empty_clause", clause)
|
||||||
|
|
||||||
|
# B3 finite-verb branch (closed: overlaps_event only). Handles the no-copula
|
||||||
|
# finite-verb surfaces with strict fail-closed guards, and returns False (fall
|
||||||
|
# through to the copula grammar below) ONLY when the clause is not a finite-verb form.
|
||||||
|
if _read_finite_verb_clause(
|
||||||
|
toks, question, pack_lemmas, span, claim, relations, queries
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
split = _split_around_connective(toks)
|
split = _split_around_connective(toks)
|
||||||
if split is None:
|
if split is None:
|
||||||
raise _Reject("no_relational_template", clause)
|
raise _Reject("no_relational_template", clause)
|
||||||
|
|
|
||||||
196
tests/test_relational_finite_verb.py
Normal file
196
tests/test_relational_finite_verb.py
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
"""Relational reader — the closed finite-verb surface (B3): ``overlaps_event``.
|
||||||
|
|
||||||
|
The reader admits ONE finite verb with NO copula: ``<A> overlaps <B>`` (declarative) and
|
||||||
|
``Does <A> overlap <B>?`` (query). Everything else still requires the copula. Fail-closed:
|
||||||
|
an adverb-modified overlap, a coordinated/leftover slot, a second verb, a negation, a
|
||||||
|
non-table verb, or a bare connective-without-copula all REFUSE.
|
||||||
|
|
||||||
|
Two adversarial-audit hazards are pinned here:
|
||||||
|
1. adverb absorption — ``A nearly overlaps B`` must NOT fabricate the entity ``a_nearly``;
|
||||||
|
2. copula bypass — admitting the finite verb must NOT let the OTHER connectives drop the
|
||||||
|
copula: ``Monday before Friday.`` must still refuse.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from chat.runtime import ChatRuntime
|
||||||
|
from generate.determine import Determined, Undetermined, determine
|
||||||
|
from generate.meaning_graph.reader import Comprehension, Refusal
|
||||||
|
from generate.meaning_graph.relational import (
|
||||||
|
comprehend_relational,
|
||||||
|
load_relational_pack_lemmas,
|
||||||
|
)
|
||||||
|
from generate.realize import NotRealized, realize_comprehension
|
||||||
|
from session.context import SessionContext
|
||||||
|
|
||||||
|
_HIGH = 10**9
|
||||||
|
_PACK = load_relational_pack_lemmas()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def vocab_persona():
|
||||||
|
rt = ChatRuntime(no_load_state=True)
|
||||||
|
return rt._context.vocab, rt._context.persona
|
||||||
|
|
||||||
|
|
||||||
|
def _ctx(vocab_persona) -> SessionContext:
|
||||||
|
vocab, persona = vocab_persona
|
||||||
|
return SessionContext(vocab=vocab, persona=persona, vault_reproject_interval=_HIGH)
|
||||||
|
|
||||||
|
|
||||||
|
def _read(text: str):
|
||||||
|
return comprehend_relational(text, _PACK)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Positives — the finite-verb declarative + interrogative read overlaps_event
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
def test_declarative_overlaps_reads() -> None:
|
||||||
|
comp = _read("Sunrise overlaps dawn.")
|
||||||
|
assert isinstance(comp, Comprehension), comp
|
||||||
|
assert not comp.queries and len(comp.meaning_graph.relations) == 1
|
||||||
|
rel = comp.meaning_graph.relations[0]
|
||||||
|
assert rel.predicate == "overlaps_event"
|
||||||
|
assert rel.arguments == ("sunrise", "dawn")
|
||||||
|
assert rel.negated is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_interrogative_does_overlap_reads_a_query() -> None:
|
||||||
|
comp = _read("Does Sunrise overlap dawn?")
|
||||||
|
assert isinstance(comp, Comprehension), comp
|
||||||
|
assert not comp.meaning_graph.relations and len(comp.queries) == 1
|
||||||
|
q = comp.queries[0]
|
||||||
|
assert q.predicate == "overlaps_event" and q.arguments == ("sunrise", "dawn")
|
||||||
|
assert not q.negated
|
||||||
|
|
||||||
|
|
||||||
|
def test_oov_arguments_in_finite_verb_surface() -> None:
|
||||||
|
rel = _read("Zorptak overlaps Quxley.").meaning_graph.relations[0]
|
||||||
|
assert rel.predicate == "overlaps_event" and rel.arguments == ("zorptak", "quxley")
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_then_determine_direct_and_symmetric(vocab_persona) -> None:
|
||||||
|
# the finite-verb read integrates with determine: direct, and the symmetric converse
|
||||||
|
# (overlaps_event is pack-declared symmetric) determines via the one-hop rule.
|
||||||
|
ctx = _ctx(vocab_persona)
|
||||||
|
realize_comprehension(_read("Sunrise overlaps dawn."), ctx)
|
||||||
|
direct = determine(_read("Does Sunrise overlap dawn?"), ctx)
|
||||||
|
assert isinstance(direct, Determined) and direct.answer is True and direct.rule == "direct"
|
||||||
|
converse = determine(_read("Does dawn overlap Sunrise?"), ctx)
|
||||||
|
assert isinstance(converse, Determined) and converse.answer is True
|
||||||
|
assert converse.rule == "symmetric"
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Hazard 1 (pinned) — adverb absorption must NOT fabricate an entity
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
def test_adverb_modified_overlap_refuses_no_fabrication(vocab_persona) -> None:
|
||||||
|
res = _read("Alice nearly overlaps Bob.")
|
||||||
|
assert isinstance(res, Refusal) and res.reason == "finite_verb_modifier"
|
||||||
|
# and nothing fabricated is realized — no `alice_nearly` enters memory.
|
||||||
|
ctx = _ctx(vocab_persona)
|
||||||
|
told = realize_comprehension(_read("Alice nearly overlaps Bob."), ctx)
|
||||||
|
assert isinstance(told, NotRealized)
|
||||||
|
assert isinstance(determine(_read("Does alice_nearly overlap Bob?"), ctx), Undetermined)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Hazard 2 (pinned) — the finite verb must NOT let other connectives bypass copula
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"text",
|
||||||
|
[
|
||||||
|
"Monday before Friday.",
|
||||||
|
"The cat inside the house.",
|
||||||
|
"The room adjacent the hall.",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_connective_without_copula_still_refuses(text) -> None:
|
||||||
|
res = _read(text)
|
||||||
|
assert isinstance(res, Refusal) and res.reason == "no_relational_template"
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Mandatory confusers — every malformed finite-verb / non-finite-verb surface refuses
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"text",
|
||||||
|
[
|
||||||
|
"Alice overlaps with the team and Bob.", # coordinated/leftover structure
|
||||||
|
"A overlaps B and C.", # coordination
|
||||||
|
"A nearly overlaps B.", # adverb
|
||||||
|
"A completely overlaps B.", # adverb
|
||||||
|
"Does A nearly overlap B?", # adverb in the query
|
||||||
|
"A does not overlap B.", # negation
|
||||||
|
"A borders B.", # finite verb not in the closed table
|
||||||
|
"A overlaps B overlaps C.", # a second finite verb
|
||||||
|
"A overlaps B then C.", # sequencing modifier
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_finite_verb_confusers_refuse(text) -> None:
|
||||||
|
res = _read(text)
|
||||||
|
assert isinstance(res, Refusal), f"{text!r} should refuse but committed {res!r}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_finite_verb_confuser_fabricates_a_relation() -> None:
|
||||||
|
# stronger than "is a Refusal": not a single relation/query is produced for any of them.
|
||||||
|
for text in (
|
||||||
|
"Alice overlaps with the team and Bob.",
|
||||||
|
"A nearly overlaps B.",
|
||||||
|
"A overlaps B overlaps C.",
|
||||||
|
"A overlaps B then C.",
|
||||||
|
"A does not overlap B.",
|
||||||
|
):
|
||||||
|
comp = _read(text)
|
||||||
|
assert isinstance(comp, Refusal), text
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Slot-fabrication hazards (adversarial audit) — the single-token slot gate closes
|
||||||
|
# the UNBOUNDED adverb / negation / qualifier / second-verb class a blocklist cannot.
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"text",
|
||||||
|
[
|
||||||
|
"Alice almost overlaps Bob.", # adverb NOT in the enumerated modifier set
|
||||||
|
"Meeting sometimes overlaps lunch.", # another non-enumerated adverb
|
||||||
|
"Sprint overlaps almost lunch.", # right-slot adverb
|
||||||
|
"Sprint overlaps lunch almost.", # trailing right-slot adverb
|
||||||
|
"Meeting overlaps lunch today.", # trailing temporal qualifier glued into the id
|
||||||
|
"North station overlaps south station.", # multi-word entities deferred (narrow v1)
|
||||||
|
"Does A overlap B overlap C?", # interrogative double-verb (base form not in net)
|
||||||
|
"Does Sprint overlap lunch now?", # interrogative trailing adverb
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_unclean_finite_verb_slots_refuse(text) -> None:
|
||||||
|
# any slot that is not exactly one content token refuses rather than glue extra tokens
|
||||||
|
# into a fabricated id — this is the backstop the enumerated blocklists cannot provide.
|
||||||
|
res = _read(text)
|
||||||
|
assert isinstance(res, Refusal), f"{text!r} should refuse but committed {res!r}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_never_negation_is_not_committed_as_positive(vocab_persona) -> None:
|
||||||
|
# THE worst class the audit found: 'never' is not a reserved word, so without the
|
||||||
|
# single-token gate "Meeting never overlaps lunch." was committed as a POSITIVE
|
||||||
|
# overlaps_event(meeting_never, lunch). It must refuse and hold no positive belief.
|
||||||
|
res = _read("Meeting never overlaps lunch.")
|
||||||
|
assert isinstance(res, Refusal)
|
||||||
|
ctx = _ctx(vocab_persona)
|
||||||
|
told = realize_comprehension(_read("Meeting never overlaps lunch."), ctx)
|
||||||
|
assert isinstance(told, NotRealized)
|
||||||
|
assert isinstance(determine(_read("Does meeting overlap lunch?"), ctx), Undetermined)
|
||||||
|
assert isinstance(
|
||||||
|
determine(_read("Does meeting_never overlap lunch?"), ctx), Undetermined
|
||||||
|
)
|
||||||
|
|
@ -45,14 +45,14 @@ def test_positive_lane_is_green() -> None:
|
||||||
report = run()
|
report = run()
|
||||||
assert report["wrong"] == 0
|
assert report["wrong"] == 0
|
||||||
assert report["refused"] == 0 # every clean case reads
|
assert report["refused"] == 0 # every clean case reads
|
||||||
assert report["correct"] == report["total"] == 17
|
assert report["correct"] == report["total"] == 18 # +1: overlaps_event finite-verb (B3)
|
||||||
|
|
||||||
|
|
||||||
def test_lane_is_composed_into_capability_index() -> None:
|
def test_lane_is_composed_into_capability_index() -> None:
|
||||||
results = {d.domain: d for d in collect_domain_results().results}
|
results = {d.domain: d for d in collect_domain_results().results}
|
||||||
dom = results.get("comprehension_relational_predicate")
|
dom = results.get("comprehension_relational_predicate")
|
||||||
assert dom is not None, "relational-predicate domain missing from the index"
|
assert dom is not None, "relational-predicate domain missing from the index"
|
||||||
assert dom.wrong == 0 and dom.correct == 17 and dom.coverage == 1.0
|
assert dom.wrong == 0 and dom.correct == 18 and dom.coverage == 1.0
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
@ -63,7 +63,12 @@ def test_lane_is_composed_into_capability_index() -> None:
|
||||||
def test_adversarial_inputs_all_refuse() -> None:
|
def test_adversarial_inputs_all_refuse() -> None:
|
||||||
pack = load_relational_pack_lemmas()
|
pack = load_relational_pack_lemmas()
|
||||||
cases = _refusal_cases()
|
cases = _refusal_cases()
|
||||||
assert len(cases) >= 9 # the #596 hazard family + negation + non-template + the verb gap
|
# the #596 fabrication family + negation + non-template + the B3 finite-verb confuser
|
||||||
|
# family (adverb absorption incl. non-enumerated 'almost'/'never', coordination,
|
||||||
|
# second-verb, trailing qualifier, connective-without-copula). The overlaps verb-gap
|
||||||
|
# (old ref-009) MIGRATED to a positive, replaced by finite-verb confusers — the refusal
|
||||||
|
# floor RISES, never drops.
|
||||||
|
assert len(cases) >= 24
|
||||||
for case in cases:
|
for case in cases:
|
||||||
comp = comprehend_relational(case["text"], pack)
|
comp = comprehend_relational(case["text"], pack)
|
||||||
assert isinstance(comp, Refusal), (
|
assert isinstance(comp, Refusal), (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue