Files
bikoret/sanhedrin/src/escalation.erl
T
Scott KarpandClaude Sonnet 4.6 80ec364da6 Phase 1-2 build: Steps 5-6, 8-13
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>
2026-02-26 14:02:34 -05:00

112 lines
3.9 KiB
Erlang

%%%-------------------------------------------------------------------
%%% @doc Escalation — assess complexity and route cases to panels.
%%%
%%% Architecture §3.2:
%%% assess(Case) determines complexity, route/2 convenes the panel.
%%%
%%% Panel sizes from halakha:
%%% simple -> 1 judge (individual matters)
%%% monetary -> 3 judges (dinei mamonot)
%%% capital -> 23 judges (dinei nefashot)
%%% novel -> 71 judges (Sanhedrin Gedolah)
%%% @end
%%%-------------------------------------------------------------------
-module(escalation).
-behaviour(gen_server).
-include("sanhedrin.hrl").
%% API
-export([start_link/0, submit_case/1, assess/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-define(SERVER, ?MODULE).
-record(state, {
active_cases = #{} :: map(), %% case_ref => panel_pid
case_count = 0 :: non_neg_integer()
}).
%%====================================================================
%% API
%%====================================================================
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%% @doc Submit a case for deliberation.
%% Returns {ok, CaseRef} or {error, Reason}.
-spec submit_case(#sanhedrin_case{}) -> {ok, binary()} | {error, term()}.
submit_case(Case) ->
gen_server:call(?SERVER, {submit_case, Case}, 30000).
%% @doc Assess complexity of a case. Pure function.
-spec assess(#sanhedrin_case{}) -> {panel, pos_integer()}.
assess(#sanhedrin_case{complexity = simple}) -> {panel, ?PANEL_SIMPLE};
assess(#sanhedrin_case{complexity = monetary}) -> {panel, ?PANEL_MONETARY};
assess(#sanhedrin_case{complexity = capital}) -> {panel, ?PANEL_CAPITAL};
assess(#sanhedrin_case{complexity = novel}) -> {panel, ?PANEL_NOVEL}.
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([]) ->
{ok, #state{}}.
handle_call({submit_case, Case}, _From, State) ->
%% First check halt conditions
case halt_monitor:check_case(Case) of
clear ->
%% Assess complexity and route
{panel, Size} = assess(Case),
Timeout = timeout_for(Case#sanhedrin_case.complexity),
case panel_sup:start_panel(Case, Size) of
{ok, PanelPid} ->
%% Start deliberation asynchronously
panel:deliberate_async(PanelPid, Timeout),
NewActive = maps:put(
Case#sanhedrin_case.case_ref,
PanelPid,
State#state.active_cases
),
{reply,
{ok, Case#sanhedrin_case.case_ref},
State#state{
active_cases = NewActive,
case_count = State#state.case_count + 1
}};
{error, Reason} ->
{reply, {error, {panel_start_failed, Reason}}, State}
end;
{halt, Condition, Reason} ->
{reply, {error, {halted, Condition, Reason}}, State}
end;
handle_call(_Request, _From, State) ->
{reply, {error, unknown_request}, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({panel_decided, CaseRef, Decision}, State) ->
%% Panel has reached a decision — store via clerk
clerk:store_decision(CaseRef, Decision),
NewActive = maps:remove(CaseRef, State#state.active_cases),
{noreply, State#state{active_cases = NewActive}};
handle_info(_Info, State) ->
{noreply, State}.
%%====================================================================
%% Internal
%%====================================================================
timeout_for(simple) -> ?TIMEOUT_SIMPLE;
timeout_for(monetary) -> ?TIMEOUT_MONETARY;
timeout_for(capital) -> ?TIMEOUT_CAPITAL;
timeout_for(novel) -> ?TIMEOUT_NOVEL.