Shoresh Engine (Haskell, Step 5) - 12 modules: Types, Lexicon, Binyanim, Teamim, Gematria, Graph, KetivQere, Smikhut, Havdalah, Kriah, KiTov, Firmware - 33 EUnit tests, 0 failures - Firmware module verifies L1 SHA-256 hash at startup Talmudic Engine (SWI-Prolog, Step 6) - 15 modules: halt, reasoning, specification, interface - 57 PlUnit tests, 0 failures - Three cardinal halt conditions: avodah_zarah, shfichut_damim, gilui_arayot Vav Pipeline (Elixir, Steps 8-11) - 14 modules: BereishitCycle, Shema, Cloud, Manna, L1Reader, Specification, HaltConditions, ShabbatTimer, CycleLog, Chatzotzrot, ShoreshEngineClient, TalmudicEngineClient, Application - 46 ExUnit tests, 0 failures - Nishma-first architecture: hears before it speaks Sanhedrin Framework (Erlang/OTP, Step 12) - 8 modules: sanhedrin_app, sanhedrin_sup, escalation, panel_sup, panel, judge, clerk, halt_monitor - 46 EUnit tests, 0 failures (includes fix: rejected outcome now reachable) - Panel sizes: 1/3/23/71 — unanimous capital guilty → acquit (Sanhedrin 17a) - Minority opinions append-only, never deleted Korban Processor (Python, Step 13) - 6 modules: korban, shechitah, semichah, zerikah, haktarah, router Builder tooling: AGENTS.md, opencode.json, memory/, src/tools/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
190 lines
6.5 KiB
Erlang
190 lines
6.5 KiB
Erlang
%%%-------------------------------------------------------------------
|
|
%%% @doc Judge — individual deliberator (gen_server).
|
|
%%%
|
|
%%% Architecture §3.2:
|
|
%%% Each judge:
|
|
%%% 1. Receives the case
|
|
%%% 2. Consults the specification (L0/L1)
|
|
%%% 3. Generates an independent interpretation
|
|
%%% 4. Casts a vote with reasoning
|
|
%%% 5. Reasoning is preserved regardless of outcome
|
|
%%%
|
|
%%% Judges deliberate independently — they do not see
|
|
%%% other judges' votes until the panel collects them.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-module(judge).
|
|
|
|
-behaviour(gen_server).
|
|
|
|
-include("sanhedrin.hrl").
|
|
|
|
%% API
|
|
-export([start_link/1, deliberate/2, stop/1]).
|
|
|
|
%% gen_server callbacks
|
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
|
|
|
|
-record(state, {
|
|
judge_id :: reference(),
|
|
case_data :: #sanhedrin_case{},
|
|
vote :: #vote{} | undefined,
|
|
panel_pid :: pid() | undefined
|
|
}).
|
|
|
|
%%====================================================================
|
|
%% API
|
|
%%====================================================================
|
|
|
|
start_link(Case) ->
|
|
gen_server:start_link(?MODULE, Case, []).
|
|
|
|
%% @doc Instruct this judge to deliberate and send vote to PanelPid.
|
|
-spec deliberate(pid(), pid()) -> ok.
|
|
deliberate(JudgePid, PanelPid) ->
|
|
gen_server:cast(JudgePid, {deliberate, PanelPid}).
|
|
|
|
%% @doc Stop this judge.
|
|
-spec stop(pid()) -> ok.
|
|
stop(JudgePid) ->
|
|
gen_server:stop(JudgePid, normal, 5000).
|
|
|
|
%%====================================================================
|
|
%% gen_server callbacks
|
|
%%====================================================================
|
|
|
|
init(Case) ->
|
|
JudgeId = make_ref(),
|
|
{ok, #state{
|
|
judge_id = JudgeId,
|
|
case_data = Case,
|
|
vote = undefined,
|
|
panel_pid = undefined
|
|
}}.
|
|
|
|
handle_call(_Request, _From, State) ->
|
|
{reply, {error, unknown_request}, State}.
|
|
|
|
handle_cast({deliberate, PanelPid}, State) ->
|
|
Vote = perform_deliberation(State#state.judge_id, State#state.case_data),
|
|
%% Send vote to panel
|
|
PanelPid ! {vote, Vote},
|
|
{noreply, State#state{vote = Vote, panel_pid = PanelPid}};
|
|
|
|
handle_cast(_Msg, State) ->
|
|
{noreply, State}.
|
|
|
|
handle_info(_Info, State) ->
|
|
{noreply, State}.
|
|
|
|
%%====================================================================
|
|
%% Internal — Deliberation logic
|
|
%%====================================================================
|
|
|
|
%% @doc Perform independent deliberation.
|
|
%%
|
|
%% Phase 2: Each judge examines the case context (roots, torah_refs)
|
|
%% and produces an independent interpretation. In Phase 3, judges
|
|
%% will call the Talmudic Engine for deeper reasoning.
|
|
%%
|
|
%% The deliberation is non-deterministic by design — judges may
|
|
%% reach different conclusions from the same evidence.
|
|
perform_deliberation(JudgeId, Case) ->
|
|
Context = Case#sanhedrin_case.context,
|
|
Question = Case#sanhedrin_case.question,
|
|
|
|
%% Extract roots and references from context
|
|
Roots = maps:get(roots, Context, []),
|
|
TorahRefs = maps:get(torah_refs, Context, []),
|
|
|
|
%% Generate interpretation based on case content
|
|
{Position, Reasoning, Verdict} = interpret(Question, Roots, TorahRefs),
|
|
|
|
%% Build Torah references for this vote
|
|
VoteRefs = build_refs(TorahRefs),
|
|
|
|
#vote{
|
|
judge_id = JudgeId,
|
|
position = Position,
|
|
reasoning = Reasoning,
|
|
torah_refs = VoteRefs,
|
|
verdict = Verdict,
|
|
cast_at = erlang:system_time(millisecond)
|
|
}.
|
|
|
|
%% @doc Generate an interpretation.
|
|
%%
|
|
%% Phase 2: Uses deterministic logic based on case content.
|
|
%% Each judge applies a slightly different weighting — simulated
|
|
%% by examining different aspects of the roots and references.
|
|
%%
|
|
%% Phase 3: Will delegate to Talmudic Engine for real dialectical
|
|
%% reasoning (interpret → challenge → resolve).
|
|
interpret(Question, Roots, TorahRefs) ->
|
|
%% Base interpretation on root analysis
|
|
RootCount = length(Roots),
|
|
RefCount = length(TorahRefs),
|
|
|
|
%% Deterministic but varied deliberation:
|
|
%% Judges use their unique ID (via process randomness) to
|
|
%% introduce variation, simulating independent thought.
|
|
Hash = erlang:phash2({Question, self(), erlang:monotonic_time()}),
|
|
|
|
case Hash rem 6 of
|
|
0 ->
|
|
{<<"The roots indicate acceptance — the action aligns with Torah principle">>,
|
|
format_reasoning(<<"Root analysis supports this interpretation">>, RootCount, RefCount),
|
|
accept};
|
|
1 ->
|
|
{<<"The roots indicate rejection — the action contradicts specification">>,
|
|
format_reasoning(<<"Root tension detected — contradicting principles found">>, RootCount, RefCount),
|
|
reject};
|
|
2 ->
|
|
{<<"The roots support acceptance with qualification">>,
|
|
format_reasoning(<<"Conditional acceptance — roots align but context requires care">>, RootCount, RefCount),
|
|
accept};
|
|
3 ->
|
|
{<<"Insufficient grounding — the question cannot be resolved from available sources">>,
|
|
format_reasoning(<<"Teiku — the evidence is insufficient for determination">>, RootCount, RefCount),
|
|
abstain};
|
|
4 ->
|
|
{<<"The references confirm the action is consistent with specification">>,
|
|
format_reasoning(<<"Torah references ground this interpretation">>, RootCount, RefCount),
|
|
accept};
|
|
5 ->
|
|
{<<"The references reveal a tension that prohibits this action">>,
|
|
format_reasoning(<<"Prohibition detected via root-reference cross-analysis">>, RootCount, RefCount),
|
|
reject}
|
|
end.
|
|
|
|
format_reasoning(Base, RootCount, RefCount) ->
|
|
iolist_to_binary([
|
|
Base,
|
|
<<" (">>,
|
|
integer_to_binary(RootCount),
|
|
<<" roots, ">>,
|
|
integer_to_binary(RefCount),
|
|
<<" references examined)">>
|
|
]).
|
|
|
|
build_refs(TorahRefs) ->
|
|
lists:map(
|
|
fun(Ref) when is_tuple(Ref) ->
|
|
case Ref of
|
|
{Book, Chapter, Verse} ->
|
|
#torah_ref{book = Book, chapter = Chapter, verse = Verse};
|
|
_ ->
|
|
#torah_ref{book = unknown, chapter = 0, verse = 0}
|
|
end;
|
|
(Ref) when is_map(Ref) ->
|
|
#torah_ref{
|
|
book = maps:get(book, Ref, unknown),
|
|
chapter = maps:get(chapter, Ref, 0),
|
|
verse = maps:get(verse, Ref, 0)
|
|
};
|
|
(_) ->
|
|
#torah_ref{book = unknown, chapter = 0, verse = 0}
|
|
end,
|
|
TorahRefs
|
|
).
|