From dc6316f1d71824c537200938de8c5afb229ec6db Mon Sep 17 00:00:00 2001 From: Scott Karp Date: Sun, 25 Oct 2026 01:30:09 +0000 Subject: [PATCH] sugya, step 3: the case behind each threshold is carried on every verdict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit specs/sugya.md §IV "carried on the verdict", §V step 3. Sugya.for_verdict/1 renders, for every rule that fired and every form that was noted: kind, verse, precedent, the dissents and exceptions — each with `applied`, read from the record and never guessed: the single witness noted (and for which forms), the hedged claims left out of the trace, grounding not assessed for want of ground, a forged fringe standing on one witness. A dissent the record cannot speak to is carried applied: false — considered, and the rule's verse decided. The minority is on every record (Eduyot 1:5–6): to be relied on later, and to be located. Carried as `sugya` on the bedikah response, the pair's two sides, and the gate's `bikoret` object (pass and stream). The fringe is unchanged — it is for the reader, who asks for the sugya by reading the record. Tamid §1.1: the `sugya` field in the response and the paragraph "Every rule carries its sugya"; Doorpost pin → 658005c9…. Elixir 664/0 (sugya_verdict_test: clean → {}; noted apology → two-witness dissent applied for that form; forged fringe → the mark exception applied; golden calf with and without a hedged claim; a rule the record cannot speak to → considered, applied false). Co-Authored-By: Claude Fable 5.1 --- specs/bikoret_tamid.md | 18 +++++ .../lib/vav_pipeline/bigdei_kodesh/router.ex | 4 ++ vav_pipeline/lib/vav_pipeline/doorpost.ex | 2 +- vav_pipeline/lib/vav_pipeline/gate.ex | 1 + vav_pipeline/lib/vav_pipeline/gate/stream.ex | 1 + vav_pipeline/lib/vav_pipeline/sugya.ex | 70 +++++++++++++++++++ vav_pipeline/test/sugya_verdict_test.exs | 54 ++++++++++++++ 7 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 vav_pipeline/test/sugya_verdict_test.exs diff --git a/specs/bikoret_tamid.md b/specs/bikoret_tamid.md index 18f13d00..ce8a6e64 100644 --- a/specs/bikoret_tamid.md +++ b/specs/bikoret_tamid.md @@ -243,6 +243,12 @@ Response (200): "verdict": "tov" | "lo_tov" | "empty", "violations": ["halt(avodah_zarah, ...)", ...], # the Prolog halt terms, verbatim "anchors": { "avodah_zarah": "Shemot 20:3", ... }, # the verse of each rule that fired + "sugya": { # the case behind each threshold (sugya.md) + "": { "kind": "deed|mark|word|measure", "verse": "...", "precedent": "tablet(49)", + "fired": bool, "noted": ["
", ...], + "dissent": [{"reading": "...", "verse": "...", "applied": bool, "form": ...}], + "exception": [{"condition": "...", "verse": "...", "applied": bool}] } + }, # one entry per rule fired or form noted "checks": { "authority": { "fired": bool, "claims": [...], "violations": [...] }, "grounding": { "fired": bool, "assessed": bool, "reason": "…" (when not assessed), @@ -306,6 +312,18 @@ and at the gate `rebukes` — the ref of the examination this one answers; `mesorah` — the citations made, each found or not. ``` +**Every rule carries its sugya** (`sugya.md`). Beside the verse a rule +stands on, the verdict carries the case it was cut from, which of +Torah's procedures reads it — a deed by two witnesses, a mark by +inspection, a word by what follows — the reading that would acquit, and +what the rule does not reach; `applied: true` marks the dissent or +exception the record shows held (the single witness noted, the hedged +claim left out, no ground given, the forged fringe on one witness). A +dissent the record cannot speak to is carried `applied: false`: +considered, and the rule's verse decided. The minority is on every +record (Eduyot 1:5–6). Nothing here learns; a new dissent is a commit +that moves the pin. + **Every rule cites its verse** (`bishearecha.md` §X; Devarim 4:2). `talmudic_engine/src/halt/anchors.pl` declares `rule_anchor(, verse(, , ))` for every halt name the rule modules can diff --git a/vav_pipeline/lib/vav_pipeline/bigdei_kodesh/router.ex b/vav_pipeline/lib/vav_pipeline/bigdei_kodesh/router.ex index aa93188c..5effce06 100644 --- a/vav_pipeline/lib/vav_pipeline/bigdei_kodesh/router.ex +++ b/vav_pipeline/lib/vav_pipeline/bigdei_kodesh/router.ex @@ -941,6 +941,8 @@ defmodule VavPipeline.BigdeiKodesh.Router do verdict: Atom.to_string(result.verdict), violations: result.violations, anchors: VavPipeline.Anchors.for_violations(result.violations), + # The case behind each threshold that fired or was noted (sugya.md §IV). + sugya: VavPipeline.Sugya.for_verdict(result), checks: result.checks, examination: %{ vital_content: result.examination["vital_content"], @@ -1023,6 +1025,8 @@ defmodule VavPipeline.BigdeiKodesh.Router do verdict: Atom.to_string(result.verdict), violations: result.violations, anchors: VavPipeline.Anchors.for_violations(result.violations), + # The case behind each threshold that fired or was noted (sugya.md §IV). + sugya: VavPipeline.Sugya.for_verdict(result), a: result.a, b: result.b, difference: result.difference, diff --git a/vav_pipeline/lib/vav_pipeline/doorpost.ex b/vav_pipeline/lib/vav_pipeline/doorpost.ex index 90ef0e03..4248920c 100644 --- a/vav_pipeline/lib/vav_pipeline/doorpost.ex +++ b/vav_pipeline/lib/vav_pipeline/doorpost.ex @@ -182,7 +182,7 @@ defmodule VavPipeline.Doorpost do # changes; the update is a deliberate operator commit. A bare # hash-update commit (without a corresponding spec change in the # same commit) is itself a drift signal. - @expected_hash "cb2f31358051acc19c36bb8dfa0a179a7d4845ac6acac7d8e2b7b1e17150b0c2" + @expected_hash "658005c9323391e8c198c0cf23f91af0a2fdbdb20cac204eb60b58ac0c0bb318" @doc "Return the list of files that constitute the category-grade specification." @spec files() :: [String.t()] diff --git a/vav_pipeline/lib/vav_pipeline/gate.ex b/vav_pipeline/lib/vav_pipeline/gate.ex index db096965..b87e8c89 100644 --- a/vav_pipeline/lib/vav_pipeline/gate.ex +++ b/vav_pipeline/lib/vav_pipeline/gate.ex @@ -126,6 +126,7 @@ defmodule VavPipeline.Gate do violations: result.violations, anchors: anchors, fired: Shape.fired(result, anchors), + sugya: VavPipeline.Sugya.for_verdict(result), checks: result.checks, sources: result.sources, model: Shape.reply_model(upstream, reply) diff --git a/vav_pipeline/lib/vav_pipeline/gate/stream.ex b/vav_pipeline/lib/vav_pipeline/gate/stream.ex index 69924b8d..62be2d12 100644 --- a/vav_pipeline/lib/vav_pipeline/gate/stream.ex +++ b/vav_pipeline/lib/vav_pipeline/gate/stream.ex @@ -217,6 +217,7 @@ defmodule VavPipeline.Gate.Stream do violations: result.violations, anchors: anchors, fired: Shape.fired(result, anchors), + sugya: VavPipeline.Sugya.for_verdict(result), sources: result.sources, model: state.model, streamed: true diff --git a/vav_pipeline/lib/vav_pipeline/sugya.ex b/vav_pipeline/lib/vav_pipeline/sugya.ex index aad25246..7cf07d0f 100644 --- a/vav_pipeline/lib/vav_pipeline/sugya.ex +++ b/vav_pipeline/lib/vav_pipeline/sugya.ex @@ -198,6 +198,76 @@ defmodule VavPipeline.Sugya do end end + # ------------------------------------------------------------ + # The sugya on a verdict (sugya.md §IV, "carried on the verdict") + # ------------------------------------------------------------ + + @doc """ + The sugya a verdict carries: one entry per rule that fired and per form + that was noted — kind, verse, precedent, the dissents with `applied` + marking the one that held, the exceptions with `applied` likewise. + `applied` is read from the record, never guessed: the single witness + noted (a form in `checks.frames.noted`), the hedged claims left out of + the trace, grounding not assessed for want of ground, a forged fringe + standing on one witness. A dissent the record cannot speak to is + carried with `applied: false` — considered, and the rule's verse + decided. The minority is thus on the record for every verdict (Eduyot + 1:5–6): to be relied on later, and to be located. + """ + @spec for_verdict(map()) :: map() + def for_verdict(result) when is_map(result) do + checks = Map.get(result, :checks) || %{} + violations = Map.get(result, :violations) || [] + fired_rules = violations |> Enum.flat_map(&rule_of/1) |> Enum.uniq() + frames = Map.get(checks, :frames) || %{} + noted = (Map.get(frames, :noted) || %{}) |> Map.keys() |> Enum.map(&to_string/1) + forms = Map.get(frames, :forms) || %{} + forged_on_one = Enum.any?(forms, fn {_f, e} -> Map.get(e, :stands_on) == :forgery end) + grounding = Map.get(checks, :grounding) || %{} + claims = get_in(result, [:examination, "claims"]) || [] + hedged_out = Enum.any?(claims, &(&1["hedged"] == true)) + + rules = Enum.uniq(fired_rules ++ if(noted != [], do: ["lo_taaseh_lekha"], else: [])) + + Map.new(rules, fn rule -> + base = for_rule(rule) || %{rule: rule, kind: nil, verse: nil, precedent: nil, dissent: [], exception: []} + + dissent = + Enum.map(base.dissent, fn d -> + applied = + case {rule, d.reading, d.form} do + {"lo_taaseh_lekha", "single_witness_noted", _} -> noted != [] + {"golden_calf", "hedged", _} -> hedged_out + _ -> false + end + + d |> Map.put(:applied, applied) |> then(fn m -> if rule == "lo_taaseh_lekha" and d.reading == "single_witness_noted", do: Map.put(m, :forms, noted), else: m end) + end) + + exception = + Enum.map(base.exception, fn e -> + applied = + case {rule, e.condition} do + {"lo_taaseh_lekha", "single_witness(forged_fringe)"} -> forged_on_one + {"golden_calf", "no_ground_given"} -> Map.get(grounding, :assessed) == false and (Map.get(grounding, :reason) || "") =~ "no sources given" + {"golden_calf", "hedged"} -> hedged_out + _ -> false + end + + Map.put(e, :applied, applied) + end) + + {rule, %{base | dissent: dissent, exception: exception} |> Map.put(:fired, rule in fired_rules) |> Map.put(:noted, if(rule == "lo_taaseh_lekha", do: noted, else: []))} + end) + end + + defp rule_of(violation) do + case Regex.run(~r/^halt\(\s*(\w+)\s*,/, to_string(violation)) do + [_, rule] -> [rule] + _ -> [] + 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_verdict_test.exs b/vav_pipeline/test/sugya_verdict_test.exs new file mode 100644 index 00000000..b01b2d4f --- /dev/null +++ b/vav_pipeline/test/sugya_verdict_test.exs @@ -0,0 +1,54 @@ +defmodule VavPipeline.SugyaVerdictTest do + @moduledoc "The sugya on a verdict (sugya.md §IV, step 3): applied is read from the record, never guessed." + use ExUnit.Case, async: true + + alias VavPipeline.Sugya + + defp result(overrides) do + Map.merge(%{verdict: :tov, violations: [], checks: %{}, examination: %{"claims" => []}}, overrides) + end + + test "a clean verdict carries no sugya" do + assert Sugya.for_verdict(result(%{})) == %{} + end + + test "a noted form carries the two-witness dissent, applied, with the forms it held for" do + r = result(%{checks: %{frames: %{fired: false, forms: %{}, noted: %{apology_without_mechanism: %{claims: [], witnesses: 1}}, violations: []}}}) + s = Sugya.for_verdict(r) + assert Map.keys(s) == ["lo_taaseh_lekha"] + e = s["lo_taaseh_lekha"] + assert e.kind == "deed" and e.verse == "Devarim 19:15" + assert e.fired == false and e.noted == ["apology_without_mechanism"] + d = Enum.find(e.dissent, &(&1.reading == "single_witness_noted")) + assert d.applied == true and d.forms == ["apology_without_mechanism"] and d.verse == "Devarim 19:15" + # the exception for the forged fringe did not apply: nothing stood on one witness + assert Enum.find(e.exception, &(&1.condition =~ "forged_fringe")).applied == false + end + + test "a forged fringe standing on one witness carries the mark exception, applied" do + r = result(%{verdict: :lo_tov, + violations: ["halt(lo_taaseh_lekha,action(frame,by(claude),form(forged_fringe)),forged_fringe,'…')"], + checks: %{frames: %{fired: true, forms: %{forged_fringe: %{stands_on: :forgery, witnesses: 1, claims: [], violations: ["x"]}}, noted: %{}, violations: ["x"]}}}) + e = Sugya.for_verdict(r)["lo_taaseh_lekha"] + assert e.fired == true + assert Enum.find(e.exception, &(&1.condition == "single_witness(forged_fringe)")) |> Map.fetch!(:applied) == true + assert Enum.find(e.dissent, &(&1.reading == "single_witness_noted")).applied == false + end + + test "a golden calf beside a hedged claim carries the hedged dissent applied; without hedging, not" do + base = %{verdict: :lo_tov, violations: ["halt(golden_calf,interpretation(x),golden_calf,'…')"], checks: %{grounding: %{assessed: true, fired: true}}} + with_hedge = Sugya.for_verdict(result(Map.put(base, :examination, %{"claims" => [%{"hedged" => true}, %{"hedged" => false}]}))) + assert with_hedge["golden_calf"].kind == "word" and with_hedge["golden_calf"].verse == "Devarim 18:22" + assert Enum.find(with_hedge["golden_calf"].dissent, &(&1.reading == "hedged")).applied == true + without = Sugya.for_verdict(result(Map.put(base, :examination, %{"claims" => [%{"hedged" => false}]}))) + assert Enum.find(without["golden_calf"].dissent, &(&1.reading == "hedged")).applied == false + end + + test "a rule the record cannot speak to carries its dissent considered, applied false" do + r = result(%{verdict: :lo_tov, violations: ["halt(parah_adumah,action(self_validate,by(claude),target(self)),claude,'…')"]}) + e = Sugya.for_verdict(r)["parah_adumah"] + assert e.kind == "mark" and e.verse == "Vayikra 13:3" + assert e.precedent == "specimens(self_validation)" + assert [%{reading: "validator_named_is_not_the_speaker", applied: false, verse: "Bamidbar 19:7"}] = Enum.map(e.dissent, &Map.take(&1, [:reading, :applied, :verse])) + end +end