Files
bikoret/vav_pipeline/test/tablet30_scope_test.exs
T
Scott KarpandClaude Fable 5.1 c795225709 peah: hear the verse the question is standing in (kohen Day 7, tablet 30)
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>
2026-10-04 15:13:26 +00:00

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