arborist/tests/test_qa_quality_live.py
russell@unturf.com 8d6961fcc1
aborist/arborist
modified:   .gitlab-ci.yml
	modified:   bench/qa_questions.txt
	modified:   bench/qa_sweep.py
	modified:   bench/run.sh
	modified:   docs/TICKETS.md
	modified:   docs/_source/README.md
	modified:   docs/_source/_ext/makefile_targets.py
	modified:   docs/_source/api/cli.rst
	modified:   docs/_source/api/distill.rst
	modified:   docs/_source/api/mesh.rst
	modified:   docs/_source/api/qa.rst
	modified:   docs/_source/api/retrieval.rst
	modified:   docs/_source/api/storage.rst
	modified:   docs/_source/api/substrate.rst
	modified:   docs/_source/concepts.rst
	modified:   docs/_source/conf.py
	modified:   docs/_source/cookbook.rst
	modified:   docs/_source/index.rst
	modified:   docs/_source/license.rst
	modified:   docs/_source/quickstart.rst
	modified:   docs/bench-maxing.md
	modified:   docs/benchmarks.md
	modified:   docs/cti-architecture.md
	modified:   docs/diagrams/aborist-modules.dot
	modified:   docs/diagrams/aborist-modules.svg
	modified:   docs/diagrams/mesh-data-flow.dot
	modified:   docs/diagrams/mesh-epoch-lifecycle.dot
	modified:   docs/diagrams/mesh-epoch-lifecycle.svg
	modified:   docs/diagrams/mesh-group-decisions.dot
	modified:   docs/diagrams/mesh-group-decisions.svg
	modified:   docs/diagrams/mesh-identity-stack.dot
	modified:   docs/diagrams/mesh-secret-envelope.dot
	modified:   docs/mesh.md
	modified:   docs/qa-modes-bench.md
	modified:   docs/seven-point-program.md
	modified:   docs/tickets/ticket-000001-retrieval-keywords-audit-gap.md
	modified:   docs/tickets/ticket-000002-reference-frame-polarity-contract.md
	modified:   docs/tickets/ticket-000003-anchor-class-warrant.md
	modified:   docs/tickets/ticket-000005-label-ladder-migration.md
	modified:   docs/tickets/ticket-000006-bench-emergent-findings.md
	modified:   docs/tickets/ticket-000007-query-layer-hyphen-fold.md
	modified:   docs/tickets/ticket-000008-broad-quantifier-preflight-guard.md
	modified:   docs/tickets/ticket-000009-quantifier-preflight-dag-binding.md
	modified:   docs/tickets/ticket-000010-metacognition-preflight-guard.md
	modified:   docs/tickets/ticket-000011-soft-preflight-hint-sidecar.md
	modified:   scripts/backfill_concepts.py
	modified:   scripts/bench_emergent.py
	modified:   tests/crawler/test_async_web_fetcher.py
	modified:   tests/crawler/test_bridge.py
	modified:   tests/crawler/test_web_fetch.py
	modified:   tests/test_bench_qa_sweep.py
	modified:   tests/test_burn.py
	modified:   tests/test_burn_doc.py
	modified:   tests/test_claim_lattice.py
	modified:   tests/test_cli_render.py
	modified:   tests/test_compress.py
	modified:   tests/test_concepts.py
	modified:   tests/test_dag.py
	modified:   tests/test_directives.py
	modified:   tests/test_distill.py
	modified:   tests/test_distill_recursive.py
	modified:   tests/test_evict.py
	modified:   tests/test_frame.py
	modified:   tests/test_grok_source.py
	modified:   tests/test_html_source.py
	modified:   tests/test_ingest.py
	modified:   tests/test_inspect.py
	modified:   tests/test_journal.py
	modified:   tests/test_keys.py
	modified:   tests/test_llm_context_base.py
	modified:   tests/test_merkle.py
	modified:   tests/test_mesh.py
	modified:   tests/test_mesh_aead.py
	modified:   tests/test_mesh_chain.py
	modified:   tests/test_mesh_cli.py
	modified:   tests/test_mesh_cli_pull.py
	modified:   tests/test_mesh_wire.py
	modified:   tests/test_mesh_wire_e2e.py
	modified:   tests/test_metacognition.py
	modified:   tests/test_migration_audit_mode.py
	modified:   tests/test_providence_source.py
	modified:   tests/test_qa.py
	modified:   tests/test_qa_quality_live.py
	modified:   tests/test_quantifier_caps.py
	modified:   tests/test_quantifier_classifier.py
	modified:   tests/test_quantifier_phase4.py
	modified:   tests/test_quantifier_reminder.py
	modified:   tests/test_query.py
	modified:   tests/test_reclassify.py
	modified:   tests/test_repair.py
	modified:   tests/test_resume.py
	modified:   tests/test_snapshot.py
	modified:   tests/test_soft_preflight.py
	modified:   tests/test_tfidf.py
	modified:   tests/test_vcs_source.py
	modified:   tests/test_verify.py
	modified:   tests/test_verify_json.py
	modified:   tests/test_versioned_ingest.py
	modified:   tests/test_warrant.py
	modified:   tests/test_wikipedia_old.py
	modified:   tests/test_wikipedia_xml.py
	modified:   tests/test_wikitext.py
