sugya, step 4: the hard matter goes up with its dissent

כי יפלא ממך דבר למשפט ... ובין נגע לנגע דברי ריבת בשעריך וקמת ועלית
(Devarim 17:8, read from L1). A mark the inspection cannot settle, a
dispute in your gates — it goes up. specs/sugya.md §V step 4: a
bedikah's applied dissents become the minority the clerk stores when
the case goes up.

POST /chatzer/sanhedrin takes `bedikah_ref`. Only this key's own
bedikah goes up (404 alike for another's or none). The case is that
verdict; the roots are the verses the output cited, read from L1 — the
output's text is not re-read, the record never kept it; the sugya is
rebuilt from the record (Sugya.from_record/1: forms found but not fired
are the noted ones; a fired forged fringe stood on one witness); the
applied dissents (Sugya.dissents_applied/1) travel in the case context
and down into L3: beside the panel's minority, or AS the minority when
the panel was unanimous — "the panel was unanimous | carried up from
the bedikah (Devarim 17:8): single_witness_noted (Devarim 19:15) for
apology_without_mechanism", grounding achorai. The response's grounding
carries the bedikah, its carried dissent and its sugya. Tamid §1.1: the
request field. Doorpost pin → 7eac65cf….

Tests: from_record/dissents_applied (noted form; forged fringe; clean);
L3 — a noted bedikah goes up under its key: roots from Devarim 6:4 as
read in L1 (ש-מ-ע among them), panel 3, the carried dissent in the
response, a minority recorded though the panel was unanimous; another
key's bedikah 404. Elixir 669/0; L3 8/0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Scott KarpandClaude Fable 5.1 committed 2026-10-25 01:44:40 +00:00
1 parent dc6316f1d7
commit ba3ff4a2f4
5 files changed
+234 -11

No files matched your search

@@ -1172,6 +1172,39 @@ defmodule VavPipeline.BigdeiKodesh.Router do
defp process_sanhedrin(conn) do
params = conn.body_params
# The hard matter goes up with its dissent (Devarim 17:8 — bein nega
# lanega, divrei rivot bish'arecha; sugya.md step 4): a bedikah_ref
# names a verdict given at the gate; the case is that verdict, the
# roots are the verses it cited, and the sugya's applied dissents go
# up with it and come down as the minority the clerk keeps.
case params["bedikah_ref"] do
ref when is_binary(ref) and ref != "" -> process_sanhedrin_bedikah(conn, ref, params)
_ -> process_sanhedrin_case(conn, params)
end
end
defp process_sanhedrin_bedikah(conn, ref, params) do
cond do
Process.whereis(VavPipeline.Repo) == nil ->
send_error(conn, 503, "L3_UNAVAILABLE", %{issue: "A bedikah goes up from L3, and L3 is not reachable", correction: "Retry", resubmit: true})
true ->
key_id = caller_key_id(conn)
case VavPipeline.L3.Store.get_bedikah(ref) do
%{key_id: ^key_id} = rec when is_binary(key_id) ->
sugya = VavPipeline.Sugya.for_verdict(VavPipeline.Sugya.from_record(rec))
carried = VavPipeline.Sugya.dissents_applied(sugya)
question = params["case"] || "Does the verdict #{rec.verdict} of #{ref} stand? (#{Enum.join(rec.violations, "; ")})"
process_sanhedrin_case(conn, Map.put(params, "case", question), %{bedikah: rec, sugya: sugya, carried: carried})
_ ->
send_error(conn, 404, "NOT_FOUND", %{issue: "No bedikah #{ref} in this key's ledger", correction: "Only an examination brought under this key goes up", resubmit: false})
end
end
end
defp process_sanhedrin_case(conn, params, up \\ nil) do
case_text = params["case"]
if is_nil(case_text) or case_text == "" do
@@ -1196,8 +1229,8 @@ defmodule VavPipeline.BigdeiKodesh.Router do
# engraved in L3; the answer takes the Tamid's shape.
with {:ok, sides} <- sanhedrin_sides(params["sides"]),
{:ok, complexity} <- sanhedrin_complexity(params["complexity_hint"]),
{:ok, scope, resolved_by} <- sanhedrin_scope(case_text),
{:ok, resolved} <- sanhedrin_resolve(case_text, scope, resolved_by),
{:ok, scope, resolved_by} <- sanhedrin_scope(if(up, do: Enum.join(up.bedikah.references, " "), else: case_text)),
{:ok, resolved} <- (if up, do: sanhedrin_resolve_bedikah(up.bedikah, scope, resolved_by), else: sanhedrin_resolve(case_text, scope, resolved_by)),
:ok <- sanhedrin_gather_panel() do
case_ref = generate_id()
@@ -1209,12 +1242,15 @@ defmodule VavPipeline.BigdeiKodesh.Router do
sides: sides,
roots: resolved.roots,
torah_refs: [resolved.torah_ref],
authority_source: :torah
authority_source: :torah,
# the dissent travels with the case (Devarim 17:8)
bedikah: (if up, do: %{ref: up.bedikah.bedikah_ref, verdict: up.bedikah.verdict, violations: up.bedikah.violations}, else: nil),
dissent: (if up, do: up.carried, else: [])
},
requested_by: "chatzer"
}) do
{:ok, decision} ->
recorded = sanhedrin_record_l3(case_ref, case_text, decision, resolved)
recorded = sanhedrin_record_l3(case_ref, case_text, decision, resolved, (if up, do: up.carried, else: []))
send_json(conn, 200, %{
case_ref: decision.case_ref,
@@ -1228,7 +1264,8 @@ defmodule VavPipeline.BigdeiKodesh.Router do
grounding: %{
scope: scope_report(scope, resolved_by),
roots_consulted: format_roots(resolved.roots_full),
recorded: recorded
recorded: recorded,
bedikah: (if up, do: %{ref: up.bedikah.bedikah_ref, verdict: up.bedikah.verdict, dissent_carried: up.carried, sugya: up.sugya}, else: nil)
},
note: "This is the system's reasoning, not a binding ruling. A human beit din may reach a different conclusion.",
disclosure: disclosure()
@@ -1369,6 +1406,38 @@ defmodule VavPipeline.BigdeiKodesh.Router do
end
end
# A bedikah that goes up is grounded in the verses it cited: each
# reference read from L1, its words' roots as the engine names them.
# The output's own text is not re-read — the record never kept it.
defp sanhedrin_resolve_bedikah(rec, scope, _resolved_by) do
refs =
(rec.references || [])
|> Enum.flat_map(fn r ->
case Regex.run(~r/^(\w+)\s+(\d+):(\d+)$/, String.trim(to_string(r))) do
[_, book, ch, vs] -> [{String.downcase(book), String.to_integer(ch), String.to_integer(vs)}]
_ -> []
end
end)
roots_full =
refs
|> Enum.flat_map(fn {b, c, v} ->
case VavPipeline.L1Reader.read_verse(b, c, v) do
{:ok, verse} -> verse.words |> Enum.filter(& &1.shoresh) |> Enum.map(&%{shoresh: &1.shoresh, shoresh_letters: [], positions: [], gematria: 0, binyan: nil, word: &1.consonants})
_ -> []
end
end)
|> Enum.uniq_by(& &1.shoresh)
torah_ref =
case refs do
[{b, c, v} | _] -> {String.to_atom(b), c, v}
[] -> {String.to_atom(scope.book), scope.chapter, scope.verse}
end
{:ok, %{roots: roots_full |> Enum.map(&VavPipeline.BereishitCycle.engine_root_name/1) |> Enum.reject(&is_nil/1), roots_full: roots_full, torah_ref: torah_ref, read_from: :references}}
end
# One panel per case from the day's manna (Vayakhel §3.7 — the ledger
# provisions sanhedrin_panels_max). The provision is loaded first, as
# Gate.gather_call does (idempotent per date). Without a ledger
@@ -1418,7 +1487,7 @@ defmodule VavPipeline.BigdeiKodesh.Router do
# against, processing_component "sanhedrin"), the minority beside it
# when it exists. Never turns a served decision into a 500: the record
# failing is logged and reported in the reply as `recorded`.
defp sanhedrin_record_l3(case_ref, case_text, decision, resolved) do
defp sanhedrin_record_l3(case_ref, case_text, decision, resolved, carried) do
if Process.whereis(VavPipeline.Repo) do
maj = decision.majority
min = decision.minority
@@ -1450,12 +1519,25 @@ defmodule VavPipeline.BigdeiKodesh.Router do
{:ok, interp} ->
Logger.info("L3: interpretation #{interp.id} recorded for sanhedrin #{case_ref} (#{decision.outcome})")
# The dissent that came up with the case is kept beside the
# panel's own minority — or as the minority when the panel had
# none (Eduyot 1:5–6: to be relied on later, and to be located).
carried_text =
Enum.map_join(carried, "; ", fn
%{reading: r, verse: v, forms: fs} -> "#{r} (#{v})" <> if(fs == [], do: "", else: " for " <> Enum.join(fs, ", "))
%{exception: c, verse: v} -> "exception #{c} (#{v})"
end)
carried_refs = Enum.map(carried, & &1.verse)
minority_recorded =
if Map.get(min, :vote_count, 0) > 0 do
if Map.get(min, :vote_count, 0) > 0 or carried != [] do
case VavPipeline.L3.Store.record_minority(%{
interpretation_id: interp.id,
dissenting_reasoning: "#{Map.get(min, :position, "")} — #{Map.get(min, :reasoning, "")}",
torah_references: Enum.map(Map.get(min, :torah_refs, []), &sanhedrin_ref_string/1),
dissenting_reasoning:
(if Map.get(min, :vote_count, 0) > 0, do: "#{Map.get(min, :position, "")} — #{Map.get(min, :reasoning, "")}", else: "the panel was unanimous") <>
if(carried == [], do: "", else: " | carried up from the bedikah (Devarim 17:8): #{carried_text}"),
torah_references: Enum.uniq(Enum.map(Map.get(min, :torah_refs, []), &sanhedrin_ref_string/1) ++ carried_refs),
grounding_type: "achorai"
}) do
{:ok, _} ->
+1 -1
View File
@@ -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 "658005c9323391e8c198c0cf23f91af0a2fdbdb20cac204eb60b58ac0c0bb318"
@expected_hash "7eac65cfadccc37e600a779fc89acef0a24a9eac35e5ab18e8ec845cc1f104c2"
@doc "Return the list of files that constitute the category-grade specification."
@spec files() :: [String.t()]
+39
View File
@@ -261,6 +261,45 @@ defmodule VavPipeline.Sugya do
end)
end
@doc """
A stored bedikah (L3 record) in the shape `for_verdict/1` reads: its
violations, its claims, the forms found — those with no violation of
their form were NOTED — and whether a forged fringe stood on one witness.
The hard matter goes up with its dissent (Devarim 17:8, sugya.md step 4).
"""
@spec from_record(struct() | map()) :: map()
def from_record(rec) do
violations = Map.get(rec, :violations) || []
fired_forms =
violations
|> Enum.flat_map(fn v -> case Regex.run(~r/form\((\w+)\)/, to_string(v)) do [_, f] -> [f]; _ -> [] end end)
|> MapSet.new()
found = (Map.get(rec, :frames) || []) |> Enum.map(&to_string(&1["form"] || Map.get(&1, :form)))
noted = found |> Enum.reject(&MapSet.member?(fired_forms, &1)) |> Enum.uniq()
forms = for f <- fired_forms, into: %{}, do: {String.to_atom(f), %{stands_on: if(f == "forged_fringe", do: :forgery, else: :sentences)}}
claims = Map.get(rec, :claims) || []
%{
verdict: Map.get(rec, :verdict),
violations: violations,
examination: %{"claims" => Enum.map(claims, fn c -> %{"hedged" => c["hedged"] == true} end)},
checks: %{
frames: %{noted: Map.new(noted, &{String.to_atom(&1), %{witnesses: 1}}), forms: forms},
grounding: %{assessed: claims != [] and (Map.get(rec, :sources) || []) != []}
}
}
end
@doc "The dissents and exceptions a sugya shows applied: `[%{rule, reading | condition, verse, form?}]`."
@spec dissents_applied(map()) :: [map()]
def dissents_applied(sugya) when is_map(sugya) do
Enum.flat_map(sugya, fn {rule, e} ->
Enum.map(Enum.filter(e.dissent, & &1.applied), &%{rule: rule, reading: &1.reading, verse: &1.verse, forms: Map.get(&1, :forms, [])}) ++
Enum.map(Enum.filter(e.exception, & &1.applied), &%{rule: rule, exception: &1.condition, verse: &1.verse})
end)
end
defp rule_of(violation) do
case Regex.run(~r/^halt\(\s*(\w+)\s*,/, to_string(violation)) do
[_, rule] -> [rule]