arborist/tests/test_honorific_brit_fold.py
russell@unturf.com 6573080284
feat(retrieval): honorific-fold + brit-fold — fold-search batch 3 (both measured wins)
Two more MEASURED fold-search wins on mined ground-truth fixtures
(deterministic recall, no LLM), both lifting at @1/@3/@8 (not
coarse-k artifacts):

  honorific (Mt/St/Dr <-> Mount/Saint/Doctor): recall@1 45% -> 75%
    (+30pp), @8 62% -> 85%, misses 15 -> 6
  brit (British <-> American spelling):         recall@1 50% -> 70%
    (+20pp), @8 70% -> 85%, misses 12 -> 6

Both _*_fold_variants are additive+symmetric, strict closed sets
(no English-word collision), no-op outside their class (verified
independent: brit no-ops on honorific titles & vice versa), unioned
into _title_query_tokens beside hyphen(#000007)/numeral/accent.
Full suite 2488 passed, 0 regressions (hot-path); real-path tests
(FakeSource->ingest->query()->real _Hit).

Fold-search FINAL across the survey backlog, ranked by MEASURED @1
headroom (not prevalence — the instrument's job):
  SHIPPED: numeral (a3ac653) accent (b573c59) honorific brit (here)
  NO-BUILD: hyphen — existing #000007 already delivers 90%@1
            (the measure-the-unmeasured-thing check pays off)
  NO-BUILD: amp — 82%@1 with no fold (prevalence-overranked;
            instrument killed it cheaply, like digit-ordinal pre-build)

Net: 4 deterministic retrieval wins + a reusable mined-recall
instrument + the discipline codified in CLAUDE.md, from a goal that
4 prior hypotheses died on because the bench couldn't measure them.
2026-05-18 19:32:03 -04:00

105 lines
4 KiB
Python

"""Honorific + British-spelling folds, retrieval-side.
Measured 2026-05-18 on mined ground-truth fixtures:
honorific recall@1 45% -> 75% (+30pp), misses 15 -> 6
brit recall@1 50% -> 70% (+20pp), misses 12 -> 6
Both lift at @1/@3/@8 (not coarse-k artifacts). Same additive+
symmetric discipline as `_hyphen_fold_variants` (#000007) /
numeral / accent.
Real path (FakeSource -> ingest -> query() -> real `_Hit`), never a
hand-built object — the discipline the reverted disambiguation v1
violated (false-green on the wrong type).
"""
from __future__ import annotations
from collections.abc import Iterator
from arborist.qa.query import (
_brit_fold_variants,
_honorific_fold_variants,
_title_query_tokens,
)
def test_variants_additive_symmetric_noop_and_independent():
assert _honorific_fold_variants("what is St Petersburg?") == {"saint"}
assert _honorific_fold_variants("Saint Petersburg") == {"st"}
assert _brit_fold_variants("what is labor economics?") == {"labour"}
assert _brit_fold_variants("Labour economics") == {"labor"}
# additive safety: pure non-class queries -> empty
assert _honorific_fold_variants("who painted the mona lisa?") == set()
assert _brit_fold_variants("who painted the mona lisa?") == set()
# independence: brit is a no-op on honorific titles & vice versa
assert _brit_fold_variants("what is St Lawrence Seaway?") == set()
assert _honorific_fold_variants("Labour economics") == set()
def test_title_query_tokens_bridges_both():
assert {"saint", "st", "petersburg"} <= (
_title_query_tokens("what is St Petersburg?")
& _title_query_tokens("Saint Petersburg"))
assert {"labor", "labour", "economics"} <= (
_title_query_tokens("what is labor economics?")
& _title_query_tokens("Labour economics"))
assert _title_query_tokens("who painted the mona lisa?") == {
"painted", "mona", "lisa"}
def _ingest(tmp_path, docs):
from arborist.document import Document
from arborist.ingest import ingest_source
from arborist.source import Source
from arborist.store import connect
class FakeSource(Source):
source_type = "test"
def __init__(self, ds):
self.ds = ds
def iter_documents(self) -> Iterator[Document]:
yield from self.ds
shard = tmp_path / "shard.db"
c = connect(shard)
try:
ingest_source(c, FakeSource([
Document(uri=f"t://{i}", content=ct, source_type="test", title=ti)
for i, (ti, ct) in enumerate(docs)]))
finally:
c.close()
return shard
def _ask(tmp_path, shard, q, ans):
from arborist.qa.client import StubClient
from arborist.qa.query import DEFAULT_QUERY_POLICY, query
r = query(question=q, qa_db=tmp_path / "qa.db",
chat_client=StubClient(answer=ans), model_id="stub",
single_db=shard,
policy=dict(DEFAULT_QUERY_POLICY,
answer_mode="claim_lattice_pointer"))
return r, {(s.get("title") or "") for s in (r.get("sources") or [])}
def test_honorific_query_retrieves_full_form_title(tmp_path):
shard = _ingest(tmp_path, [
("Saint Lawrence Seaway",
"The Saint Lawrence Seaway is a system of locks and canals. " * 12),
("Gardening", "Unrelated gardening content. " * 12)])
r, titles = _ask(tmp_path, shard, "what is St Lawrence Seaway?",
"The Saint Lawrence Seaway is a waterway. [E1]\n")
assert r["status"] != "error"
assert "Saint Lawrence Seaway" in titles
def test_brit_query_retrieves_british_spelled_title(tmp_path):
shard = _ingest(tmp_path, [
("Labour economics",
"Labour economics studies labour markets and wages. " * 12),
("Gardening", "Unrelated gardening content. " * 12)])
r, titles = _ask(tmp_path, shard, "what is labor economics?",
"Labour economics studies labour markets. [E1]\n")
assert r["status"] != "error"
assert "Labour economics" in titles