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>
105 lines
3.5 KiB
Elixir
105 lines
3.5 KiB
Elixir
defmodule VavPipeline.Shema do
|
|
@moduledoc """
|
|
The Shema context builder.
|
|
|
|
Architecture §3.9, §4 Step 9:
|
|
Reads from L1 for te'amim/Torah governance.
|
|
Reads calendar for Shabbat/festivals.
|
|
Reads provision file for manna.
|
|
|
|
The ShemaContext is IMMUTABLE once built. It is passed through
|
|
every stage of the pipeline. Processing happens WITHIN the context,
|
|
never modifying it.
|
|
|
|
Construction spec §4.4: shema_context_cache_ttl_ms: 0
|
|
— always read fresh. No caching.
|
|
"""
|
|
|
|
alias VavPipeline.{L1Reader, Cloud, Manna}
|
|
|
|
@type shema_context :: %{
|
|
# From L1
|
|
verse: L1Reader.verse(),
|
|
teamim_marks: [map()],
|
|
ketiv_qere: [map()],
|
|
root_gematria: map(),
|
|
smikhut: [map()],
|
|
# Voice profile — anti-capture: the Shema deliberately oscillates
|
|
# between "you sg." and "you pl." (e.g. Devarim 11:14 switches
|
|
# mid-verse from אַרְצְכֶם to וְאָסַפְתָּ דְגָנֶךָ). Stabilization
|
|
# at either pole is anti-Shema; the trajectory IS the protection.
|
|
# See L1Reader.extract_voice/1 for full rationale.
|
|
voice_profile: map(),
|
|
# From cloud (Torah calendar + L1 integrity)
|
|
cloud_state: Cloud.cloud_state(),
|
|
# From manna
|
|
daily_provision: Manna.manna() | :no_provision,
|
|
# Metadata
|
|
scope: VavPipeline.scope(),
|
|
received_at: DateTime.t()
|
|
}
|
|
|
|
@doc """
|
|
Receive context for a verse scope.
|
|
|
|
This is RECEPTION, not processing. The system hears before it speaks.
|
|
Reads L1 (te'amim, roots, ketiv/qere), checks cloud (Torah + calendar),
|
|
checks manna (provision).
|
|
|
|
Returns {:ok, context} or {:error, reason}.
|
|
"""
|
|
@spec receive_context(VavPipeline.scope()) :: {:ok, shema_context()} | {:error, String.t()}
|
|
def receive_context(scope) do
|
|
now = DateTime.utc_now()
|
|
|
|
with {:ok, verse} <- L1Reader.read_verse(scope.book, scope.chapter, scope.verse),
|
|
{:ok, cloud_state} <- Cloud.check_at(now),
|
|
{:ok, manna} <- Manna.check_at(now) do
|
|
context = %{
|
|
# L1 data — read fresh, never cached
|
|
verse: verse,
|
|
teamim_marks: L1Reader.extract_teamim(verse),
|
|
ketiv_qere: L1Reader.extract_ketiv_qere(verse),
|
|
root_gematria: L1Reader.extract_roots(verse),
|
|
smikhut: L1Reader.extract_smikhut(verse),
|
|
voice_profile: L1Reader.extract_voice(verse),
|
|
# Cloud state — derived from Torah (L1 integrity + calendar)
|
|
cloud_state: cloud_state,
|
|
# Manna — daily provision
|
|
daily_provision: manna,
|
|
# Metadata
|
|
scope: scope,
|
|
received_at: now
|
|
}
|
|
|
|
{:ok, context}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Build context with explicit time. Testable variant.
|
|
"""
|
|
@spec receive_context_at(VavPipeline.scope(), DateTime.t()) ::
|
|
{:ok, shema_context()} | {:error, String.t()}
|
|
def receive_context_at(scope, now) do
|
|
with {:ok, verse} <- L1Reader.read_verse(scope.book, scope.chapter, scope.verse),
|
|
{:ok, cloud_state} <- Cloud.check_at(now),
|
|
{:ok, manna} <- Manna.check_at(now) do
|
|
context = %{
|
|
verse: verse,
|
|
teamim_marks: L1Reader.extract_teamim(verse),
|
|
ketiv_qere: L1Reader.extract_ketiv_qere(verse),
|
|
root_gematria: L1Reader.extract_roots(verse),
|
|
smikhut: L1Reader.extract_smikhut(verse),
|
|
voice_profile: L1Reader.extract_voice(verse),
|
|
cloud_state: cloud_state,
|
|
daily_provision: manna,
|
|
scope: scope,
|
|
received_at: now
|
|
}
|
|
|
|
{:ok, context}
|
|
end
|
|
end
|
|
end
|