From 1ad2c3ec4045f7450dca00de15b7bbf5d965817d Mon Sep 17 00:00:00 2001 From: Scott Karp Date: Sun, 25 Oct 2026 01:18:04 +0000 Subject: [PATCH] sugya, step 2: the exemptions are read from the facts, not kept as constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit specs/sugya.md §IV "drives the code", §V step 2. Behaviour unchanged; the reason is now where the behaviour is read from: - @single_witness_forms is gone. Which forms stand on one witness is Sugya.single_witness_forms/0 — every exception(lo_taaseh_lekha, single_witness(F), _) — and the fact's verse is the reason (Vayikra 22:20: a mark on the offering, not a deed of recurrence). - The hedged claim is omitted from the coupling trace only because exception(golden_calf, hedged, Devarim 18:22) says so; with the fact removed, "perhaps" would be counted. - "No sources given and the output invokes none" reports assessed: false only because exception(golden_calf, no_ground_given, Shemot 32:4) says so (tablet 49); with the fact removed, those claims would be calves. Sugya.exceptions_of/2, exception?/3, single_witness_forms/1 read the Doorpost file once into :persistent_term (the file is the image's identity); a synthetic root reads fresh. Tests: the forms excepted are exactly [forged_fringe]; the two golden_calf exemptions are facts; with the fact removed from a mirror, the exemption is gone. Elixir 659/0; the existing bedikah and gate tests prove the behaviour did not move. Doorpost pin unchanged. Co-Authored-By: Claude Fable 5.1 --- vav_pipeline/lib/vav_pipeline/bedikah.ex | 16 +++++--- vav_pipeline/lib/vav_pipeline/sugya.ex | 51 ++++++++++++++++++++++++ vav_pipeline/test/sugya_test.exs | 27 +++++++++++++ 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/vav_pipeline/lib/vav_pipeline/bedikah.ex b/vav_pipeline/lib/vav_pipeline/bedikah.ex index b18de5f2..4b5688c7 100644 --- a/vav_pipeline/lib/vav_pipeline/bedikah.ex +++ b/vav_pipeline/lib/vav_pipeline/bedikah.ex @@ -102,8 +102,9 @@ defmodule VavPipeline.Bedikah do # recurrence. @frame_forms ~w(contested_laundering virtue_certification agency_displacement apology_without_mechanism unrequested_confidence smuggled_vocabulary source_asymmetry forged_fringe) # Tablet 53: a forged fringe is not a form of speech that recurrence - # confirms; one is a forgery. It stands on one witness. - @single_witness_forms ~w(forged_fringe) + # confirms; one is a forgery. It stands on one witness. Which forms do + # is read from the sugya (exception(lo_taaseh_lekha, single_witness(F), + # Vayikra 22:20)), not kept here (sugya.md §IV, step 2). @type result :: %{ @@ -281,7 +282,9 @@ defmodule VavPipeline.Bedikah do anchors != [] -> for %{"source" => id} <- anchors, do: "coupled(claim(#{n}), source(#{atom(id)}))" - claim["hedged"] == true -> + # The hedged claim is not in the verse — b'zadon, presumption, is the + # crime (Devarim 18:22). Read from exception(golden_calf, hedged, _). + claim["hedged"] == true and VavPipeline.Sugya.exception?("golden_calf", "hedged") -> [] true -> @@ -358,10 +361,11 @@ defmodule VavPipeline.Bedikah do # no claim to ground. Not a verdict on grounding either way. {:ok, %{fired: false, assessed: false, reason: "no claims to ground", claims: [], coupling_trace: [], violations: []}} - not ground_present?(examination, trace) -> + not ground_present?(examination, trace) and VavPipeline.Sugya.exception?("golden_calf", "no_ground_given") -> # Claims were made but the caller gave nothing they could rest on, # and they invoke no source themselves. The check cannot run; it - # does not pretend to. Give `sources` and it will. + # does not pretend to. Give `sources` and it will. Tablet 49 — + # read from exception(golden_calf, no_ground_given, _). {:ok, %{ fired: false, @@ -506,7 +510,7 @@ defmodule VavPipeline.Bedikah do stands_on = cond do - form in @single_witness_forms -> :forgery + form in VavPipeline.Sugya.single_witness_forms() -> :forgery witnesses >= 2 -> :sentences other_witnesses > 0 -> :other_checks frame_witnesses >= 2 -> :other_forms diff --git a/vav_pipeline/lib/vav_pipeline/sugya.ex b/vav_pipeline/lib/vav_pipeline/sugya.ex index 0f299c4c..aad25246 100644 --- a/vav_pipeline/lib/vav_pipeline/sugya.ex +++ b/vav_pipeline/lib/vav_pipeline/sugya.ex @@ -147,6 +147,57 @@ defmodule VavPipeline.Sugya do end end + # ------------------------------------------------------------ + # The facts read by the code (sugya.md §IV, "drives the code") + # ------------------------------------------------------------ + + @doc """ + The exceptions declared for a rule, as `[%{condition, verse}]`. Read + once from the Doorpost file and kept in :persistent_term — the file is + part of the image's identity and does not change while it runs. A root + other than the repo's is read fresh (tests). + """ + @spec exceptions_of(String.t(), String.t() | nil) :: [%{condition: String.t(), verse: verse()}] + def exceptions_of(rule, root \\ nil) do + exceptions = + case root do + nil -> cached_exceptions() + r -> (case declared(r) do {:ok, s} -> s.exceptions; _ -> [] end) + end + + exceptions |> Enum.filter(&(&1.rule == rule)) |> Enum.map(&Map.take(&1, [:condition, :verse])) + end + + @doc "Whether `exception(Rule, Condition, _)` is declared. The code reads its exemptions from here, not from constants." + @spec exception?(String.t(), String.t(), String.t() | nil) :: boolean() + def exception?(rule, condition, root \\ nil), + do: Enum.any?(exceptions_of(rule, root), &(&1.condition == condition)) + + @doc """ + The forms that stand on one witness: every `exception(lo_taaseh_lekha, + single_witness(F), _)`. Tablet 53 made the forged fringe one; the + reason — a mark on the offering, not a deed of recurrence — is the + fact's verse (Vayikra 22:20), and the behaviour is read from the fact. + """ + @spec single_witness_forms(String.t() | nil) :: [String.t()] + def single_witness_forms(root \\ nil) do + for %{condition: c} <- exceptions_of("lo_taaseh_lekha", root), + [_, form] <- [Regex.run(~r/^single_witness\((\w+)\)$/, c)], + do: form + end + + defp cached_exceptions do + case :persistent_term.get({__MODULE__, :exceptions}, :none) do + :none -> + exceptions = case declared() do {:ok, s} -> s.exceptions; _ -> [] end + :persistent_term.put({__MODULE__, :exceptions}, exceptions) + exceptions + + exceptions -> + exceptions + end + end + @doc "A verse as a citation: \"Devarim 19:15\"." @spec cite(verse()) :: String.t() def cite(%{book: b, chapter: c, verse: v}), do: "#{String.capitalize(b)} #{c}:#{v}" diff --git a/vav_pipeline/test/sugya_test.exs b/vav_pipeline/test/sugya_test.exs index cce9d998..d2acc123 100644 --- a/vav_pipeline/test/sugya_test.exs +++ b/vav_pipeline/test/sugya_test.exs @@ -48,6 +48,33 @@ defmodule VavPipeline.SugyaTest do assert Sugya.for_rule("no_such_rule") == nil end + describe "the code reads its exemptions from the facts (step 2)" do + test "the single-witness forms are those the sugya excepts, and only those" do + assert Sugya.single_witness_forms() == ["forged_fringe"] + end + + test "the hedged and no-ground exemptions of golden_calf are facts, not constants" do + assert Sugya.exception?("golden_calf", "hedged") + assert Sugya.exception?("golden_calf", "no_ground_given") + refute Sugya.exception?("golden_calf", "anything_at_all") + refute Sugya.exception?("chelbenah", "hedged") + end + + test "with the fact removed, the exemption is gone: behaviour is read from the reason" do + temp = Path.join(System.tmp_dir!(), "sugya_step2_#{System.unique_integer([:positive])}") + rel = "talmudic_engine/src/halt/sugya.pl" + File.mkdir_p!(Path.dirname(Path.join(temp, rel))) + File.cp!(Path.join(Doorpost.repo_root(), rel), Path.join(temp, rel)) + on_exit(fn -> File.rm_rf!(temp) end) + assert Sugya.single_witness_forms(temp) == ["forged_fringe"] + path = Path.join(temp, rel) + File.write!(path, String.replace(File.read!(path), "exception(lo_taaseh_lekha, single_witness(forged_fringe), verse(vayikra, 22, 20)).", "")) + assert Sugya.single_witness_forms(temp) == [] + File.write!(path, String.replace(File.read!(path), "exception(golden_calf, hedged, verse(devarim, 18, 22)).", "")) + refute Sugya.exception?("golden_calf", "hedged", temp) + end + end + describe "drift (synthetic spec root)" do setup do temp = Path.join(System.tmp_dir!(), "sugya_test_#{System.unique_integer([:positive])}")