%%%------------------------------------------------------------------- %%% @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 ).