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:
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, _} ->
|
||||
|
||||
@@ -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()]
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in new issue
Block a user