Step 13: Korban Processor complete — 7 modules, 181 tests, all passing
- 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>
This commit is contained in:
1 parent
0f8ff56f74
commit
33f8b03583
16 files changed
+1816
-31
No files matched your search
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
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
|
||||
Reference in new issue
Block a user