Every Hebrew question was answered against Bereishit 1:1 — extract_scope_from_question/1 ignored its argument. ויאמר אלהים יהי אור (1:3) and ויהי ערב ויהי בקר (1:5) came back sourced from 1:1 with roots "not in this verse". The grounding check passed by construction. - L1Index: inverted index consonants → verses over l1/torah, built in handle_continue right after start (5853 verses, ~1 s), read-only after. - ScopeResolver: explicit reference (bereishit/Genesis/בראשית 1:3) first; then quoted Torah — most question words in a verse, ties to the verse where they stand together in order, then Torah order; else default. A reference to a verse L1 lacks is a 422 KIYOR_VALIDATION_FAILED with the correction, not a silent 1:1. - Router: processing.scope reports verse + resolved_by; a default scope carries a note saying the verse was assumed, not heard. - test/tablet30_scope_test.exs: the five Day-7 offerings through the real router land on 1:1, 1:1, 1:3, 1:5, 1:1; Shema → Devarim 6:4; reference, invalid reference, default confession. 14 tests. - KOHEN.md Day 7: what to expect in sources[0].ref. 454 tests, 0 failures on seeds 0 1 2 3 42 99. Seed 191243 fails LivenessTest before and after this change — pre-existing, not touched. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
220 lines
8.7 KiB
Elixir
220 lines
8.7 KiB
Elixir
defmodule VavPipeline.Tablet30ScopeTest do
|
|
@moduledoc """
|
|
Tablet 30 (kohen Day 7, 2026-10-04) — every Hebrew question was answered
|
|
against Bereishit 1:1. `ויאמר אלהים יהי אור` (1:3) and `ויהי ערב ויהי בקר`
|
|
(1:5) came back with `sources: [Bereishit 1:1]` and roots marked "not in
|
|
this verse". `extract_scope_from_question/1` ignored its argument.
|
|
|
|
These tests send the Day-7 offerings through the real router and demand
|
|
that the verse in the answer is the verse in the question.
|
|
"""
|
|
|
|
use ExUnit.Case, async: false
|
|
|
|
import Plug.Test
|
|
import Plug.Conn
|
|
|
|
alias VavPipeline.{HaltMonitor, L1Index, MannaLedger, ScopeResolver}
|
|
alias VavPipeline.BigdeiKodesh.Router
|
|
|
|
@opts Router.init([])
|
|
|
|
setup_all do
|
|
# The index builds in handle_continue; the first call waits for it.
|
|
{verses, words} = L1Index.stats()
|
|
assert verses == 5853, "L1Index built #{verses} verses, expected the WLC 5853"
|
|
assert words > 10_000
|
|
:ok
|
|
end
|
|
|
|
# ============================================================
|
|
# ScopeResolver — the three ways a question names its verse
|
|
# ============================================================
|
|
describe "hebrew_tokens/1" do
|
|
test "strips nikud and te'amim, splits maqaf, drops everything else" do
|
|
assert ScopeResolver.hebrew_tokens("וַֽיְהִי־אֽוֹר ׃") == ["ויהי", "אור"]
|
|
assert ScopeResolver.hebrew_tokens("What does בְּרֵאשִׁ֖ית mean?") == ["בראשית"]
|
|
assert ScopeResolver.hebrew_tokens("Test query 3") == []
|
|
end
|
|
end
|
|
|
|
describe "resolve/1 — quoted Torah" do
|
|
test "the Day-7 offerings land on their own verses" do
|
|
assert {:ok, %{book: "bereishit", chapter: 1, verse: 1}, :text_match} =
|
|
ScopeResolver.resolve("בראשית ברא אלהים")
|
|
|
|
assert {:ok, %{book: "bereishit", chapter: 1, verse: 1}, :text_match} =
|
|
ScopeResolver.resolve("את השמים ואת הארץ")
|
|
|
|
assert {:ok, %{book: "bereishit", chapter: 1, verse: 3}, :text_match} =
|
|
ScopeResolver.resolve("ויאמר אלהים יהי אור")
|
|
|
|
assert {:ok, %{book: "bereishit", chapter: 1, verse: 5}, :text_match} =
|
|
ScopeResolver.resolve("ויהי ערב ויהי בקר")
|
|
|
|
assert {:ok, %{book: "bereishit", chapter: 1, verse: 1}, :text_match} =
|
|
ScopeResolver.resolve("ברא אלהים")
|
|
end
|
|
|
|
test "pointed text resolves the same as bare consonants" do
|
|
assert {:ok, %{book: "bereishit", chapter: 1, verse: 3}, :text_match} =
|
|
ScopeResolver.resolve("וַיֹּ֥אמֶר אֱלֹהִ֖ים יְהִ֣י א֑וֹר")
|
|
end
|
|
|
|
test "reaches past Bereishit — the Shema" do
|
|
assert {:ok, %{book: "devarim", chapter: 6, verse: 4}, :text_match} =
|
|
ScopeResolver.resolve("שמע ישראל יהוה אלהינו יהוה אחד")
|
|
end
|
|
|
|
test "a phrase found in several verses goes to the earliest in Torah order" do
|
|
# שמע ישראל opens Devarim 5:1 before it opens 6:4.
|
|
assert {:ok, %{book: "devarim", chapter: 5, verse: 1}, :text_match} =
|
|
ScopeResolver.resolve("שמע ישראל")
|
|
end
|
|
|
|
test "Hebrew the Torah does not contain falls to the default, and says so" do
|
|
assert {:ok, %{book: "bereishit", chapter: 1, verse: 1}, :default} =
|
|
ScopeResolver.resolve("מחשב")
|
|
end
|
|
end
|
|
|
|
describe "resolve/1 — explicit reference" do
|
|
test "transliterated, English and Hebrew book names, colon or dot" do
|
|
assert {:ok, %{book: "bereishit", chapter: 1, verse: 3}, :verse_reference} =
|
|
ScopeResolver.resolve("What happens in bereishit 1:3?")
|
|
|
|
assert {:ok, %{book: "shemot", chapter: 3, verse: 14}, :verse_reference} =
|
|
ScopeResolver.resolve("Exodus 3.14")
|
|
|
|
assert {:ok, %{book: "devarim", chapter: 6, verse: 4}, :verse_reference} =
|
|
ScopeResolver.resolve("דברים 6:4")
|
|
end
|
|
|
|
test "a reference outranks quoted text" do
|
|
assert {:ok, %{book: "devarim", chapter: 6, verse: 4}, :verse_reference} =
|
|
ScopeResolver.resolve("devarim 6:4 — בראשית ברא אלהים")
|
|
end
|
|
|
|
test "a verse L1 does not have is a shogeg error, not a silent default" do
|
|
assert {:error, {:invalid_reference, "devarim 99:1"}} =
|
|
ScopeResolver.resolve("devarim 99:1")
|
|
end
|
|
end
|
|
|
|
describe "resolve/1 — nothing to go on" do
|
|
test "English defaults, and the resolution is :default" do
|
|
assert {:ok, %{book: "bereishit", chapter: 1, verse: 1}, :default} =
|
|
ScopeResolver.resolve("What is bereishit?")
|
|
end
|
|
end
|
|
|
|
# ============================================================
|
|
# Router — the verse in the answer is the verse in the question
|
|
# ============================================================
|
|
describe "POST /peah/ask" do
|
|
setup do
|
|
# halt_monitor_test leaves the monitor halted on some seeds; a halted
|
|
# vessel answers 503 before the resolver is reached. Clear it, as
|
|
# liveness_test does, so this file measures scope and nothing else.
|
|
if HaltMonitor.halted?(), do: HaltMonitor.resolve("tablet30 test setup")
|
|
write_provision(50)
|
|
:ok
|
|
end
|
|
|
|
test "Day-7 Hebrew offerings are sourced from their own verses" do
|
|
for {q, ref} <- [
|
|
{"ויאמר אלהים יהי אור", "Bereishit 1:3"},
|
|
{"ויהי ערב ויהי בקר", "Bereishit 1:5"},
|
|
{"בראשית ברא אלהים", "Bereishit 1:1"}
|
|
] do
|
|
conn = post_ask(q)
|
|
body = Jason.decode!(conn.resp_body)
|
|
|
|
assert conn.status == 200, "#{q}: #{conn.status} #{conn.resp_body}"
|
|
assert [%{"ref" => ^ref} | _] = body["sources"], "#{q} sourced from #{inspect(body["sources"])}"
|
|
assert body["processing"]["scope"]["resolved_by"] == "text_match"
|
|
assert body["processing"]["scope"]["verse"]["ref"] == ref
|
|
refute Map.has_key?(body["processing"]["scope"], "note")
|
|
end
|
|
end
|
|
|
|
test "an explicit reference is honoured — the error text promised it" do
|
|
conn = post_ask("bereishit 1:3")
|
|
body = Jason.decode!(conn.resp_body)
|
|
|
|
assert conn.status == 200
|
|
assert [%{"ref" => "Bereishit 1:3"}] = body["sources"]
|
|
assert body["processing"]["scope"]["resolved_by"] == "verse_reference"
|
|
end
|
|
|
|
test "a verse that does not exist is a 422 chatat, with the correction" do
|
|
conn = post_ask("bereishit 99:1")
|
|
body = Jason.decode!(conn.resp_body)
|
|
|
|
assert conn.status == 422
|
|
assert body["error"]["code"] == "KIYOR_VALIDATION_FAILED"
|
|
assert body["error"]["guidance"]["issue"] =~ "INVALID_SCOPE"
|
|
assert body["error"]["guidance"]["resubmit"] == true
|
|
end
|
|
|
|
test "a question with no verse in it confesses the default scope" do
|
|
# In the test VM there is no Haskell Port, so the L1 fallback grounds
|
|
# English against the default verse and answers 200 (day6_serving_test
|
|
# holds the real-engine 422 shut). Whatever the status, the scope
|
|
# report must say the verse was assumed, not heard.
|
|
conn = post_ask("What is bereishit?")
|
|
body = Jason.decode!(conn.resp_body)
|
|
|
|
scope =
|
|
case conn.status do
|
|
200 -> body["processing"]["scope"]
|
|
422 -> %{"resolved_by" => "default", "note" => "n/a", "verse" => body["error"]["guidance"]["scope_verse"]}
|
|
end
|
|
|
|
assert scope["resolved_by"] == "default"
|
|
assert scope["verse"]["ref"] == "Bereishit 1:1"
|
|
assert is_binary(scope["note"])
|
|
end
|
|
end
|
|
|
|
# ------------------------------------------------------------
|
|
# Helpers (shape from day6_serving_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" => "tablet30_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))
|
|
# Leave no provision behind: the Shema tests run at a fixed 2026-02-25
|
|
# and read a dated file as MANNA_STALE.
|
|
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
|