tablet 55, second: the judge consults — the Sanhedrin no longer rolls dice
judge.erl chose one of six canned positions by a hash over the question,
its own pid and the clock; a panel of one "accepted" on zero roots and
zero references under "Torah references ground this interpretation" —
the calf, cast by the court itself. Architecture §3.2 says a judge
"consults the specification (L0/L1)"; Construction §3.2 says the
pipeline hands the case roots and torah_refs. Neither happened.
Sanhedrin (Erlang):
- judge.erl: seat N performs step N of the dialectic — interpret,
challenge, resolve, cycling — through a consult {M, F} in the app env.
resolved → accept with the verses; challenged (a kushya stands) →
reject; halt → reject; teiku → abstain. No consult, an error, or a
consult that raises → abstain, and the reasoning says so; the judge
stands. judge:start_link/2 takes the seat; panel.erl seats its judges.
- EUnit 54/0: seats cycle; no consult → abstain; resolved → accept with
#torah_ref{}; a raising consult → abstain and alive; a panel of three
with a kushya at seat 2 → accepted 2–1, minority preserved with verse.
Pipeline (Elixir):
- VavPipeline.SanhedrinConsult: what a judge asks — TalmudicEngineSocket
interpret/challenge/resolve over the case's roots, direct (the client's
stub fallbacks are NOT used: a court that accepts on a stub is the
calf). Engine absent → {:error, _} → the judge abstains. Registered in
the sanhedrin env at application start.
- router process_sanhedrin: the case is heard before it is submitted —
ScopeResolver → Shema context (cloud honoured) → tohu → vayomer
(Shoresh Engine over the verse) → roots; torah_refs = the scope;
complexity_hint names the panel (422 on an unknown one); one unit of
sanhedrin_panels_max per case (503 MANNA_DEPLETED when spent); the
decision engraved in L3 as an interpretation (panel_size, votes
for/against, processing_component "sanhedrin", coupling root→verse)
with the minority beside it (grounding_type achorai); the reply in
the Tamid's shape (position, reasoning, sources, votes) plus
grounding {scope, roots_consulted, recorded}.
- Tests: Elixir 628/0 (router 30, incl. monetary → panel 3; no engine →
teiku with 0 votes, never accepted; fake consult → 2–1 with sources;
manna → 503; unknown hint → 422); L3 test: interpretation + minority
rows land.
Pre-existing and untouched: with --include l3 the gate and hakhel
tests fail 17 times on main as well (the gate's local upstream double
refuses connections under the L3 flag) — a separate finding.
Doorpost pin unchanged (no @spec_files touched).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
7c40200dfc
commit
046010f0ed
8 files changed
+734
-107
No files matched your search
@@ -17,6 +17,9 @@
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include("sanhedrin.hrl").
|
||||
|
||||
%% The consult callback the tablet-55 tests register (judge:consult reads {M,F}).
|
||||
-export([fake_consult/5]).
|
||||
|
||||
%% ============================================================
|
||||
%% Test fixture — start/stop the application for each test
|
||||
%% ============================================================
|
||||
@@ -382,6 +385,92 @@ judge_deliberate_sends_vote_test() ->
|
||||
end,
|
||||
judge:stop(JudgePid).
|
||||
|
||||
%% ============================================================
|
||||
%% Tablet 55 — the judge consults; it does not roll dice
|
||||
%% ============================================================
|
||||
|
||||
judge_seats_cycle_the_dialectic_test() ->
|
||||
?assertEqual(interpret, judge:step_for_seat(1)),
|
||||
?assertEqual(challenge, judge:step_for_seat(2)),
|
||||
?assertEqual(resolve, judge:step_for_seat(3)),
|
||||
?assertEqual(interpret, judge:step_for_seat(4)),
|
||||
?assertEqual(challenge, judge:step_for_seat(23)),
|
||||
?assertEqual(challenge, judge:step_for_seat(71)).
|
||||
|
||||
judge_without_consult_abstains_test() ->
|
||||
application:unset_env(sanhedrin, consult),
|
||||
Case = make_case_with_context(simple, <<"q">>, #{roots => [<<"br-a">>]}),
|
||||
{ok, JudgePid} = judge:start_link(Case, 1),
|
||||
judge:deliberate(JudgePid, self()),
|
||||
receive
|
||||
{vote, Vote} ->
|
||||
?assertEqual(abstain, Vote#vote.verdict),
|
||||
?assertEqual([], Vote#vote.torah_refs),
|
||||
?assertMatch({_, _}, binary:match(Vote#vote.reasoning, <<"no consultation">>))
|
||||
after 5000 -> ?assert(false)
|
||||
end,
|
||||
judge:stop(JudgePid).
|
||||
|
||||
judge_consult_resolved_accepts_with_verses_test() ->
|
||||
application:set_env(sanhedrin, consult, {?MODULE, fake_consult}),
|
||||
Case = make_case_with_context(simple, <<"q">>, #{roots => [<<"br-a">>], fake => resolved}),
|
||||
{ok, JudgePid} = judge:start_link(Case, 1),
|
||||
judge:deliberate(JudgePid, self()),
|
||||
receive
|
||||
{vote, Vote} ->
|
||||
?assertEqual(accept, Vote#vote.verdict),
|
||||
?assertEqual(<<"the reading">>, Vote#vote.position),
|
||||
?assertMatch([#torah_ref{book = bereishit, chapter = 1, verse = 1}], Vote#vote.torah_refs),
|
||||
?assertMatch({_, _}, binary:match(Vote#vote.reasoning, <<"interpret via the Talmudic Engine">>))
|
||||
after 5000 -> ?assert(false)
|
||||
end,
|
||||
judge:stop(JudgePid),
|
||||
application:unset_env(sanhedrin, consult).
|
||||
|
||||
judge_consult_raises_abstains_and_stands_test() ->
|
||||
application:set_env(sanhedrin, consult, {?MODULE, fake_consult}),
|
||||
Case = make_case_with_context(simple, <<"q">>, #{fake => raise}),
|
||||
{ok, JudgePid} = judge:start_link(Case, 2),
|
||||
judge:deliberate(JudgePid, self()),
|
||||
receive
|
||||
{vote, Vote} -> ?assertEqual(abstain, Vote#vote.verdict)
|
||||
after 5000 -> ?assert(false)
|
||||
end,
|
||||
?assert(is_process_alive(JudgePid)),
|
||||
judge:stop(JudgePid),
|
||||
application:unset_env(sanhedrin, consult).
|
||||
|
||||
%% A panel of three where the dialectic finds a kushya at seat 2:
|
||||
%% interpret → resolved, challenge → challenged, resolve → resolved.
|
||||
%% Majority 2 accept, minority 1 reject — the minority preserved.
|
||||
panel_of_three_dissent_from_the_dialectic_test() ->
|
||||
setup(),
|
||||
application:set_env(sanhedrin, consult, {?MODULE, fake_consult}),
|
||||
Case = make_case_with_context(monetary, <<"q">>, #{roots => [<<"br-a">>], fake => dialectic}),
|
||||
{ok, CaseRef} = escalation:submit_case(Case),
|
||||
timer:sleep(600),
|
||||
{ok, Decision} = clerk:get_decision(CaseRef),
|
||||
?assertEqual(3, Decision#decision.panel_size),
|
||||
?assertEqual(accepted, Decision#decision.outcome),
|
||||
?assertEqual(2, maps:get(vote_count, Decision#decision.majority)),
|
||||
?assertEqual(1, maps:get(vote_count, Decision#decision.minority)),
|
||||
?assertEqual(<<"but the verse says otherwise">>, maps:get(position, Decision#decision.minority)),
|
||||
application:unset_env(sanhedrin, consult),
|
||||
cleanup(ok).
|
||||
|
||||
%% The fake consultation the tests register. Step-aware for `dialectic`.
|
||||
fake_consult(_Step, _Ref, _Roots, _Q, #{fake := raise}) ->
|
||||
erlang:error(engine_exploded);
|
||||
fake_consult(_Step, _Ref, _Roots, _Q, #{fake := resolved}) ->
|
||||
{ok, #{status => <<"resolved">>, text => <<"the reading">>, verses => [{bereishit, 1, 1}]}};
|
||||
fake_consult(challenge, _Ref, _Roots, _Q, #{fake := dialectic}) ->
|
||||
{ok, #{status => <<"challenged">>, text => <<>>, kushya => <<"but the verse says otherwise">>,
|
||||
verses => [{devarim, 6, 4}]}};
|
||||
fake_consult(_Step, _Ref, _Roots, _Q, #{fake := dialectic}) ->
|
||||
{ok, #{status => <<"resolved">>, text => <<"the reading">>, verses => [{bereishit, 1, 1}]}};
|
||||
fake_consult(_Step, _Ref, _Roots, _Q, _Ctx) ->
|
||||
{error, no_fake}.
|
||||
|
||||
%% ============================================================
|
||||
%% Panel majority/minority computation tests
|
||||
%% ============================================================
|
||||
|
||||
Reference in new issue
Block a user