- semichah: provenance tagging (6 sources, rejects empty/anonymous) - shechitah: strip preamble, self-reference, hedging, markdown, sycophancy - zerikah: extract Torah refs, Hebrew text, root analysis, insight markers - haktarah: transform prose to Prolog propositions (verse/3, root/2, claim/2, connection/3) - router: stateless routing — removed module-level _routing_log (kiyyor principle) - model_interface: outbound Claude API via httpx (Construction §3.4, HTTPS only) - hebrew_parser: nikkud/teamim stripping, consonant extraction, gematria, root identification All 5 BUILD_KORBAN.md verification tests pass. AGENTS.md: added structure_not_content.md to spec table, added Principle 6. build_log.md: recorded session, broken tablets #18-20, design decisions. Ka'asher tzivah Hashem et Moshe. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
186 lines
6.3 KiB
Python
186 lines
6.3 KiB
Python
"""
|
|
Tests for haktarah — transform modality (prose → Prolog propositions).
|
|
|
|
BUILD_KORBAN.md Verification §3:
|
|
Feed it extracted vital content. Verify the Prolog propositions are
|
|
syntactically valid and semantically grounded.
|
|
|
|
Architecture §3.5: haktarah converts prose insights into Prolog-compatible
|
|
propositions for the Talmudic Engine.
|
|
"""
|
|
|
|
import re
|
|
|
|
from korban_processor.haktarah import (
|
|
haktarah,
|
|
torah_ref_to_prolog,
|
|
root_to_prolog,
|
|
extract_claims,
|
|
extract_connections,
|
|
sanitize_prolog_string,
|
|
)
|
|
from test.fixtures import CLEAN_TORAH_RESPONSE, LATIN_TORAH_RESPONSE
|
|
|
|
|
|
# Valid Prolog term pattern: functor(args...)
|
|
_PROLOG_TERM_PATTERN = re.compile(r"^\w+\(.*\)$")
|
|
|
|
|
|
def is_valid_prolog_term(s: str) -> bool:
|
|
"""Check if a string looks like a valid Prolog term."""
|
|
return bool(_PROLOG_TERM_PATTERN.match(s.strip()))
|
|
|
|
|
|
class TestTorahRefToProlog:
|
|
def test_genesis_becomes_bereishit(self):
|
|
result = torah_ref_to_prolog("Genesis 1:1")
|
|
assert result == "verse(bereishit, 1, 1)"
|
|
|
|
def test_bereishit_stays_bereishit(self):
|
|
result = torah_ref_to_prolog("Bereishit 1:1")
|
|
assert result == "verse(bereishit, 1, 1)"
|
|
|
|
def test_exodus_becomes_shemot(self):
|
|
result = torah_ref_to_prolog("Exodus 3:14")
|
|
assert result == "verse(shemot, 3, 14)"
|
|
|
|
def test_leviticus_becomes_vayikra(self):
|
|
result = torah_ref_to_prolog("Leviticus 19:18")
|
|
assert result == "verse(vayikra, 19, 18)"
|
|
|
|
def test_numbers_becomes_bamidbar(self):
|
|
result = torah_ref_to_prolog("Numbers 15:38")
|
|
assert result == "verse(bamidbar, 15, 38)"
|
|
|
|
def test_deuteronomy_becomes_devarim(self):
|
|
result = torah_ref_to_prolog("Deuteronomy 6:4")
|
|
assert result == "verse(devarim, 6, 4)"
|
|
|
|
def test_abbreviated_gen(self):
|
|
result = torah_ref_to_prolog("Gen. 1:1")
|
|
assert result == "verse(bereishit, 1, 1)"
|
|
|
|
def test_invalid_ref_returns_empty(self):
|
|
result = torah_ref_to_prolog("not a reference")
|
|
assert result == ""
|
|
|
|
|
|
class TestRootToProlog:
|
|
def test_dash_separated_hebrew_root(self):
|
|
result = root_to_prolog("ר-א-ש")
|
|
assert result.startswith('root("ראש"')
|
|
assert "hebrew" in result
|
|
|
|
def test_dot_separated_hebrew_root(self):
|
|
result = root_to_prolog("ר.א.ש")
|
|
assert result.startswith('root("ראש"')
|
|
|
|
def test_root_keyword_with_hebrew(self):
|
|
result = root_to_prolog("root ראש")
|
|
assert "ראש" in result
|
|
assert "hebrew" in result
|
|
|
|
def test_transliterated_root(self):
|
|
result = root_to_prolog("root shin")
|
|
assert "shin" in result
|
|
assert "transliterated" in result
|
|
|
|
def test_invalid_root_returns_empty(self):
|
|
result = root_to_prolog("not a root")
|
|
assert result == ""
|
|
|
|
|
|
class TestExtractClaims:
|
|
def test_extracts_means_claim(self):
|
|
text = "The root ר-א-ש means head or beginning."
|
|
claims = extract_claims(text)
|
|
assert len(claims) >= 1
|
|
assert any('claim(' in c for c in claims)
|
|
|
|
def test_extracts_indicates_claim(self):
|
|
text = "This passage indicates divine ownership of creation."
|
|
claims = extract_claims(text)
|
|
assert len(claims) >= 1
|
|
|
|
def test_claim_is_valid_prolog_term(self):
|
|
text = "The root ר-א-ש means head or beginning."
|
|
claims = extract_claims(text)
|
|
for claim in claims:
|
|
assert is_valid_prolog_term(claim), f"Not a valid Prolog term: {claim}"
|
|
|
|
def test_no_claims_returns_empty(self):
|
|
text = "This is a plain sentence without analytical markers."
|
|
claims = extract_claims(text)
|
|
# May be empty — that's fine
|
|
for claim in claims:
|
|
assert is_valid_prolog_term(claim)
|
|
|
|
|
|
class TestExtractConnections:
|
|
def test_extracts_connects_to(self):
|
|
text = "The concept of Shabbat connects to creation cessation."
|
|
conns = extract_connections(text)
|
|
assert len(conns) >= 1
|
|
assert any('connection(' in c for c in conns)
|
|
|
|
def test_connection_is_valid_prolog_term(self):
|
|
text = "Rosh Hashana parallels Bereishit through the root ר-א-ש."
|
|
conns = extract_connections(text)
|
|
for conn in conns:
|
|
assert is_valid_prolog_term(conn), f"Not a valid Prolog term: {conn}"
|
|
|
|
|
|
class TestSanitizePrologString:
|
|
def test_escapes_double_quotes(self):
|
|
result = sanitize_prolog_string('He said "hello"')
|
|
assert '\\"' in result
|
|
|
|
def test_replaces_newlines(self):
|
|
result = sanitize_prolog_string("line one\nline two")
|
|
assert "\n" not in result
|
|
|
|
def test_truncates_long_strings(self):
|
|
long_str = "x" * 300
|
|
result = sanitize_prolog_string(long_str)
|
|
assert len(result) <= 204 # 200 + "..."
|
|
|
|
|
|
class TestHaktarah:
|
|
def test_returns_list(self):
|
|
result = haktarah(CLEAN_TORAH_RESPONSE)
|
|
assert isinstance(result, list)
|
|
|
|
def test_empty_input_returns_empty_list(self):
|
|
result = haktarah("")
|
|
assert result == []
|
|
|
|
def test_all_propositions_are_valid_prolog_terms(self):
|
|
"""BUILD_KORBAN verification §3: Prolog propositions are syntactically valid."""
|
|
result = haktarah(CLEAN_TORAH_RESPONSE)
|
|
assert len(result) > 0
|
|
for prop in result:
|
|
assert is_valid_prolog_term(prop), f"Invalid Prolog term: {prop}"
|
|
|
|
def test_torah_refs_produce_verse_facts(self):
|
|
"""BUILD_KORBAN verification §3: semantically grounded."""
|
|
result = haktarah(LATIN_TORAH_RESPONSE)
|
|
verse_props = [p for p in result if p.startswith("verse(")]
|
|
assert len(verse_props) >= 3 # 5 books referenced
|
|
|
|
def test_contains_bereishit_verse(self):
|
|
result = haktarah(CLEAN_TORAH_RESPONSE)
|
|
assert any("verse(bereishit," in p for p in result)
|
|
|
|
def test_hebrew_roots_produce_root_facts(self):
|
|
content = "The root ר-א-ש means beginning. Genesis 1:1 uses בְּרֵאשִׁית."
|
|
result = haktarah(content)
|
|
root_props = [p for p in result if p.startswith("root(")]
|
|
assert len(root_props) >= 1
|
|
|
|
def test_stateless_between_calls(self):
|
|
"""Kiyyor principle: second call unaffected by first."""
|
|
result1a = haktarah(CLEAN_TORAH_RESPONSE)
|
|
_ = haktarah(LATIN_TORAH_RESPONSE)
|
|
result1b = haktarah(CLEAN_TORAH_RESPONSE)
|
|
assert result1a == result1b
|