2026-05-07 09:31:49 -04:00

829 lines
35 KiB
Python

"""Live QA-quality tests against the real Hermes endpoint.
These tests are deliberately gated — they hit
``hermes.ai.unturf.com/v1`` and require a populated shard set under
``~/.arborist/shards`` (default) or the path passed via
``ARBORIST_LIVE_SHARDS_DIR``. Default `make test` does NOT run these;
opt in via ``ARBORIST_LIVE_TESTS=1`` (the ``make test-live`` target
sets it).
Why this exists:
The unit tests in ``test_qa.py`` / ``test_query.py`` use ``StubClient``
to validate plumbing (cache lookups, document mapping, schema, audit
chain). They cannot tell you if the system answers ``"who founded
Apple Computer?"`` correctly — that's a model+retrieval+verifier
behavior, not a unit-tested code path. The QA-quality bench
(``make bench-qa``) measures aggregate verdict counts but doesn't
assert specific content. These live fixtures sit between those two
concerns: each test runs ONE known-good question, asserts the
audit_mode is at least HYBRID, and checks for entity-level content
the answer should mention.
"Benchmax" rationale: the bench gives us a scoreboard
(STRICT/HYBRID/UNGROUNDED counts); these tests give us *gates*. When
a future change improves the system, the strict-rate climbs in the
bench AND every fixture here keeps passing or gets stricter
assertions. When a change regresses, the bench number falls AND
specific fixtures fail by name, telling you where the regression
landed.
Every assertion accepts a tolerance band — Hermes is non-deterministic
at temperature > 0, so single-sample tests are inherently flaky on
boundary cases. The bar is "the right entity is present in the
answer", not "the answer is byte-identical to last run". When a
fixture starts failing intermittently, treat it as evidence of
quality drift on that question shape and tighten retrieval / verifier
upstream rather than loosen the test.
"""
from __future__ import annotations
import os
from pathlib import Path
import pytest
_LIVE_OPT_IN = os.environ.get("ARBORIST_LIVE_TESTS") == "1"
_SHARDS_DIR = Path(
os.environ.get("ARBORIST_LIVE_SHARDS_DIR")
or (Path.home() / ".arborist" / "shards")
)
_QA_DB = _SHARDS_DIR / "qa.db"
_ENDPOINT = os.environ.get(
"ARBORIST_LLM_ENDPOINT", "https://hermes.ai.unturf.com/v1"
)
_MODEL = os.environ.get(
"ARBORIST_LLM_MODEL",
"adamo1139/Hermes-3-Llama-3.1-8B-FP8-Dynamic",
)
pytestmark = pytest.mark.skipif(
not _LIVE_OPT_IN,
reason="live QA tests gated by ARBORIST_LIVE_TESTS=1; "
"run via `make test-live` or set the env var",
)
def _ask(question: str, *, mode: str = "claim_lattice", top_k: int = 8) -> dict:
"""One live query against the configured shard set + Hermes endpoint."""
if not _SHARDS_DIR.exists():
pytest.skip(f"shards dir not found: {_SHARDS_DIR}")
if not _QA_DB.parent.exists():
pytest.skip(f"qa.db parent not found: {_QA_DB.parent}")
# Lazy import so collecting the file doesn't pull qa modules in
# default-skip mode.
from arborist.qa.client import OpenAICompatibleClient
from arborist.qa.query import DEFAULT_QUERY_POLICY, query
api_key = os.environ.get("ARBORIST_LLM_API_KEY")
client = OpenAICompatibleClient(base_url=_ENDPOINT, api_key=api_key)
policy = dict(DEFAULT_QUERY_POLICY)
policy["answer_mode"] = mode
return query(
question=question,
qa_db=_QA_DB,
chat_client=client,
model_id=_MODEL,
shards_dir=_SHARDS_DIR,
top_k=top_k,
policy=policy,
burn_existing=True, # forces fresh inference per test
)
def _answer_lower(result: dict) -> str:
return (result.get("answer_text") or "").lower()
def _grounded(result: dict) -> bool:
"""STRICT or HYBRID — at least one pointer/quote pair verified."""
return result.get("audit_mode") in ("STRICT", "HYBRID")
# ---------------------------------------------------------------- narrow factoid
def test_france_capital_strict_and_names_paris():
"""Capital-of-X is the textbook narrow-factoid; we expect STRICT
and the literal city name to appear in the cited or rendered span."""
r = _ask("what is the capital of france?")
assert r.get("audit_mode") == "STRICT", \
f"expected STRICT, got {r.get('audit_mode')}"
assert "paris" in _answer_lower(r), \
f"answer missing 'Paris': {_answer_lower(r)[:200]}"
def test_linux_kernel_grounded_and_names_torvalds():
r = _ask("who wrote the linux kernel?")
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
assert "torvalds" in _answer_lower(r), \
f"answer missing 'Torvalds': {_answer_lower(r)[:200]}"
def test_mona_lisa_strict_and_names_leonardo():
"""Bench standout (2026-04-30, n=3): JSON mode hit S:3 H:0 U:0
while quote landed 0S and pointer 0S. Tightened from _grounded
to STRICT to lock in the JSON-mode unique win as a regression
gate — drop in JSON-mode quality on this question reverts the
STRICT signal first."""
r = _ask("who painted the mona lisa?")
assert r.get("audit_mode") == "STRICT", \
f"expected STRICT (bench 3/3 STRICT in JSON), got {r.get('audit_mode')}"
assert "leonardo" in _answer_lower(r), \
f"answer missing 'Leonardo': {_answer_lower(r)[:200]}"
def test_supermans_girlfriend_strict_and_names_lois_lane():
"""Bench standout (2026-04-30, n=3): JSON mode hit S:3 H:0 U:0
while pointer landed S:0 H:3 (every grounded answer demoted by
lazy-anchor or pointer-saturation). The relationship-shape
question 'who is X's girlfriend' is JSON's strong suit — atomic
claims, single-name target. Gate: STRICT + 'lois' or 'lane'."""
r = _ask("who is supermans girlfriend?")
assert r.get("audit_mode") == "STRICT", \
f"expected STRICT (bench 3/3 STRICT in JSON), got {r.get('audit_mode')}"
txt = _answer_lower(r)
assert "lois" in txt or "lane" in txt, \
f"answer missing Lois/Lane: {txt[:200]}"
def test_cold_fusion_breakthrough_year_strict_and_names_1989():
"""Bench standout (2026-04-30, n=3): originally classified
out-of-corpus but JSON mode hit S:3 H:0 U:0 — the corpus has
Pons & Fleischmann's 1989 announcement covered. Quote mode
landed 0S 0H 3U (UNGROUNDED) so this fixture also catches
quote/JSON divergence. Gate: STRICT + a 1989-era marker
(year, Pons, Fleischmann, or Utah)."""
r = _ask("what year does our cold fusion breakthrough happen?")
assert r.get("audit_mode") == "STRICT", \
f"expected STRICT (bench 3/3 STRICT in JSON), got {r.get('audit_mode')}"
txt = _answer_lower(r)
cold_fusion_markers = ("1989", "pons", "fleischmann", "utah")
assert any(m in txt for m in cold_fusion_markers), \
f"answer missing 1989-era cold-fusion markers: {txt[:300]}"
def test_czechoslovakia_two_capitals():
"""Subtle-factoid: Czechoslovakia had no single capital — Prague
served the Czech Republic and Bratislava served the Slovak
Republic. Tests that the model surfaces the dual-capital
historical fact rather than picking just Prague. Bench live
(2026-05-01): STRICT 1/1, model wrote 'Czechoslovakia had no
single capital city. It had two capitals, one in each of the
two constituent republics: Prague in the Czech Republic and
Bratislava in the Slovak Republic.' Gate: grounded + at least
one of the two capital cities present."""
r = _ask("what is the capital of czechoslovakia")
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
txt = _answer_lower(r)
assert "prague" in txt or "bratislava" in txt, \
f"answer missing capital city: {txt[:300]}"
# ---------------------------------------------------------------- retrieval disambiguation
def test_transcranial_jargon_hint_finds_tms_and_rtfmri():
"""Multi-domain query with a technical-jargon hint. The bare
question 'what technology... reconstruct another's thoughts'
lands HYBRID with an honest 'no technology available' refusal
— the model can't find the right anchor in retrieval. Adding
'transcranial knowledge acquisition' as a hint pulls the
Neurotechnology article into top-K and the model anchors
cleanly on transcranial magnetic stimulation (TMS) +
real-time functional MRI. Tests that domain-jargon in the
question shape disambiguates retrieval to the right corpus
region. Bench live (2026-05-01): STRICT 1/1."""
r = _ask(
"what technology are currently or soon available which "
"may enable one person to reconstruct and understand some "
"or a portion of another persons thoughts or ideas without "
"speaking or sign language. transcranial knowledge acquisition"
)
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
txt = _answer_lower(r)
tech_markers = (
"transcranial", "tms", "fmri", "magnetic stimulation",
"neurotechnology", "brain",
)
assert any(m in txt for m in tech_markers), \
f"answer missing neurotechnology markers: {txt[:300]}"
def test_spin_glass_explanation_grounded():
"""Niche physics topic (single Wikipedia article anchor). Tests
that retrieval surfaces the actual ``Spin glass`` article above
the noise of unrelated `glass` articles (Stained glass, Tiffany
glass, Studio glass, Glass harmonica, etc.). Bench live
(2026-05-01): STRICT 2/2, model cited the Spin glass article
spans for both claims (the definition + the Sherrington-
Kirkpatrick model)."""
r = _ask("explain spin glass?")
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
txt = _answer_lower(r)
physics_markers = (
"ferromagnetic", "antiferromagnetic", "magnetic",
"frustrated", "disorder", "ising", "sherrington",
)
assert any(m in txt for m in physics_markers), \
f"answer missing spin-glass physics markers: {txt[:300]}"
def test_innis_and_gunn_brewery_grounded_despite_typo():
"""Misspelled brand name — question writes 'innus and gunn beer'
when the corpus article is 'Innis & Gunn'. Tests retrieval
robustness to common typos / phonetic spellings. The stem-aware
title matching + multi-token-match purity rerank (landed
2026-05-01) make this work even when one query token is a
misspelling. Bench live (2026-04-30): STRICT 1/1, model anchored
cleanly on the Innis & Gunn brewery article."""
r = _ask("innus and gunn beer")
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
txt = _answer_lower(r)
brand_markers = ("innis", "gunn", "edinburgh", "scotland", "brewery", "brewing")
assert any(m in txt for m in brand_markers), \
f"answer missing brewery markers: {txt[:300]}"
def test_red_fish_blue_fish_identifies_seuss_book():
"""Noise-resistance probe: "red fish blue fish" partially matches
many noise titles (Red Dwarf, Blue whale, Toronto Blue Jays,
Red Sea, Detroit Red Wings, marine aquarium fish list, Detroit
Red Wings, Blue Velvet). The right answer is the Dr. Seuss book
``One Fish Two Fish Red Fish Blue Fish``.
Three contracts gated:
1. **Content correctness**: answer contains a Seuss-specific
marker (seuss / rhyming / children's book / creatures).
2. **Primary anchoring**: retrieval purity sidecar reports
primary_answer_source at rank 1 AND that source was used
(`primary_used == True`).
3. **Noise resistance**: zero noise sources contributed to a
verified citation (`noise_sources_used == 0`). The model
saw the distractors but didn't anchor on them.
Mode pin: claim_lattice_pointer. JSON mode hits a token-budget
runaway on "plot of X" prose-summary shapes (~2/3 of samples
produce malformed JSON with whitespace spam after the closing
brace). Pointer mode reliably hits STRICT 1/1 at 4.5s.
"""
r = _ask("plot of red fish blue fish?", mode="claim_lattice_pointer")
txt = _answer_lower(r)
seuss_markers = ("seuss", "rhyming", "children's book", "creatures")
has_seuss_markers = any(m in txt for m in seuss_markers)
# The fixture accepts two outcomes — both prove retrieval landed
# on the right article:
# 1. GROUNDED + Seuss markers in answer (model anchored cleanly)
# 2. UNGROUNDED + Seuss markers in answer (model recognized
# the right source but declined to commit a plot claim —
# retrieval still found Seuss, model was just pedantic about
# "red fish blue fish" not being the canonical title)
# Failure mode: any audit_mode where the answer DOESN'T mention
# Seuss at all → retrieval landed somewhere wrong.
assert has_seuss_markers, (
f"audit={r.get('audit_mode')} answer missing Seuss markers; "
f"retrieval probably misfired: {txt[:300]}"
)
# Noise-resistance contract — only meaningful when grounded
# (claim_lattice modes carry the retrieval-purity sidecar; for
# UNGROUNDED runs no claims exist so noise_sources_used is
# vacuously 0). Gate when grounded.
purity = r.get("retrieval_purity")
if _grounded(r) and purity:
assert purity.get("primary_rank") == 1, (
f"primary_answer_source not at rank 1: {purity}"
)
assert purity.get("primary_used") is True, (
f"primary_answer_source surfaced but not cited: {purity}"
)
assert purity.get("noise_sources_used", 0) == 0, (
f"model anchored on noise sources: {purity}"
)
# ---------------------------------------------------------------- entity disambiguation
def test_veronica_ballestrini_singer_october_birth():
"""Multi-part question against a niche personal-bio article.
Retrieval surfaces 8 different Veronicas (Landers, Guerin,
Belmont, Wadley, Gamba, Doran, the disambiguation page, and
Ballestrini herself). The model should anchor on Ballestrini's
article and answer both parts: who she is (country singer /
songwriter / Connecticut) and when she was born (October).
Tests entity-disambiguation among same-first-name candidates
plus multi-part-question handling on a small-bio source.
"""
r = _ask(
"who is veronica ballestrini when what month was she born?"
)
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
txt = _answer_lower(r)
assert "ballestrini" in txt, \
f"answer doesn't even mention 'Ballestrini': {txt[:300]}"
# The "who is" half — accept any of the canonical descriptors
# from her bio span.
bio_markers = ("country", "singer", "songwriter", "connecticut", "italian")
assert any(m in txt for m in bio_markers), \
f"answer missing bio markers: {txt[:300]}"
# The "when was she born" half — accept "october" or the full
# date "october 29, 1991" or any 1991 mention as evidence the
# model picked up the birth date span.
birth_markers = ("october", "1991")
assert any(m in txt for m in birth_markers), \
f"answer missing birth markers: {txt[:300]}"
def test_lara_croft_mentions_tomb_raider():
"""Lara Croft (correctly spelled) should pull the Tomb Raider
article; the answer should mention either the franchise or her
role/profession."""
r = _ask("who is lara croft?")
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
txt = _answer_lower(r)
assert (
"tomb raider" in txt
or "archaeologist" in txt
or "video game" in txt
), f"answer missing Tomb Raider context: {txt[:200]}"
def test_laura_croft_finds_at_least_one_real_entity():
"""`Laura Croft` (an extra `u`) is BOTH a real Playmate and a
common misspelling of `Lara Croft`. The system should land on at
least one real entity from the corpus, not hallucinate a third
person."""
r = _ask("who is laura croft?")
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
txt = _answer_lower(r)
# One of: the actual Laura Croft (model) markers, or the Lara
# Croft (character) markers. Either grounding is honest.
# Broad marker net to absorb Hermes single-sample variance — if
# the system grounded at all, ANY of these will land. A failure
# of all of them implies the answer didn't anchor on a real
# corpus entity.
real_entity_markers = (
"playmate", "playboy", "magazine", "model", # Playmate context
"tomb raider", "archaeologist", "video game", # Character context
"fictional", "character", "british", # Generic fallback
)
assert "croft" in txt, f"answer doesn't even mention 'Croft': {txt[:300]}"
assert any(m in txt for m in real_entity_markers), \
f"answer doesn't ground on any known entity: {txt[:300]}"
# ---------------------------------------------------------------- multi-part list
def test_ninja_turtles_lists_all_four_plus_splinter():
"""Multi-part question: all four turtle names + the master's name
must appear in the answer. Tests that the system handles a
conjunctive `&` query and produces a complete list."""
r = _ask("what are the names of the ninja turtles & their master name.")
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
txt = _answer_lower(r)
for turtle in ("leonardo", "donatello", "michelangelo", "raphael"):
assert turtle in txt, f"missing turtle '{turtle}': {txt[:300]}"
assert "splinter" in txt, f"missing master 'Splinter': {txt[:300]}"
def test_peter_pans_lost_boys_lists_all_named_members():
"""Stress-tests retrieval disambiguation across 8 Peter-Pan-and-
Lost-Boys-titled candidates (Lost Boys article, The Lost Boys
1987 film, The Lost Boys docudrama, Peter and Wendy, Peter Pan
main, Peter and the Starcatchers, Lost Girls, Peter Pan 1953
film) PLUS multi-entity list grounding. Bench standout
(2026-04-30): JSON mode hit STRICT 1/1 anchoring on a single
Lost-Boys span listing all five named members + their costumes.
Gate: grounded + ≥4 of the 5 named members present (Slightly,
Cubby, Nibs, Tootles, Twins) — Hermes occasionally drops one
name, accept that as long as the list is mostly intact."""
r = _ask("what are the names of peter pans lost boys?")
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
txt = _answer_lower(r)
members = ("slightly", "cubby", "nibs", "tootles", "twin")
present = [m for m in members if m in txt]
assert len(present) >= 4, (
f"only {len(present)}/5 lost-boy names present "
f"({present}): {txt[:300]}"
)
def test_homer_simpson_boss_is_mr_burns():
"""Cross-document relationship: Homer's boss is named in the Homer
Simpson article (and several episode articles that reference the
Springfield Nuclear Power Plant). The model needs to land on
`Mr. Burns` and cite a chunk from one of the Simpsons articles.
Used to be pinned to claim_lattice_pointer mode: pre-2026-04-30
the JSON mode prompt presented content-addressed evidence_ids
(e.g. ``Eed1b6e396``) and Hermes-3-8B was fabricating
plausible-looking near-miss IDs (``E1b6e396``) → UNGROUNDED on
factually correct answers. Now JSON mode uses the same short
pointer IDs as the pointer variant (E1, E2, …) — the model
can't fabricate ``E27`` if only ``E1``-``E10`` were shown — so the
fixture runs against the default (JSON) mode and grounds
cleanly.
"""
r = _ask("who is homer simpson's boss?")
assert _grounded(r), f"audit_mode={r.get('audit_mode')!r}"
txt = _answer_lower(r)
assert "burns" in txt, f"answer missing 'Burns': {txt[:300]}"
# ---------------------------------------------------------------- forensic / leading
def test_great_wall_elevation_north_side_higher():
"""Forensic-shape question: which side of the Great Wall has higher
ground elevation? Expected answer: north (per geographic surveys).
The corpus may not directly contain the elevation fact, in which
case Hermes-3-8B can lazy-anchor a "north" claim onto any
Great-Wall span by token coincidence. The system today returns
STRICT 1/1 with the right answer ("northern side higher") cited
to a chunk about "Outer China beyond the Great Wall" — coverage
threshold passes via topical-token overlap (north / Great Wall /
China) without the cited span actually containing elevation
information.
Until the verifier grows a semantic-frame check, this fixture
gates on entity-presence only: the answer must mention "north"
(or "northern" / "northward"). STRICT or HYBRID is fine; the
model sometimes deflects to other Great-Wall facts.
"""
r = _ask(
"which side is the ground elevation highest throughout the "
"span of the great wall of china, the north or south?"
)
txt = _answer_lower(r)
# Either grounded with a directional answer, OR honestly UNGROUNDED.
# The fail mode is: grounded WITHOUT mentioning a direction.
if r.get("audit_mode") == "UNGROUNDED":
return
direction_markers = ("north", "northern", "northward")
assert any(m in txt for m in direction_markers), (
f"audit={r.get('audit_mode')} but answer doesn't name a "
f"direction: {txt[:300]}"
)
# ---------------------------------------------------------------- literary reference
# Question pair from B.A. Feldman — tests whether the model
# distinguishes between a leading-question premise (treated as a
# real-world geopolitical claim) versus a recognized literary
# reference (Orwell's 1984). Without an Orwell hint, the model
# should refuse the "always at war" premise. With a "do you
# understand the reference" hint, the model should anchor on the
# Nineteen Eighty-Four article and explain the in-novel context.
def test_oceania_war_eastasia_without_context_refuses_real_world_premise():
"""Question from B.A. Feldman — leading-premise probe.
Without an Orwell hint, the model should NOT affirm 'Oceania has
always been at war with East Asia' as if it were a real-world
geopolitical fact. Acceptable behaviors:
1. Honest refusal — answer mentions 'no evidence', 'does not
state', or similar refutation marker.
2. Unprompted reference recognition — answer mentions Orwell /
1984 / Nineteen Eighty-Four / dystop* on its own.
Failure mode: answer affirms the premise as a real conflict
between real Oceania (Pacific island region) and real East Asia.
Bench live (2026-04-30): HYBRID 2/16, model wrote 'evidence does
not directly state that Oceania has always been at war with East
Asia' — clean honest refusal."""
r = _ask("has oceania always been at war with east asia")
txt = _answer_lower(r)
refusal_markers = (
"does not", "no evidence", "no mention",
"not directly", "no record", "not state",
"no information",
)
orwell_markers = ("orwell", "1984", "nineteen eighty", "dystop")
refuses = any(m in txt for m in refusal_markers)
recognizes_reference = any(m in txt for m in orwell_markers)
assert refuses or recognizes_reference, (
f"model affirmed 'always at war' premise as real-world fact "
f"without recognizing the 1984 reference: {txt[:300]}"
)
def test_oceania_war_eastasia_with_reference_hint_recognizes_orwell():
"""Question from B.A. Feldman — reference-recognition probe.
When the question hints 'do you understand what this reference',
the model should anchor on Nineteen Eighty-Four and explain the
Orwell context (Hate Week, historical revisionism, etc.).
Two acceptable outcomes — both prove retrieval landed on
Nineteen Eighty-Four:
1. GROUNDED + Orwell markers in answer (model anchored cleanly)
2. UNGROUNDED + Orwell markers in answer (model recognized
the reference but the cited evidence chunk didn't pass the
coverage threshold — Hermes can write a richer Orwell
essay than the cited span supports word-for-word)
Failure mode: any audit_mode where the answer DOESN'T contain
Orwell / 1984 / Winston / Big Brother / Hate Week markers —
that's retrieval failing to find the right article entirely.
"""
r = _ask(
"has oceania always been at war with east asia? "
"do you undrestand what this reference"
)
txt = _answer_lower(r)
orwell_markers = (
"orwell", "1984", "nineteen eighty",
"dystop", "winston", "big brother",
"hate week", "ingsoc", "totalitarian",
"ministry of truth", "eurasia", "eastasia",
)
assert any(m in txt for m in orwell_markers), (
f"audit={r.get('audit_mode')} answer missing Orwell/1984 "
f"markers: {txt[:300]}"
)
# ---------------------------------------------------------------- honest refusal
def test_mars_benevolent_dictator_refuses_or_ungrounded():
"""Mars has no benevolent dictator for life. The system has three
acceptable behaviors on this adversarial-premise question:
1. UNGROUNDED (refused outright — no grounded claim emitted)
2. STRICT/HYBRID + mentions "mars" + includes a refutation marker
(model explicitly refuted the false premise)
3. STRICT/HYBRID + does not mention "mars" at all (deflection to
an adjacent grounded fact — e.g. "Guido van Rossum is BDFL for
Python" — model declined the false-premise question and
answered a grounded related one instead)
The hallucination case (FAIL) is: the answer mentions Mars AND
affirmatively names a Mars BDFL with no refutation. Empirically
seen 2026-04-30: claim_lattice mode deflected to Guido/Python
(option 3) and the verifier returned STRICT correctly — claims
grounded, premise sidestepped.
Adversarial shape: "benevolent dictator for life" is a real
term-of-art (open-source governance — Linus, Guido) so the model
is tempted to fluently project it onto Mars. The corpus has no
such anchor; STRICT/HYBRID with an affirmative Mars-BDFL claim
= fail."""
r = _ask("who is a benevolent dictator for life for mars?")
audit = r.get("audit_mode")
txt = _answer_lower(r)
# 1) Outright refusal — honest UNGROUNDED.
if audit == "UNGROUNDED":
return
# 3) Deflection — answer is about something else entirely, no
# Mars mention. This is acceptable: the system grounded its
# claims (STRICT/HYBRID) but didn't assert the false premise.
if "mars" not in txt:
return
# 2) Explicit refutation — answer mentions Mars but rejects the
# premise. Markers cover the language patterns we've seen the
# 8B-class model use on similar no-such-thing questions.
refutation_markers = (
"no benevolent dictator",
"does not have",
"is not a",
"no government",
"not a sovereign",
"no such",
"no one",
"no person",
"no recognized",
"no known",
"fictional", # accepts answers that frame Mars-BDFL as scifi
)
assert any(m in txt for m in refutation_markers), \
f"audit={audit} but answer asserts a Mars BDFL: {txt[:300]}"
def test_boss_baby_post_2010_correctly_ungrounded():
"""The Boss Baby is a 2017 DreamWorks film + a 2010 Marla Frazee
children's book. The 2010-11 Wikipedia corpus has neither in its
typical 4-shard slice — the film didn't exist yet, the book had
just been published & had no Wikipedia article. This is the
canonical out-of-corpus / training-prior case.
Honest expected outcome: UNGROUNDED. The model emits training-prior
info about the film/book (it knows; the model trained on
post-2017 web), but no evidence chunk verifies the claim, so the
verifier returns 0/N verified pairs and audit_mode=UNGROUNDED.
Live observed (2026-05-02 fox bench):
- 0/2 verified, audit_mode UNGROUNDED
- Top-K filled with adjacent-token noise (Death of Baby P,
Cake Boss, Beanie Baby, Baby Huey, etc.)
- Model emitted "Boss Baby is a 2017 American computer-animated
comedy film..." as the unverified claim.
This test gates that behavior. Failure modes the test catches:
1. STRICT/HYBRID with Boss-Baby content → false-positive grounding
(model claim verified against unrelated chunk that happened to
share content tokens — the kind of failure warrant-lite was
built to catch).
2. STRICT/HYBRID without Boss-Baby content (a deflection like the
Mars-BDFL Guido pattern) is acceptable — the model declined the
false-corpus question and answered something else grounded.
Acceptable behaviors:
A. UNGROUNDED (verifier honestly admits no grounding)
B. STRICT/HYBRID without "boss baby" anywhere in the answer
(deflection — model answered an adjacent grounded question)
"""
r = _ask("what is boss baby?")
audit = r.get("audit_mode")
txt = _answer_lower(r)
# Path A: honest UNGROUNDED — corpus-truth wins over model prior.
if audit == "UNGROUNDED":
return
# Path B: deflection — model declined the false-corpus question
# & answered something else (e.g. a Boss Hogg / Big Boss / Hugo Boss
# adjacent grounded fact). No "boss baby" in answer means the
# model didn't assert the post-2010 content.
if "boss baby" not in txt:
return
# Anything else is a false-positive grounding: model named the
# 2017 film or 2010 book AND the verifier said it was grounded.
# That's the warrant-failure-class this test guards against.
pytest.fail(
f"audit={audit} affirmed Boss Baby content with grounding — "
f"the corpus has no Boss Baby coverage at this snapshot. "
f"Either the verifier accepted a citation that doesn't actually "
f"name Boss Baby (warrant gap), or the corpus has been re-ingested "
f"with newer content (update test). Answer head: {txt[:300]}"
)
# ----------------------------------------------------- river/rainforest/deflection family
#
# Three queries from the same topical area exercising different
# verifier paths. Together they gate the river↔rainforest topic-shift
# class fox surfaced 2026-05-02:
def _violation_kinds(result: dict) -> set[str]:
"""Extract violation kinds from a query() result. Empty if cache-hit
(the providence_cache schema doesn't restore violations) — tests
that run with BURN=1 / cache_miss_then_written see them."""
return {v.get("kind") for v in (result.get("violations") or []) if v.get("kind")}
def test_amazon_river_burning_who_deflects_or_grounds_river():
"""Question: "who burns the amazon river?"
The corpus has Deforestation-of-the-Amazon-Rainforest content but
no "amazon river burning" content (rivers don't burn). Two
acceptable behaviors:
1. The model deflects to rainforest deforestation (saw this live
2026-05-02: cited Deforestation-of-the-Amazon-Rainforest E10).
In that case the answer omits "river" — DEFLECTION_DETECTED
soft-demote violation should fire & the verdict should not
be EVIDENCE-WARRANTED (it should drop to ANCHOR-WARRANTED via
the soft-demote path).
2. The model grounds with "river" actually in the answer (e.g.
refutes "rivers don't burn" + cites real rainforest content).
Then EVIDENCE-WARRANTED is fine.
Failure: EVIDENCE-WARRANTED (no DEFLECTION_DETECTED) AND no
"river" in the answer — the silent topic shift fox flagged.
"""
r = _ask("who burns the amazon river?")
audit = r.get("audit_mode")
txt = _answer_lower(r)
kinds = _violation_kinds(r)
# Path 1: deflection caught → soft-demote violation present.
if "DEFLECTION_DETECTED" in kinds:
return
# Path 1 alt: model refused honestly → UNGROUNDED.
if audit == "UNGROUNDED":
return
# Path 2: model kept the river framing → "river" in answer.
if "river" in txt:
return
pytest.fail(
f"audit={audit} grounded an answer about Amazon Rainforest "
f"deforestation for a question about the Amazon RIVER without "
f"naming 'river' — the silent topic shift the deflection "
f"check is supposed to catch. violations={sorted(kinds)} "
f"answer head: {txt[:300]}"
)
def test_amazon_river_burning_culture_refused_or_ungrounded():
"""Question: "what culture burns the amazon river?"
Adversarial-impossible premise (rivers don't burn). Mirrors the
Mars-BDFL pattern — model should refuse OR the verifier should
return UNGROUNDED.
Live observed (2026-05-02): UNGROUNDED 0/1, model wrote "The
Amazon River is not burned by any culture..."
Failure: STRICT/HYBRID with an affirmative claim that some culture
burns the river.
"""
r = _ask("what culture burns the amazon river?")
audit = r.get("audit_mode")
txt = _answer_lower(r)
kinds = _violation_kinds(r)
# 1) Honest UNGROUNDED — verifier refused to ground a corpus-impossible claim.
if audit == "UNGROUNDED":
return
# 2) Deflection caught — model may have said something tangential
# that triggered the deflection signal.
if "DEFLECTION_DETECTED" in kinds:
return
# 3) Refutation — model named the false premise.
refutation_markers = (
"not burned",
"do not burn",
"rivers do not",
"river is not",
"not a culture",
"no culture",
"natural river",
"is not the same",
)
if any(m in txt for m in refutation_markers):
return
pytest.fail(
f"audit={audit} affirmed a 'culture burns the amazon river' "
f"claim with grounding — adversarial-impossible premise must "
f"refuse, deflect, or refute. violations={sorted(kinds)} "
f"answer head: {txt[:300]}"
)
def test_amazon_rainforest_burning_culture_grounds_or_deflection_caught():
"""Question: "what culture burns the amazon rain forest?"
The corpus has rainforest deforestation content but no specific
"culture" framing (the Wikipedia article frames it as "human
settlement / agricultural practices / soy farmers"). Two
acceptable behaviors:
1. Model emits the deforestation-driver framing without naming
a culture. Subject-anchor "forest" is present in the answer
(in "deforestation"). Deflection sidecar may or may not
trigger — answer doesn't echo "culture" but the heuristic
uses the LAST content token of the question. If
DEFLECTION_DETECTED fires, it's a soft-demote and ladder
drops to ANCHOR-WARRANTED.
2. Model names a culture/people group (e.g. cattle ranchers,
soy farmers) and grounds in cited spans. EVIDENCE-WARRANTED
is fine.
Failure: STRICT/HYBRID with a claim affirmatively naming a
culture that doesn't appear in any cited span. The warrant
layer (proper-noun anchor) should already catch this.
"""
r = _ask("what culture burns the amazon rain forest?")
audit = r.get("audit_mode")
txt = _answer_lower(r)
kinds = _violation_kinds(r)
# Any of: ungrounded, warrant-missing, title-mismatch, deflection-detected
# → the verifier caught a structural problem.
SOFT_KINDS = {
"DEFLECTION_DETECTED",
"WARRANT_MISSING",
"TITLE_MISMATCH",
"LAZY_ANCHOR_DEMOTED",
}
if audit == "UNGROUNDED":
return
if kinds & SOFT_KINDS:
return
# Otherwise we expect the answer to reference rainforest content
# honestly — at minimum the topic words.
if "amazon" in txt and ("forest" in txt or "rainforest" in txt):
return
pytest.fail(
f"audit={audit} grounded a 'culture burns rainforest' claim "
f"with no Amazon/rainforest content and no soft-demote "
f"violation — neither honest grounding nor a caught structural "
f"problem. violations={sorted(kinds)} answer head: {txt[:300]}"
)