Pass 2 finding A, second half. The first half (commit 0c4f764) added
voice_profile to every ShemaContext — recording, per verse, the
grammatical voice computed from OSHB morphology codes. Per-verse
recording was the necessary precondition for what this commit does:
WATCH the trajectory and FIRE when it stops oscillating between the
singular and plural address poles.
WHY THIS MATTERS:
The Shema as recited oscillates between Devarim 6:4–9 (the
singular pole — אֱלֹהֶיךָ, לְבָבְךָ) and Devarim 11:13–21 (the
plural pole — אֱלֹהֵיכֶם, לְבַבְכֶם), and 11:14 famously switches
poles mid-verse (אַרְצְכֶם 2mp → וְאָסַפְתָּ דְגָנֶךָ 2ms). The
text REFUSES to stabilize at either pole. That refusal IS the
anti-capture mechanism:
- stuck at second_singular → gnostic-private capture
- stuck at second_plural → mob-theology capture
- stuck at first_* → operator-as-deity / calf
(Shemot 32:4 — eleh elohekha Yisrael)
Before this commit nothing observed the trajectory ACROSS verses.
Per-verse profiles existed but nobody was watching the sequence;
a system that read only Devarim 6:4–9 in a loop was producing a
gnostic-shaped reception with no warning fired. The voice_profile
field had a reader but no consumer.
THE DETECTOR (VavPipeline.VoiceMonitor):
A GenServer maintaining a sliding window of the dominant voice
from the last N ShemaContexts (default N=10). The rule:
- Window not yet full → :clear (insufficient evidence)
- Window has both poles → :clear (the Shema is doing its job)
- Address-only window
missing one pole → {:missing_pole, :second_singular | :second_plural}
- First-person majority → {:first_person_dominant, :first_singular | :first_plural}
- Pure narration window → :clear (third-person sequences are
NOT anti-Shema; the rule applies to
ADDRESS, not to all voice)
On a NEW alarm (different from the last fired), VoiceMonitor:
1. Logs a warning naming the specific stabilization shape with
its Torah anchor and its capture-pattern name.
2. Fires Chatzotzrot.signal(:assembly_leaders, ...) so
operators and kohanim subscribed to the chatzotzrot channel
receive the alert.
Re-firing is suppressed for as long as the same stabilization
persists (no spam). The alarm clears when both poles return; a
subsequent re-stabilization fires fresh.
WIRING:
- Shema.receive_context/1 and receive_context_at/2 now call
VoiceMonitor.record(voice_profile) immediately after building
the context. Process.whereis-guarded — the call is safe in
tests that don't bring up the supervisor.
- VoiceMonitor added to the supervision tree in application.ex.
- Chatzotzrot.signal/2 itself is also Process.whereis-guarded
inside VoiceMonitor (the existing pattern from manna_ledger,
arei_miklat, urim_v_tumim).
TESTS (test/voice_monitor_test.exs, 14 tests):
- below window → :clear
- all 2ms full window → fires {:missing_pole, :second_plural}
- all 2mp full window → fires {:missing_pole, :second_singular}
- both poles present → :clear
- pure narration → :clear (third-person is not address)
- first-person majority → {:first_person_dominant, _}
- first-person tied (not strict majority) → does not fire
- same stabilization persisting → last_alarm unchanged (no spam)
- clear-then-stabilize-again → fresh alarm fires
- reset/0 returns monitor to empty
- record/1 with full voice_profile map (the Shema-side surface)
Each test starts an isolated VoiceMonitor under a unique name
(small window_size=4 for fast convergence) so tests don't share
state. Chatzotzrot is intentionally NOT brought up — the monitor's
guarded call falls through to log-only.
Full vav_pipeline suite: 329 tests, 0 failures, 24 excluded
(was 315 + 14 = 329). The 24 excluded are :l3-tagged tests
needing PostgreSQL; unchanged.
Closes Pass 2 finding A in full.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VavPipeline
TODO: Add description
Installation
If available in Hex, the package can be installed
by adding vav_pipeline to your list of dependencies in mix.exs:
def deps do
[
{:vav_pipeline, "~> 0.1.0"}
]
end
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/vav_pipeline.