defmodule VavPipeline.Tablet32ReferenceTest do @moduledoc """ Tablet 32 (2026-10-04) — `devarim 6:5` on the live pod: 422 TEIKU_NO_ROOTS. The scope was heard (tablet 30), but the Shoresh Engine activates roots from the text it is handed, it was handed the question, and a bare reference has no Hebrew in it. `tablet30_scope_test` asserted "an explicit reference is honoured" and passed — on the L1 fallback, which reads the scoped verse and never looks at the question. The Day-6 pattern, again: green here, broken where the engine is real. So this file does not run the fallback. Like `day6_serving_test`, it registers a process under the REAL name `VavPipeline.ShoreshEnginePort` that behaves the way the engine behaves — roots only from Hebrew it is given — and it reports back exactly what text it was given. """ use ExUnit.Case, async: false import Plug.Test import Plug.Conn alias VavPipeline.{HaltMonitor, L1Reader, MannaLedger} alias VavPipeline.BigdeiKodesh.Router @opts Router.init([]) # ============================================================ # An engine-shaped stand-in that tells the test what it was handed # ============================================================ defmodule SpyPort do use GenServer def start(test_pid), do: GenServer.start(__MODULE__, test_pid, name: VavPipeline.ShoreshEnginePort) @impl true def init(test_pid), do: {:ok, test_pid} @impl true def handle_call({:request, json_line}, _from, test_pid) do text = json_line |> Jason.decode!() |> get_in(["input", "text"]) || "" send(test_pid, {:engine_received, text}) roots = cond do # Devarim 6:5 opens וְאָהַבְתָּ — the engine would find א-ה-ב. String.contains?(text, "ואהבת") -> [root(["aleph", "he", "bet"], "אהב", 8)] String.match?(text, ~r/[\x{05D0}-\x{05EA}]/u) -> [root(["bet", "resh", "aleph"], "ברא", 203)] true -> [] end {:reply, {:ok, %{"roots_activated" => roots, "parse_tree" => nil, "dual_readings" => []}}, test_pid} end # The engine's root shape as the pod delivers it: string keys, no `position`. defp root(letters, hebrew, gematria) do %{ "shoresh" => letters, "shoresh_hebrew" => hebrew, "gematria_value" => gematria, "binyan" => "Qal", "domain" => "unknown", "torah_refs" => [], "neighbors" => [], "gematria_neighbors" => [] } end end setup do if HaltMonitor.halted?(), do: HaltMonitor.resolve("tablet32 test setup") write_provision(50) # This file is about the real-engine path. If a real engine is # registered on this host there is nothing to stand in for. assert Process.whereis(VavPipeline.ShoreshEnginePort) == nil, "a real ShoreshEnginePort is registered; this test needs to spy on the port" {:ok, pid} = SpyPort.start(self()) on_exit(fn -> if Process.alive?(pid), do: GenServer.stop(pid) end) :ok end describe "a named verse, no Hebrew in the question" do test "the engine is handed the verse's own words, and the ger is answered" do conn = post_ask("devarim 6:5") body = Jason.decode!(conn.resp_body) {:ok, verse} = L1Reader.read_verse("devarim", 6, 5) verse_text = Enum.map_join(verse.words, " ", & &1.consonants) assert_received {:engine_received, ^verse_text} assert verse_text =~ "ואהבת" assert conn.status == 200, "#{conn.status} #{conn.resp_body}" assert [%{"ref" => "Devarim 6:5"}] = body["sources"] assert body["processing"]["scope"]["resolved_by"] == "verse_reference" assert body["processing"]["scope"]["roots_read_from"] == "verse" assert body["processing"]["roots_consulted"] != [] assert body["answer"] =~ "Devarim 6:5" end test "English around the reference changes nothing" do conn = post_ask("What does Deuteronomy 6:5 command?") body = Jason.decode!(conn.resp_body) assert_received {:engine_received, text} assert text =~ "ואהבת" assert conn.status == 200 assert body["processing"]["scope"]["roots_read_from"] == "verse" end end describe "what must NOT be handed the verse" do test "no reference, no Hebrew: the default scope earns no reading — still a teiku" do conn = post_ask("Test query 1") body = Jason.decode!(conn.resp_body) assert_received {:engine_received, "Test query 1"} assert conn.status == 422 assert body["error"]["code"] == "TEIKU_NO_ROOTS" end test "quoted Torah: the engine reads the asker's own words" do conn = post_ask("ויאמר אלהים יהי אור") body = Jason.decode!(conn.resp_body) assert_received {:engine_received, "ויאמר אלהים יהי אור"} assert conn.status == 200 assert body["processing"]["scope"]["resolved_by"] == "text_match" assert body["processing"]["scope"]["roots_read_from"] == "question" end test "a reference WITH Hebrew: the asker's Hebrew is what is read" do q = "devarim 6:5 — ואהבת" conn = post_ask(q) body = Jason.decode!(conn.resp_body) assert_received {:engine_received, ^q} assert conn.status == 200 assert body["processing"]["scope"]["resolved_by"] == "verse_reference" assert body["processing"]["scope"]["roots_read_from"] == "question" end end # ------------------------------------------------------------ # Helpers (shape from day6_serving_test.exs / tablet30_scope_test.exs) # ------------------------------------------------------------ defp write_provision(compute_units) do MannaLedger.reset() on_exit(fn -> MannaLedger.reset() end) today = Date.utc_today() |> Date.to_iso8601() expires = DateTime.utc_now() |> DateTime.add(6 * 3600) |> DateTime.to_iso8601() provision = %{ "date" => today, "provision" => %{ "compute_units" => compute_units, "memory_mb" => 512, "api_calls_external" => 5, "storage_delta_mb" => 2, "sanhedrin_panels_max" => 2 }, "type" => "regular", "expires_at" => expires, "written_by" => "tablet32_test", "written_at" => DateTime.utc_now() |> DateTime.to_iso8601() } path = VavPipeline.Manna.provision_file() File.mkdir_p!(Path.dirname(path)) File.write!(path, Jason.encode!(provision)) on_exit(fn -> File.rm(path) end) path end defp post_ask(question) do conn(:post, "/peah/ask", Jason.encode!(%{question: question})) |> put_req_header("content-type", "application/json") |> Router.call(@opts) end end