defmodule Step15ShemaTest do @moduledoc """ Step 15 Verification — The Shema Test. Architecture §6.3: Before the ki tov test, the shema test: can the system hear? Five tests from §6.3: 1. Te'amim reception — the text's own divisions guide havdalah 2. Ketiv/qere preservation — dual readings never collapse 3. Cloud obedience — system does NOT process when cloud lifts 4. Manna fidelity — system waits when no provision 5. Context immutability — ShemaContext unchanged at every stage Plus integration tests from §6.2: 6. Halt condition — pipeline halts on cardinal sin trigger 7. Full pipeline end-to-end — input through to evaluated output """ use ExUnit.Case, async: false alias VavPipeline.{BereishitCycle, Shema, Specification, HaltConditions} # A known weekday for testing — Wednesday noon UTC @wednesday_noon "2026-02-25T12:00:00Z" @scope %{book: "bereishit", chapter: 1, verse: 1} # ============================================================ # Test 1: Te'amim Reception (Architecture §6.3) # ============================================================ describe "Shema Test 1: Te'amim Reception" do test "ShemaContext contains raw te'amim marks from L1" do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) # 7 words in Bereishit 1:1 → 7 te'amim marks assert length(context.teamim_marks) == 7 # Each mark has taam, word_index, role Enum.each(context.teamim_marks, fn mark -> assert Map.has_key?(mark, :taam) assert Map.has_key?(mark, :word_index) assert Map.has_key?(mark, :role) assert mark.role in ["melekh", "mesharet"] end) end test "etnachta on word 3 classified as melekh" do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) etnachta = Enum.find(context.teamim_marks, fn m -> m.taam == "etnachta" end) assert etnachta != nil assert etnachta.word_index == 2 assert etnachta.role == "melekh" end test "meteg_siluq on last word classified as melekh" do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) siluq = Enum.find(context.teamim_marks, fn m -> m.taam == "meteg_siluq" end) assert siluq != nil assert siluq.word_index == 6 assert siluq.role == "melekh" end test "havdalah follows the text's own divisions, not imposed ones" do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) input = %{ source: "test", verse_scope: @scope, content: "בראשית ברא אלהים את השמים ואת הארץ" } # Run through tohu → vayomer → havdalah result = input |> BereishitCycle.tohu(context) |> BereishitCycle.vayomer(context) |> BereishitCycle.havdalah(context) # Havdalah boundaries must match melekh positions from te'amim melekh_positions = context.teamim_marks |> Enum.filter(fn m -> m.role == "melekh" end) |> Enum.map(fn m -> m.word_index end) |> Enum.sort() assert result.separation_boundaries == melekh_positions # System used text's divisions (etnachta at 2, meteg_siluq at 6) assert 2 in result.separation_boundaries assert 6 in result.separation_boundaries end end # ============================================================ # Test 2: Ketiv/Qere Preservation (Architecture §6.3) # ============================================================ describe "Shema Test 2: Ketiv/Qere Preservation" do setup do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) # Inject synthetic ketiv/qere (Bereishit 1:1 has none) ketiv_qere = [ %{ word_index: 3, ketiv: "את", qere: "אות", both_preserved: true } ] context = %{context | ketiv_qere: ketiv_qere} input = %{ source: "test", verse_scope: @scope, content: "בראשית ברא אלהים את השמים ואת הארץ" } {:ok, context: context, input: input, ketiv_qere: ketiv_qere} end test "context contains both readings", %{context: context, ketiv_qere: ketiv_qere} do assert context.ketiv_qere == ketiv_qere kq = hd(context.ketiv_qere) assert kq.ketiv == "את" assert kq.qere == "אות" end test "ketiv/qere preserved through vayomer", %{context: context, input: input} do result = input |> BereishitCycle.tohu(context) |> BereishitCycle.vayomer(context) # vayomer copies ketiv_qere from context into state assert result.ketiv_qere == context.ketiv_qere end test "ki_tov evaluation preserves both readings", %{context: context, input: input} do result = input |> BereishitCycle.tohu(context) |> BereishitCycle.vayomer(context) |> BereishitCycle.havdalah(context) |> BereishitCycle.kriah(context) named_results = result.named_results # Specification.evaluate receives the ketiv_qere list evaluation = Specification.evaluate(named_results, context.ketiv_qere) # With both_preserved: true, evaluation should be :tov (not lo_tov) assert evaluation == :tov end test "collapsed ketiv/qere triggers lo_tov" do # Simulate a collapsed dual reading collapsed_kq = [%{word_index: 3, ketiv: "את", qere: "את", both_preserved: false}] results = [%{shoresh: "ברא"}] evaluation = Specification.evaluate(results, collapsed_kq) assert {:lo_tov, reason} = evaluation assert reason =~ "ketiv/qere" end end # ============================================================ # Test 3: Cloud Obedience (Architecture §6.3) # ============================================================ describe "Shema Test 3: Cloud Obedience" do test "Shabbat cloud state prevents processing" do {:ok, shabbat, _} = DateTime.from_iso8601("2026-02-28T10:00:00Z") {:ok, context} = Shema.receive_context_at(@scope, shabbat) # Context reports major_transition assert {:major_transition, :shabbat_rest, _} = context.cloud_state end test "pipeline returns {:transition, :shabbat_rest} on Shabbat — does NOT process" do {:ok, shabbat, _} = DateTime.from_iso8601("2026-02-28T10:00:00Z") {:ok, context} = Shema.receive_context_at(@scope, shabbat) # Simulate what VavPipeline.process/1 does after context reception result = case context.cloud_state do :moving -> {:transition, :cloud_lifted} {:major_transition, signal, _params} -> {:transition, signal} :cloud_not_visible -> {:waiting, :cloud_not_visible} :camped -> :would_process end assert {:transition, :shabbat_rest} = result end test "festival cloud state prevents processing — Pesach" do # 15 Nisan 5787 = 2 April 2026 {:ok, pesach, _} = DateTime.from_iso8601("2026-04-02T12:00:00Z") {:ok, context} = Shema.receive_context_at(@scope, pesach) assert {:major_transition, :festival_rest, %{festival: :pesach}} = context.cloud_state # Pipeline would return {:transition, :festival_rest} result = case context.cloud_state do {:major_transition, signal, _params} -> {:transition, signal} _ -> :would_process end assert {:transition, :festival_rest} = result end test "cloud_not_visible triggers waiting" do # Manually construct context with cloud_not_visible {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) context = %{context | cloud_state: :cloud_not_visible} result = case context.cloud_state do :cloud_not_visible -> {:waiting, :cloud_not_visible} _ -> :would_process end assert {:waiting, :cloud_not_visible} = result end end # ============================================================ # Test 4: Manna Fidelity (Architecture §6.3) # ============================================================ describe "Shema Test 4: Manna Fidelity" do test "no provision → pipeline returns {:waiting, :no_provision}" do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) # No provision file exists in test environment assert context.daily_provision == :no_provision # Simulate VavPipeline.process decision result = case context.daily_provision do :no_provision -> {:waiting, :no_provision} _provision -> :would_process end assert {:waiting, :no_provision} = result end test "with provision → pipeline proceeds" do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) # Inject valid provision provision = %{ date: "2026-02-25", provision: %{ compute_units: 1000, memory_mb: 32768, api_calls_external: 500, storage_delta_mb: 100, sanhedrin_panels_max: 5 }, type: :regular, expires_at: DateTime.utc_now() |> DateTime.add(3600), written_by: "test", written_at: DateTime.utc_now() } context = %{context | daily_provision: provision} result = case context.daily_provision do :no_provision -> {:waiting, :no_provision} _provision -> :would_process end assert result == :would_process end end # ============================================================ # Test 5: Context Immutability (Architecture §6.3) # ============================================================ describe "Shema Test 5: Context Immutability" do test "ShemaContext is identical at every pipeline stage" do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) input = %{ source: "test", verse_scope: @scope, content: "בראשית ברא אלהים את השמים ואת הארץ" } # Capture context hash before processing ctx_hash = :erlang.phash2(context) # Stage 1: tohu state1 = BereishitCycle.tohu(input, context) assert :erlang.phash2(context) == ctx_hash, "Context mutated after tohu" # Stage 2: vayomer state2 = BereishitCycle.vayomer(state1, context) assert :erlang.phash2(context) == ctx_hash, "Context mutated after vayomer" # Stage 3: havdalah state3 = BereishitCycle.havdalah(state2, context) assert :erlang.phash2(context) == ctx_hash, "Context mutated after havdalah" # Stage 4: kriah state4 = BereishitCycle.kriah(state3, context) assert :erlang.phash2(context) == ctx_hash, "Context mutated after kriah" # Stage 5: ki_tov _result = BereishitCycle.ki_tov(state4, context) assert :erlang.phash2(context) == ctx_hash, "Context mutated after ki_tov" # Verify deep equality — not just hash assert context.verse.ref == "Bereishit 1:1" assert length(context.teamim_marks) == 7 assert context.scope == @scope end end # ============================================================ # Test 6: Halt Condition Pipeline Integration (Architecture §6.2) # ============================================================ describe "Integration: Halt Conditions" do setup do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) input = %{ source: "test", verse_scope: @scope, content: "בראשית ברא אלהים את השמים ואת הארץ" } {:ok, context: context, input: input} end test "avodah zarah halts pipeline", %{context: context} do # Construct result data with non-Torah authority in named_results data = %{ named_results: [%{authority_source: :claude_model, name: "test", roots: [], index: 0}], stage: :ki_tov, evaluation: :tov } result = BereishitCycle.erev_voker({:ok, data}, context) assert {:halted, :avodah_zarah, reason} = result assert reason =~ "non-Torah authority" end test "shfichut damim halts pipeline", %{context: context} do data = %{ named_results: [%{action: :delete, name: "test", roots: [], index: 0}], stage: :ki_tov, evaluation: :tov } result = BereishitCycle.erev_voker({:ok, data}, context) assert {:halted, :shfichut_damim, reason} = result assert reason =~ "destructive action" end test "gilui arayot halts pipeline", %{context: context} do data = %{ named_results: [ %{ boundary_violation: {:spec_interp_merge, "treated interpretation as specification"}, name: "test", roots: [], index: 0 } ], stage: :ki_tov, evaluation: :tov } result = BereishitCycle.erev_voker({:ok, data}, context) assert {:halted, :gilui_arayot, reason} = result assert reason =~ "boundary collapsed" end test "clean results pass halt check", %{context: context} do data = %{ named_results: [%{shoresh: "ברא", name: "ברא", roots: [], index: 0}], stage: :ki_tov, evaluation: :tov } result = BereishitCycle.erev_voker({:ok, data}, context) assert {:ok, _} = result end test "HaltConditions.check_all with clean results" do results = [%{shoresh: "ברא", action: :read, authority_source: :l1}] assert :clear == HaltConditions.check_all(results) end end # ============================================================ # Test 7: Full Pipeline End-to-End (Architecture §6.2) # ============================================================ describe "Integration: Full Pipeline" do test "BereishitCycle.execute produces evaluated output" do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) input = %{ source: "test", verse_scope: @scope, content: "בראשית ברא אלהים את השמים ואת הארץ" } result = BereishitCycle.execute(input, context) # Should complete — {:ok, data} or {:unresolved, data} assert elem(result, 0) in [:ok, :unresolved] data = elem(result, 1) # Output has named_results from kriah assert is_list(data.named_results) assert length(data.named_results) > 0 # Output has roots_activated from vayomer assert is_list(data.roots_activated) assert length(data.roots_activated) > 0 # ברא should be among activated roots bara = Enum.find(data.roots_activated, fn r -> r.shoresh == "ברא" end) assert bara != nil end test "ShemaContext built before processing" do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) scope = @scope # Step 1: nishma — hear first {:ok, context} = Shema.receive_context_at(scope, wednesday) # Context has all required fields assert context.verse != nil assert is_list(context.teamim_marks) assert is_list(context.ketiv_qere) assert is_map(context.root_gematria) assert context.cloud_state != nil assert context.daily_provision != nil assert context.scope == scope assert context.received_at != nil end test "pipeline does NOT produce {:halted, ...} on clean Bereishit 1:1 input" do {:ok, wednesday, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, context} = Shema.receive_context_at(@scope, wednesday) input = %{ source: "l1", verse_scope: @scope, content: "בראשית ברא אלהים את השמים ואת הארץ" } result = BereishitCycle.execute(input, context) # Must NOT be halted — Bereishit 1:1 is clean Torah text refute match?({:halted, _, _}, result) end end end