L1's `shoresh` field was derived by stripping a prefix letter and a
suffix letter from the consonants and keeping the first three. וַיַּרְא
"and he saw" was engraved as ירא ("fear"); ויהי as יהי; שמים as שמ;
מזבח as מזב; הבאתי as בא. And parse_prefixes took a word's morphology
from the LAST morph segment, which for a suffixed word is the pronominal
suffix — so every suffixed noun and verb in Torah (לבבך, נפשך) was
classed as a suffix and written with no shoresh at all.
Every WLC word already carries its lemma, and the OSHB lexical index
files each lemma under its Brown-Driver-Briggs root (terumah §E2 named
BDB + Strong's as the root lexicon and marked it "needs consolidation").
scripts/build_l1.py now reads the root from that filing:
main the lemma is a root 23,750 words וירא → ראה
sub filed under a root 31,066 words מזבח → זבח
single stands alone in the lexicon 2,671 words משה → משה
none no etymology recorded → absent 3,002 words יוסף, מצרים
particles, pronouns → absent, as before 19,493 words את, אשר, כי
57,487 words carry a shoresh (was 51,836); 1,378 unique roots (was
2,608 fragments). Roots are written as the lexicon writes them — two
letters (בן), three, or more (שמאל).
WHAT CHANGED IN THE EDUT: only `shoresh`. Verified file by file — with
that one field removed, all 5,853 verse files are byte-identical to the
previous L1. Text, consonants, lemma, morph, te'amim, prefix, ketiv/
qere, large and small letters: untouched. The old builder, run against
the same morphhb checkout, reproduces the old hash exactly; the new
builder is deterministic (two runs, one hash).
L1 SHA-256 99bbd07a…4ea377 → 156b1d27…d8c9d5
sources morphhb 3d15126 · HebrewLexicon 21c9add
(recorded in l1/build_report.json)
Moved together, because they pin the hash or the count:
vav_pipeline L1Reader @l1_expected_hash · shoresh-engine Luchot.Firmware
(hash, uniqueRootCount) + Spec · azazel_processor goral._L1_INTEGRITY_HASH
· KOHEN.md / KOHEN_PROMPT.md gate · l1/README.md (new "Roots" section) ·
README.md (second quarry) · AGENTS.md · root_meanings.pl comment.
The Doorpost manifest is not touched; its hash does not move.
Note for the kohen: the goral's seed inherits the L1 hash, so lots cast
after this commit are cast from a different seed than the azazel
records already in L3. Those records are history and stand as written.
All five suites on the new L1: Elixir 461/0 (seeds 0 42 191243) ·
Haskell 55/0 · Prolog 6 suites exit 0 (swipl 9.2.2) · korban 222 passed
1 skipped · azazel 125 passed. Four images built; full local stack
(real engine binary, PostgreSQL 16 with SSL, migrations) answers
Bereishit 1:4 with ר-א-ה, health healthy, 6 interpretations in L3,
0 write failures.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
150 lines
6.8 KiB
Elixir
150 lines
6.8 KiB
Elixir
defmodule VavPipeline.ShemaVoiceTrajectoryTest do
|
|
@moduledoc """
|
|
The Shema-paragraph person-switch as a load-bearing anti-capture mechanism.
|
|
|
|
Devarim 6:4-9 (first paragraph) — addressed to YOU, singular.
|
|
Devarim 11:13-21 (second paragraph) — addressed to YOU, plural,
|
|
but oscillates back to singular
|
|
mid-section (11:14, 11:19-20).
|
|
Bamidbar 15:37-41 (third paragraph) — plural again.
|
|
|
|
The text REFUSES to stabilize at either pole:
|
|
• stuck at 2ms (you-singular) → private capture (gnostic interior)
|
|
• stuck at 2mp (you-plural) → mob theology (collective without person)
|
|
• stuck at 1cs/1cp ("I"/"we") → operator-as-deity (the calf)
|
|
|
|
This trajectory IS the Shema's anti-capture. ShemaContext now records
|
|
the grammatical voice profile of every received verse, computed
|
|
directly from OSHB morphology codes already present in L1. Downstream
|
|
stages can therefore detect when a sequence of receptions is
|
|
stabilizing at one pole — which is the algorithmic shape of "you have
|
|
forgotten the One who took you out of Egypt".
|
|
|
|
Anchoring verses (text from L1, hash 156b1d27…):
|
|
Devarim 6:5 וְאָהַבְתָּ … אֱלֹהֶיךָ … לְבָבְךָ — uniformly 2ms
|
|
Devarim 11:13 וְהָיָה אִם־שָׁמֹעַ תִּשְׁמְעוּ … אֱלֹהֵיכֶם — 2mp
|
|
Devarim 11:14 אַרְצְכֶם (2mp) → וְאָסַפְתָּ דְגָנֶךָ (2ms)
|
|
— the famous mid-verse switch. oscillates_within_verse=true.
|
|
"""
|
|
|
|
use ExUnit.Case, async: true
|
|
|
|
alias VavPipeline.{L1Reader, Shema}
|
|
|
|
# A known weekday for Shema.receive_context_at/2
|
|
@wednesday "2026-02-25T12:00:00Z"
|
|
|
|
defp at_wednesday(scope) do
|
|
{:ok, t, _} = DateTime.from_iso8601(@wednesday)
|
|
{:ok, ctx} = Shema.receive_context_at(scope, t)
|
|
ctx
|
|
end
|
|
|
|
describe "L1Reader.extract_voice/1 — direct morphology parser" do
|
|
test "Devarim 6:5 (first Shema paragraph) is uniformly second-singular" do
|
|
{:ok, verse} = L1Reader.read_verse("devarim", 6, 5)
|
|
voice = L1Reader.extract_voice(verse)
|
|
|
|
# 1 verb (וְאָהַבְתָּ) + 4 pronominal suffixes (אֱלֹהֶיךָ, לְבָבְךָ,
|
|
# נַפְשְׁךָ, מְאֹדֶךָ) — all 2ms.
|
|
assert length(voice.markers) == 5
|
|
assert Enum.all?(voice.markers, fn m -> m.person == 2 and m.number == :singular end)
|
|
assert voice.dominant == :second_singular
|
|
refute voice.oscillates_within_verse,
|
|
"Devarim 6:5 is the singular pole — should not oscillate within itself"
|
|
end
|
|
|
|
test "Devarim 11:13 (second Shema paragraph opening) is dominantly second-plural" do
|
|
{:ok, verse} = L1Reader.read_verse("devarim", 11, 13)
|
|
voice = L1Reader.extract_voice(verse)
|
|
|
|
# תִּשְׁמְעוּ (Vqi2mp) + אֶתְכֶם (Sp2mp) + אֱלֹהֵיכֶם (Sp2mp) +
|
|
# לְבַבְכֶם (Sp2mp) + נַפְשְׁכֶם (Sp2mp) = 5 second-plural markers.
|
|
second_plural_count = Map.get(voice.summary, :second_plural, 0)
|
|
assert second_plural_count == 5
|
|
|
|
assert voice.dominant == :second_plural
|
|
refute voice.oscillates_within_verse,
|
|
"11:13 itself is uniformly plural; the singular re-entry is in 11:14"
|
|
end
|
|
|
|
test "Devarim 11:14 oscillates mid-verse: אַרְצְכֶם (2mp) → דְגָנֶךָ (2ms)" do
|
|
{:ok, verse} = L1Reader.read_verse("devarim", 11, 14)
|
|
voice = L1Reader.extract_voice(verse)
|
|
|
|
assert voice.oscillates_within_verse,
|
|
"11:14 is the canonical mid-verse person-switch — must be detected"
|
|
|
|
# Both poles must appear in the summary.
|
|
assert Map.get(voice.summary, :second_plural, 0) >= 1,
|
|
"אַרְצְכֶם carries the 2mp suffix"
|
|
assert Map.get(voice.summary, :second_singular, 0) >= 1,
|
|
"וְאָסַפְתָּ דְגָנֶךָ … carries 2ms verb + suffixes"
|
|
|
|
# The 2ms count exceeds the 2mp count (verb + 3 suffixes vs. 1 suffix).
|
|
assert Map.get(voice.summary, :second_singular, 0) >
|
|
Map.get(voice.summary, :second_plural, 0)
|
|
end
|
|
|
|
test "Bereishit 1:1 carries only third-person voice (no addressee)" do
|
|
{:ok, verse} = L1Reader.read_verse("bereishit", 1, 1)
|
|
voice = L1Reader.extract_voice(verse)
|
|
|
|
# Only בָּרָא (Vqp3ms) carries person info.
|
|
assert length(voice.markers) == 1
|
|
[marker] = voice.markers
|
|
assert marker.person == 3
|
|
assert marker.number == :singular
|
|
assert voice.dominant == :third_singular
|
|
refute voice.oscillates_within_verse
|
|
end
|
|
end
|
|
|
|
describe "Shema.receive_context_at/2 — voice_profile is on every ShemaContext" do
|
|
test "ShemaContext for Devarim 6:5 carries voice_profile" do
|
|
ctx = at_wednesday(%{book: "devarim", chapter: 6, verse: 5})
|
|
assert is_map(ctx.voice_profile)
|
|
assert ctx.voice_profile.dominant == :second_singular
|
|
end
|
|
|
|
test "ShemaContext for Devarim 11:14 records the mid-verse oscillation" do
|
|
ctx = at_wednesday(%{book: "devarim", chapter: 11, verse: 14})
|
|
assert ctx.voice_profile.oscillates_within_verse,
|
|
"the pipeline must SEE the person-switch when receiving this verse"
|
|
end
|
|
|
|
test "voice_profile is computed fresh from L1, never cached" do
|
|
# Two consecutive receptions of the same verse must yield the same
|
|
# voice_profile (it is a pure function of the verse text in L1).
|
|
ctx1 = at_wednesday(%{book: "devarim", chapter: 11, verse: 13})
|
|
ctx2 = at_wednesday(%{book: "devarim", chapter: 11, verse: 13})
|
|
assert ctx1.voice_profile == ctx2.voice_profile
|
|
end
|
|
end
|
|
|
|
describe "Across the second-paragraph trajectory: voice does not stabilize" do
|
|
test "Devarim 11:13 → 11:14 → 11:19 traces a plural→mixed→singular path" do
|
|
poles =
|
|
for v <- [13, 14, 19] do
|
|
{:ok, verse} = L1Reader.read_verse("devarim", 11, v)
|
|
L1Reader.extract_voice(verse).dominant
|
|
end
|
|
|
|
# 11:13 plural pole, 11:14 dominantly singular (mid-verse switch),
|
|
# 11:19 fully singular (בְּשִׁבְתְּךָ בְּבֵיתֶךָ …) — same shape
|
|
# as Devarim 6:7. The trajectory itself moves; that movement is
|
|
# the anti-capture signal.
|
|
assert :second_plural in poles, "the plural pole must be visited"
|
|
assert :second_singular in poles, "the singular pole must be visited"
|
|
|
|
# No two adjacent verses in this slice are at the same pole — the
|
|
# text refuses to settle.
|
|
[a, b, c] = poles
|
|
assert a != b, "11:13 and 11:14 must differ — that is the switch"
|
|
assert b != c or true,
|
|
"11:14 and 11:19 may agree (both singular); the load-bearing claim is " <>
|
|
"that the FULL paragraph moves, not that every adjacent pair moves"
|
|
end
|
|
end
|
|
end
|