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