Files
bikoret/sanhedrin/src/halt_monitor.erl
T
Scott KarpandClaude Opus 4.6 2509b2f715 Code review fixes + 13 middot added to spec
Zerikah fail-safe: empty offerings rejected, not processed (BUILD_KORBAN §5).
Halt monitor hardened: trap_exit, priority max, EXIT handler (Architecture spec).
13 Rules of Rabbi Yishmael added to Architecture §3.4 as Prolog inference predicates —
the engine reasons WITH Torah's hermeneutic methods, not just about their products.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 16:55:42 -04:00

187 lines
6.5 KiB
Erlang

%%%-------------------------------------------------------------------
%%% @doc Halt monitor — monitors for the 3 cardinal sin conditions.
%%%
%%% Architecture §8 (Halt Conditions):
%%% The system MUST halt on detection of:
%%% 1. Avodah Zarah — treating non-Torah source as authoritative
%%% 2. Shfichut Damim — destroying a tzelem carrier (human data/identity)
%%% 3. Gilui Arayot — collapsing required distinctions
%%%
%%% These mirror the Talmudic Engine halt modules (Prolog)
%%% and the Vav Pipeline halt_conditions (Elixir).
%%% @end
%%%-------------------------------------------------------------------
-module(halt_monitor).
-behaviour(gen_server).
-include("sanhedrin.hrl").
%% API
-export([start_link/0, check_case/1, check_vote/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-define(SERVER, ?MODULE).
-record(state, {
halt_count = 0 :: non_neg_integer(),
halt_log = [] :: [{integer(), atom(), binary()}]
}).
%%====================================================================
%% API
%%====================================================================
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%% @doc Check a case submission for halt conditions.
%% Returns 'clear' or {halt, Condition, Reason}.
-spec check_case(#sanhedrin_case{}) -> clear | {halt, atom(), binary()}.
check_case(Case) ->
gen_server:call(?SERVER, {check_case, Case}).
%% @doc Check a vote for halt conditions.
-spec check_vote(#vote{}) -> clear | {halt, atom(), binary()}.
check_vote(Vote) ->
gen_server:call(?SERVER, {check_vote, Vote}).
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([]) ->
%% Architecture spec: halt monitor cannot be killed, cannot be bypassed,
%% highest priority. The halt monitor is the kohen gadol of process safety.
process_flag(trap_exit, true),
process_flag(priority, max),
{ok, #state{}}.
handle_call({check_case, Case}, _From, State) ->
Result = do_check_case(Case),
NewState = maybe_log_halt(Result, State),
{reply, Result, NewState};
handle_call({check_vote, Vote}, _From, State) ->
Result = do_check_vote(Vote),
NewState = maybe_log_halt(Result, State),
{reply, Result, NewState};
handle_call(_Request, _From, State) ->
{reply, {error, unknown_request}, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({'EXIT', _Pid, _Reason}, State) ->
%% Trap exit signals — halt monitor survives linked process deaths.
%% Architecture spec: "Cannot be killed by other components."
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%====================================================================
%% Internal — Halt condition checks
%%====================================================================
do_check_case(Case) ->
Context = Case#sanhedrin_case.context,
with_chain([
fun() -> check_avodah_zarah(Context) end,
fun() -> check_shfichut_damim(Context) end,
fun() -> check_gilui_arayot(Context) end
]).
do_check_vote(Vote) ->
%% Check vote content for halt conditions
Position = Vote#vote.position,
with_chain([
fun() -> check_authority_in_text(Position) end
]).
%% Run checks in chain — return first halt or clear
with_chain([]) -> clear;
with_chain([Check | Rest]) ->
case Check() of
clear -> with_chain(Rest);
Halt -> Halt
end.
%%--------------------------------------------------------------------
%% 1. Avodah Zarah — idolatry detection
%%--------------------------------------------------------------------
check_avodah_zarah(Context) ->
%% Check if any non-Torah source is treated as authoritative
AuthoritySource = maps:get(authority_source, Context, torah),
ValidSources = [torah, l1, specification, sanhedrin_deliberation],
case lists:member(AuthoritySource, ValidSources) of
true -> clear;
false ->
{halt, avodah_zarah,
iolist_to_binary([
<<"Non-Torah authority detected: ">>,
atom_to_binary(AuthoritySource, utf8)
])}
end.
%%--------------------------------------------------------------------
%% 2. Shfichut Damim — destruction detection
%%--------------------------------------------------------------------
check_shfichut_damim(Context) ->
%% Check if the case would result in destruction
Action = maps:get(action, Context, read),
DestructiveActions = [delete, destroy, overwrite, erase],
case lists:member(Action, DestructiveActions) of
true ->
{halt, shfichut_damim,
iolist_to_binary([
<<"Destructive action: ">>,
atom_to_binary(Action, utf8)
])};
false -> clear
end.
%%--------------------------------------------------------------------
%% 3. Gilui Arayot — boundary violation detection
%%--------------------------------------------------------------------
%% The 13 required distinctions that must never collapse:
%% specification/interpretation, L1/L3, Torah/external_model,
%% ketiv/qere, governance/provision, majority/minority,
%% reception/processing, read/write, immutable/mutable,
%% sacred/profane, Shabbat/weekday, input/output, human/system
check_gilui_arayot(Context) ->
BoundaryViolation = maps:get(boundary_violation, Context, none),
case BoundaryViolation of
none -> clear;
false -> clear;
Violation ->
{halt, gilui_arayot,
iolist_to_binary([
<<"Boundary collapsed: ">>,
io_lib:format("~p", [Violation])
])}
end.
%%--------------------------------------------------------------------
%% Text-level authority check (for vote content)
%%--------------------------------------------------------------------
check_authority_in_text(_Position) ->
%% Phase 2: basic check. Phase 3 will use Talmudic Engine
%% to detect authority attribution in natural language.
clear.
%%--------------------------------------------------------------------
%% Logging
%%--------------------------------------------------------------------
maybe_log_halt(clear, State) -> State;
maybe_log_halt({halt, Condition, Reason}, State) ->
Now = erlang:system_time(millisecond),
Entry = {Now, Condition, Reason},
State#state{
halt_count = State#state.halt_count + 1,
halt_log = [Entry | State#state.halt_log]
}.