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:
Scott KarpandClaude Sonnet 4.6 committed 2026-03-09 09:37:25 -04:00
1 parent 0f8ff56f74
commit 33f8b03583
16 files changed
+1816 -31

No files matched your search

+177
View File
@@ -0,0 +1,177 @@
"""
Tests for hebrew_parser — Hebrew text analysis utilities.
Architecture §3.5: Used by zerikah for Hebrew analysis.
Construction §1.4: hebrew-tokenizer==2.3.0.
"""
from korban_processor.hebrew_parser import (
is_consonant,
is_hebrew_char,
is_hebrew_text,
strip_nikkud,
strip_teamim,
strip_nikkud_and_teamim,
extract_consonants,
gematria,
identify_root,
normalize_root_notation,
split_hebrew_words,
)
class TestHebrewCharDetection:
def test_alef_is_consonant(self):
assert is_consonant("א")
def test_tav_is_consonant(self):
assert is_consonant("ת")
def test_latin_a_not_consonant(self):
assert not is_consonant("a")
def test_nikkud_sheva_not_consonant(self):
# U+05B0 = SHEVA — vowel point, not consonant
assert not is_consonant("\u05b0")
def test_hebrew_text_with_consonants(self):
assert is_hebrew_text("בְּרֵאשִׁית")
def test_latin_text_not_hebrew(self):
assert not is_hebrew_text("Genesis")
def test_empty_not_hebrew(self):
assert not is_hebrew_text("")
class TestNikkudStripping:
def test_strip_nikkud_bereishit(self):
# בְּרֵאשִׁית → בראשית (consonants + te'amim, no nikkud)
result = strip_nikkud("בְּרֵאשִׁית")
# Should contain only consonants (and possibly te'amim if any)
for ch in result:
assert ord(ch) < 0x05B0 or ord(ch) >= 0x05C8 or is_consonant(ch), \
f"Unexpected nikkud char: {ch!r} (U+{ord(ch):04X})"
def test_strip_nikkud_preserves_consonants(self):
result = strip_nikkud("בְּרֵאשִׁית")
assert "ב" in result
assert "ר" in result
assert "א" in result
assert "ש" in result
assert "י" in result
assert "ת" in result
def test_strip_nikkud_plain_text_unchanged(self):
assert strip_nikkud("בראשית") == "בראשית"
def test_strip_nikkud_and_teamim(self):
result = strip_nikkud_and_teamim("בְּרֵאשִׁית")
# Only consonants should remain
for ch in result:
assert is_consonant(ch), f"Non-consonant after full strip: {ch!r}"
class TestExtractConsonants:
def test_extracts_consonants_from_bereishit(self):
result = extract_consonants("בְּרֵאשִׁית")
assert result == "בראשית"
def test_extracts_consonants_plain(self):
assert extract_consonants("בראשית") == "בראשית"
def test_empty_returns_empty(self):
assert extract_consonants("") == ""
def test_latin_returns_empty(self):
assert extract_consonants("Genesis") == ""
class TestGematria:
def test_alef_is_1(self):
assert gematria("א") == 1
def test_bet_is_2(self):
assert gematria("ב") == 2
def test_tav_is_400(self):
assert gematria("ת") == 400
def test_torah_gematria(self):
# תורה = 400 + 6 + 200 + 5 = 611
assert gematria("תורה") == 611
def test_bereishit_gematria(self):
# בראשית = 2 + 200 + 1 + 300 + 10 + 400 = 913
assert gematria("בראשית") == 913
def test_kaf_sofit_equals_kaf(self):
# ך = 20 (same as כ)
assert gematria("ך") == gematria("כ")
def test_mem_sofit_equals_mem(self):
# ם = 40 (same as מ)
assert gematria("ם") == gematria("מ")
def test_latin_chars_ignored(self):
assert gematria("abc") == 0
def test_nikkud_ignored(self):
# Nikkud chars have no gematria value
assert gematria("בְּרֵאשִׁית") == gematria("בראשית")
class TestIdentifyRoot:
def test_three_letter_word_returns_word(self):
root = identify_root("בָּרָא")
assert root == "ברא"
def test_short_word_returns_none(self):
assert identify_root("אל") is None
def test_empty_returns_none(self):
assert identify_root("") is None
def test_returns_first_three_consonants(self):
# For a 4-letter word, return first 3 (Phase 2 approximation)
root = identify_root("בראשית")
assert root == "בר" + "א" # First 3 consonants
class TestNormalizeRootNotation:
def test_dash_separated(self):
result = normalize_root_notation("ר-א-ש")
assert result == "ראש"
def test_dot_separated(self):
result = normalize_root_notation("ר.א.ש")
assert result == "ראש"
def test_bare_consonants(self):
result = normalize_root_notation("ראש")
assert result == "ראש"
def test_non_three_letter_returns_none(self):
assert normalize_root_notation("אב") is None
assert normalize_root_notation("אבגד") is None
class TestSplitHebrewWords:
def test_splits_single_word(self):
words = split_hebrew_words("בְּרֵאשִׁית")
assert len(words) == 1
def test_splits_multiple_words(self):
words = split_hebrew_words("בְּרֵאשִׁית בָּרָא אֱלֹהִים")
assert len(words) == 3
def test_splits_on_latin_boundary(self):
words = split_hebrew_words("The word בְּרֵאשִׁית opens Torah.")
assert len(words) == 1
assert "ב" in words[0]
def test_empty_text_returns_empty(self):
assert split_hebrew_words("") == []
def test_latin_only_returns_empty(self):
assert split_hebrew_words("Genesis 1:1") == []