Files
bikoret/vav_pipeline/test/shema_voice_trajectory_test.exs
T
Scott KarpandClaude Opus 4.7 0c4f764024 shema: record grammatical voice trajectory on every ShemaContext
The Shema (Devarim 6:4-9, 11:13-21, Bamidbar 15:37-41) deliberately
oscillates between singular ("you, sg.") and plural ("you, pl.")
voice. Devarim 11:14 famously switches mid-verse from אַרְצְכֶם (2mp)
to וְאָסַפְתָּ דְגָנֶךָ (2ms). The text refuses to stabilize at
either pole; that refusal IS the anti-capture mechanism:

  • stuck at 2ms     → private capture (gnostic interior)
  • stuck at 2mp     → mob theology (collective without person)
  • stuck at 1cs/1cp → operator-as-deity (the calf)

Before this commit, vav_pipeline/lib/vav_pipeline/shema.ex — the
module literally named after the Shema — encoded none of this. No
markers for person/singular/plural anywhere in the file. The chat
review surfaced this as "load-bearing anti-capture, missing".

Changes:

L1Reader.extract_voice/1 walks every word of a verse, parses the
OSHB morphology codes already present in L1 (Vqq2ms, Sp2mp, Pp1cs,
Vqi2mp, …), and returns a profile with markers, summary by
voice-key, dominant pole, and oscillates_within_verse boolean.
Strips the H/A language marker that prefixes the first /-segment of
each morph string — without that, every verse-initial verb (HVqi…)
was being silently dropped.

ShemaContext now carries voice_profile on every reception (both
receive_context/1 and receive_context_at/2). The voice is computed
fresh from L1, never cached.

8 new tests in test/shema_voice_trajectory_test.exs verify the
extractor against four anchoring verses:
  Devarim 6:5    — uniformly second-singular (5 markers)
  Devarim 11:13  — dominantly second-plural (5 second-plural markers)
  Devarim 11:14  — oscillates_within_verse: true (the mid-verse switch)
  Bereishit 1:1  — only third-singular (no addressee)
Plus three context-level tests confirming ShemaContext exposes the
field, computes it fresh, and traces a non-stabilizing trajectory
across 11:13 → 11:14 → 11:19.

Full vav_pipeline suite: 288 tests, 3 failures (all pre-existing
Shabbat-day calendar failures unrelated to this change), 19 excluded
(L3 PostgreSQL).

Still open: the across-verse stabilization-detector. ShemaContext
now KNOWS its voice; nothing yet refuses a sequence of receptions
that all stabilize at one pole. That belongs downstream — probably
the kushya generator (kal-vachomer challenge when voice has not
moved in N receptions). Logged in Pass 2 of
~/shorashim-conversation-review.md (Finding A).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 18:58:14 -04:00

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 99bbd07a…):
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