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>
112 lines
3.9 KiB
Erlang
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.
|