defmodule VavPipeline.Day6ServingTest do @moduledoc """ Kohen Day 6 (2026-09-07) — Sh'mirat HaMishmeret — the vessel ran and did not serve. Five defects, each found by RUNNING the cycle on a live pod, none visible to the unit tests, because every unit test ran the L1 fallback (no Port in the test VM) and none ran the cycle to L3. "Unit tests prove the vessel exists. Only the cycle proves it serves." So this file does what the suite never did: it registers a process under the REAL name `VavPipeline.ShoreshEnginePort` that answers the way the Haskell engine answers — string keys, no `position`, nothing at all for non-Hebrew input — and drives the whole path from HTTP request to cycle result. The engine's shape is the one the kohen captured from the pod, verbatim. Each describe names the defect it holds shut. """ use ExUnit.Case, async: false use Plug.Test alias VavPipeline.{BereishitCycle, Shema, Specification, MannaLedger} alias VavPipeline.BigdeiKodesh.Router alias VavPipeline.L3.{CouplingTrace, Interpretation, TeikuRecord} @opts Router.init([]) @scope %{book: "bereishit", chapter: 1, verse: 1} # A Wednesday at noon — not Shabbat, not a festival. @wednesday_noon "2026-02-25T12:00:00Z" # The engine's answer for ברא, exactly as the pod delivered it over the # Port on Day 6. Note what is NOT here: `position`. @engine_bara %{ "shoresh" => ["bet", "resh", "aleph"], "shoresh_hebrew" => "ברא", "gematria_value" => 203, "binyan" => "Qal", "domain" => "unknown", "torah_refs" => [], "neighbors" => [], "gematria_neighbors" => [] } # ============================================================ # A stand-in for the Haskell engine, under its real name # ============================================================ # # ShoreshEngineClient checks Process.whereis(VavPipeline.ShoreshEnginePort) # and then calls VavPipeline.ShoreshEnginePort.process/2, which does # GenServer.call(__MODULE__, {:request, json_line}). Registering THIS # process under that name puts the real module's client code on the # path and only replaces the port itself. It answers like the engine: # Hebrew in → the ברא root; anything else → no roots. defmodule FakePort do use GenServer def start, do: GenServer.start(__MODULE__, %{}, name: VavPipeline.ShoreshEnginePort) @impl true def init(s), do: {:ok, s} @impl true def handle_call({:request, json_line}, _from, s) do text = json_line |> Jason.decode!() |> get_in(["input", "text"]) || "" roots = if String.match?(text, ~r/[\x{05D0}-\x{05EA}]/u) do [ %{ "shoresh" => ["bet", "resh", "aleph"], "shoresh_hebrew" => "ברא", "gematria_value" => 203, "binyan" => "Qal", "domain" => "unknown", "torah_refs" => [], "neighbors" => [], "gematria_neighbors" => [] } ] else [] end {:reply, {:ok, %{"roots_activated" => roots, "parse_tree" => nil, "dual_readings" => []}}, s} end end defp with_fake_port(fun) do case Process.whereis(VavPipeline.ShoreshEnginePort) do nil -> {:ok, pid} = FakePort.start() try do fun.() after GenServer.stop(pid) end _real -> # A real engine is registered on this host; the test is still # meaningful against it. fun.() end end defp context do {:ok, now, _} = DateTime.from_iso8601(@wednesday_noon) {:ok, ctx} = Shema.receive_context_at(@scope, now) ctx end # The ledger is a global GenServer. Every test that loads or consumes # from it must leave it as it found it — an exhausted ledger leaked # into an older test (step31, Test 4) reads :already_loaded, refuses # the fresh provision that test writes, and the cycle waits. defp write_provision(compute_units) do MannaLedger.reset() # The serving path answers 503 while a halt from another file stands. if VavPipeline.HaltMonitor.halted?(), do: VavPipeline.HaltMonitor.resolve("day6 serving test setup") 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" => "day6_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)) path end # WLC text carries vowels and cantillation interleaved in canonical # Unicode order (dagesh before sheva, etc.); compare consonants only. defp consonants(text), do: String.replace(text, ~r/[\x{0591}-\x{05C7}]/u, "") defp post_ask(question) do conn(:post, "/peah/ask", Jason.encode!(%{question: question})) |> put_req_header("content-type", "application/json") |> Router.call(@opts) end # ============================================================ # Defect 1 — real engine output crashed havdalah # ============================================================ describe "Defect 1: engine-shaped roots (no position) through havdalah" do test "normalize_root anchors an engine root to the verse from the CONTEXT" do ctx = context() root = BereishitCycle.normalize_root(@engine_bara, ctx, :engine) assert root.shoresh == "ברא" assert root.shoresh_letters == ["bet", "resh", "aleph"] assert root.gematria == 203 # ברא is the second word of Bereishit 1:1 — the ShemaContext says so. assert root.positions == [2] assert root.position == 2 assert root.source == :engine assert root.engine == @engine_bara end test "havdalah → kriah → ki_tov do not crash on the pod's exact payload" do ctx = context() state = %{source: "test", verse_scope: @scope, content: "בראשית ברא אלהים"} |> BereishitCycle.tohu(ctx) |> Map.merge(%{ roots_activated: [BereishitCycle.normalize_root(@engine_bara, ctx, :engine)], root_source: :engine, teamim_parse: ctx.teamim_marks, ketiv_qere: ctx.ketiv_qere, stage: :vayomer }) # This is the exact call that raised KeyError :position on Day 6. result = state |> BereishitCycle.havdalah(ctx) |> BereishitCycle.kriah(ctx) |> BereishitCycle.ki_tov(ctx) assert {:ok, data} = result assert data.evaluation == :tov assert [%{shoresh: "ברא", name: "ברא", positions: [2]}] = data.named_results end test "a root the verse does not contain is kept apart, not dropped, not a crash" do ctx = context() stranger = %{@engine_bara | "shoresh_hebrew" => "שמר", "shoresh" => ["shin", "mem", "resh"]} roots = [ BereishitCycle.normalize_root(@engine_bara, ctx, :engine), BereishitCycle.normalize_root(stranger, ctx, :engine) ] assert Enum.at(roots, 1).positions == [] assert Enum.at(roots, 1).position == nil result = %{roots_activated: roots} |> BereishitCycle.havdalah(ctx) |> BereishitCycle.kriah(ctx) names = Enum.map(result.named_results, & &1.name) assert "ברא" in names assert "שמר" in names # The unplaced root forms the trailing group. assert List.last(result.named_results).name == "שמר" end test "L1-fallback roots pass through the same normalization (one shape)" do ctx = context() state = %{source: "test", verse_scope: @scope, content: "בראשית ברא אלהים"} |> BereishitCycle.tohu(ctx) |> BereishitCycle.vayomer(ctx) # Every root — whichever source produced it — has the one shape. Enum.each(state.roots_activated, fn r -> assert is_binary(r.shoresh) assert is_list(r.positions) assert Map.has_key?(r, :position) assert r.source in [:engine, :l1] end) bara = Enum.find(state.roots_activated, &(&1.shoresh == "ברא")) assert bara.gematria == 203 assert 2 in bara.positions end end # ============================================================ # Defect 2 — emptiness was graded tov # ============================================================ describe "Defect 2: no roots is lo tov, not 'Processing complete'" do test "zero roots → zero groups → zero named results → rejected" do ctx = context() result = %{roots_activated: [], raw: "What is bereishit?", case_ref: "t"} |> BereishitCycle.havdalah(ctx) |> BereishitCycle.kriah(ctx) assert result.separated_groups == [] assert result.named_results == [] assert {:rejected, reason} = BereishitCycle.ki_tov(result, ctx) assert reason =~ "empty results" end test "the Day-6 shape — a nameless group with no :shoresh — is lo tov, not tov" do # This is literally what kriah emitted on the pod. assert {:lo_tov, reason} = Specification.evaluate([%{name: "group_0", roots: [], index: 0}], []) assert reason =~ "shoresh" end test "a halt marker is still a halt even when the result has no shoresh" do # Yehareg v'al ya'avor — shape never masks a cardinal sin. results = [%{authority_source: :external_model}] assert {:lo_tov, "halt condition triggered"} = Specification.evaluate(results, []) end test "all_roots_grounded? no longer grounds by default" do refute Specification.all_roots_grounded?([%{name: "group_0"}]) refute Specification.all_roots_grounded?([%{shoresh: nil}]) refute Specification.all_roots_grounded?([%{shoresh: ""}]) assert Specification.all_roots_grounded?([%{shoresh: "ברא"}]) end test "a rejected cycle records nothing to L3 (BUILD_KORBAN.md:123)" do assert :none = BereishitCycle.l3_attrs({:rejected, "empty results"}, context()) assert :none = BereishitCycle.l3_attrs({:halted, :avodah_zarah, "x"}, context()) end end # ============================================================ # The public ring, end to end, against an engine-shaped Port # ============================================================ describe "/peah/ask against an engine that answers like the pod's" do setup do path = write_provision(50) on_exit(fn -> File.rm(path) end) :ok end test "Hebrew: 200 with a real answer, the verse as source, ב-ר-א consulted" do with_fake_port(fn -> conn = post_ask("בראשית ברא אלהים") assert conn.status == 200, conn.resp_body body = Jason.decode!(conn.resp_body) refute body["answer"] == "Processing complete" assert body["answer"] =~ "ב-ר-א" assert body["answer"] =~ "203" assert [source | _] = body["sources"] assert source["book"] == "bereishit" or source["book"] == "Bereishit" assert source["chapter"] == 1 assert source["verse"] == 1 assert is_binary(source["text"]) and String.contains?(consonants(source["text"]), "בראשית") assert "ב-ר-א" in body["processing"]["roots_consulted"] assert body["processing"]["cycle_phase"] == "voker" assert Map.has_key?(body, "disclosure") end) end test "English: 422 chatat that says what cannot yet be done, with the verse offered" do with_fake_port(fn -> conn = post_ask("What is bereishit?") assert conn.status == 422, conn.resp_body body = Jason.decode!(conn.resp_body) err = body["error"] assert err["code"] == "TEIKU_NO_ROOTS" assert err["type"] == "unresolvable" assert is_binary(err["message"]) and err["message"] != "" assert err["guidance"]["resubmit"] == true assert err["guidance"]["correction"] =~ "Step 22" # The corner of the field: the verse in scope, in Hebrew. sv = err["guidance"]["scope_verse"] assert sv["chapter"] == 1 and sv["verse"] == 1 assert String.contains?(consonants(sv["text"]), "בראשית") # Never "Processing complete" with sources: [] and a 200. refute Map.has_key?(body, "answer") assert Map.has_key?(body, "disclosure") end) end test "manna is consumed per cycle and NOT zeroed per request" do with_fake_port(fn -> today = Date.utc_today() |> Date.to_iso8601() assert post_ask("בראשית ברא אלהים").status == 200 l1 = MannaLedger.ledger() assert l1.cycle_date == today assert l1.consumed.compute_units == 1 assert post_ask("בראשית ברא אלהים").status == 200 l2 = MannaLedger.ledger() assert l2.cycle_date == today # Second request took a second unit. Before the fix, erev_voker # zeroed the ledger after every request — this would read 1 # again (a fresh full day's provision each time). assert l2.consumed.compute_units == 2 assert l2.allocated.compute_units == 50 end) end test "provision exhausted → 503 MANNA_DEPLETED, not a crash" do path = write_provision(1) on_exit(fn -> File.rm(path) end) with_fake_port(fn -> assert post_ask("בראשית ברא אלהים").status == 200 conn = post_ask("בראשית ברא אלהים") assert conn.status == 503 assert Jason.decode!(conn.resp_body)["error"]["code"] == "MANNA_DEPLETED" end) end end # ============================================================ # Defect 3 — the pipeline never wrote L3 # ============================================================ describe "Defect 3: the cycle produces a valid L3 record" do test "a served cycle yields a valid Interpretation with L1-grounded couplings" do ctx = context() {:ok, data} = %{source: "test", verse_scope: @scope, content: "בראשית ברא אלהים"} |> BereishitCycle.tohu(ctx) |> BereishitCycle.vayomer(ctx) |> BereishitCycle.havdalah(ctx) |> BereishitCycle.kriah(ctx) |> BereishitCycle.ki_tov(ctx) assert {:interpretation, attrs, nil} = BereishitCycle.l3_attrs({:ok, data}, ctx) assert attrs.status == "accepted" assert attrs.panel_size == 1 assert attrs.vote_count_for == 1 assert attrs.vote_count_against == 0 assert attrs.processing_component == "bereishit_cycle" assert "ברא" in attrs.roots_involved assert Enum.any?(attrs.torah_references, &String.contains?(&1, "1:1")) assert attrs.case_ref =~ ~r/^cycle-\d+-[0-9a-f]{6}$/ assert String.length(attrs.shema_context_hash) == 64 # Every coupling is the L1-anchored shape the write gate accepts. assert attrs.coupling_trace != [] assert :ok = CouplingTrace.validate(attrs.coupling_trace) assert Enum.any?(attrs.coupling_trace, &(&1 =~ ~r/^coupled\(root\(ברא\), verse\(bereishit, 1, 1\)\)$/)) # And the schema itself accepts it — no missing required field. changeset = Interpretation.changeset(%Interpretation{}, attrs) assert changeset.valid?, inspect(changeset.errors) end test "an unresolved cycle yields a valid TeikuRecord" do ctx = context() data = %{ case_ref: "cycle-1-abcdef", roots_activated: [BereishitCycle.normalize_root(@engine_bara, ctx, :engine)], evaluation: :teiku, talmudic_interpretation: %{ "status" => "teiku", "interpretation" => %{"text" => "Talmudic Engine not connected — teiku", "verses" => []}, "challenge" => nil } } assert {:teiku, attrs} = BereishitCycle.l3_attrs({:unresolved, data}, ctx) assert attrs.case_ref == "cycle-1-abcdef" assert attrs.tension_description =~ "ברא" assert attrs.interpretation_a =~ "teiku" assert is_binary(attrs.interpretation_b) changeset = TeikuRecord.changeset(%TeikuRecord{}, attrs) assert changeset.valid?, inspect(changeset.errors) end test "a Sanhedrin-accepted cycle carries the panel and preserves the minority" do ctx = context() data = %{ case_ref: "cycle-2-abcdef", roots_activated: [BereishitCycle.normalize_root(@engine_bara, ctx, :engine)], resolution: :sanhedrin, interpretation: "Majority reading", sanhedrin_decision: %{ outcome: :accepted, panel_size: 3, majority: %{position: "A", reasoning: "Because Bereishit 1:1", torah_refs: ["Bereishit 1:1"], vote_count: 2}, minority: %{position: "B", reasoning: "But Shemot 20:11", torah_refs: ["Shemot 20:11"], vote_count: 1} } } assert {:interpretation, attrs, minority} = BereishitCycle.l3_attrs({:ok, data}, ctx) assert attrs.panel_size == 3 assert attrs.vote_count_for == 2 assert attrs.vote_count_against == 1 assert attrs.processing_component == "sanhedrin" assert minority.dissenting_reasoning == "But Shemot 20:11" assert minority.torah_references == ["Shemot 20:11"] end @tag :l3 test "a live cycle appends to interpretations (PostgreSQL)" do :ok = Ecto.Adapters.SQL.Sandbox.checkout(VavPipeline.Repo) path = write_provision(5) on_exit(fn -> File.rm(path) end) before = VavPipeline.L3.Store.count_interpretations() with_fake_port(fn -> assert {:ok, _} = VavPipeline.process(%{source: "peah", verse_scope: @scope, content: "בראשית ברא אלהים"}) end) assert VavPipeline.L3.Store.count_interpretations() == before + 1 end end # ============================================================ # Defect 4 — errors are chatat, never an empty 500 # ============================================================ describe "Defect 4: every error is a chatat" do test "a raise on the public path returns the tamid §1.2 body, then re-raises" do # Plug.Parsers raises on a body that is not JSON. That raise used to # reach the socket as an empty response. Now handle_errors/2 sends # the chatat body first; Plug.ErrorHandler then re-raises the # original exception so the crash is still logged and supervised. conn = conn(:post, "/peah/ask", "{this is not json") |> put_req_header("content-type", "application/json") assert_raise Plug.Parsers.ParseError, fn -> Router.call(conn, @opts) end assert {status, _headers, body} = sent_resp(conn) assert status == 400 err = Jason.decode!(body)["error"] assert err["code"] == "KIYOR_MALFORMED_REQUEST" assert err["type"] == "purification_needed" assert is_binary(err["message"]) assert err["guidance"]["resubmit"] == true assert err["guidance"]["correction"] =~ "Content-Type" end test "an unexpected exception is CHATAT_UNEXPECTED with guidance and no stack" do conn = conn(:post, "/peah/ask") |> Plug.Conn.put_status(500) |> Router.handle_errors(%{kind: :error, reason: %RuntimeError{message: "boom"}, stack: []}) assert conn.status == 500 err = Jason.decode!(conn.resp_body)["error"] assert err["code"] == "CHATAT_UNEXPECTED" assert err["type"] == "correction_needed" assert err["message"] =~ "fault is the system's" assert err["guidance"]["issue"] =~ "RuntimeError: boom" refute conn.resp_body =~ "stacktrace" end test "{:rejected, _} from the cycle is handled — no CaseClauseError" do # Before: process_peah had no clause for {:rejected, reason}, so a # lo_tov result was itself a crash. The English test above proves # the positive path; this pins the courtyard handler too. path = write_provision(5) on_exit(fn -> File.rm(path) end) with_fake_port(fn -> conn = conn(:post, "/courtyard/offering", Jason.encode!(%{content: "What is bereishit?", offering_type: "minchah"})) |> put_req_header("content-type", "application/json") |> put_req_header("authorization", "Bearer test-token") |> Router.call(@opts) assert conn.status == 422 assert Jason.decode!(conn.resp_body)["error"]["code"] == "TEIKU_NO_ROOTS" end) end end # ============================================================ # Defect 5 — health must see the serving path # ============================================================ describe "Defect 5: esh tamid pushes a real input" do test "tamid_probe runs the real cycle, is marked probe, and is not recorded" do assert {:ok, data} = VavPipeline.tamid_probe() assert data.probe == true assert data.provenance == "tamid" assert Enum.any?(data.roots_activated, &(&1.shoresh == "ברא")) # A probe builds a valid record and the write path skips it — # the attrs are still sound, the ledger and L3 are untouched. assert {:interpretation, _attrs, nil} = BereishitCycle.l3_attrs({:ok, data}, context()) end test "tamid_probe does not consume manna" do path = write_provision(5) on_exit(fn -> File.rm(path) end) MannaLedger.load_provision() before = MannaLedger.ledger().consumed.compute_units assert {:ok, _} = VavPipeline.tamid_probe() assert MannaLedger.ledger().consumed.compute_units == before end test "/kodesh/health reports esh_tamid from the cycle and lists l3_writable" do conn = conn(:get, "/kodesh/health") |> Router.call(@opts) body = Jason.decode!(conn.resp_body) # Runs the probe unless it is Shabbat right now. assert body["checks"]["esh_tamid"] in ["pass", "shabbat"] assert Map.has_key?(body["checks"], "l3_writable") assert Map.has_key?(body["checks"], "manna_present") end test "manna_present fails when there is no provision (Day-4 finding)" do File.rm(VavPipeline.Manna.provision_file()) conn = conn(:get, "/kodesh/health") |> Router.call(@opts) assert Jason.decode!(conn.resp_body)["checks"]["manna_present"] == "fail" end test "/kodesh/status exposes the ledger the kohen was reading by hand" do conn = conn(:get, "/kodesh/status") |> Router.call(@opts) body = Jason.decode!(conn.resp_body) assert Map.has_key?(body["manna_provision"], "cycle_date") assert Map.has_key?(body["manna_provision"], "remaining") end end end