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>
187 lines
6.5 KiB
Erlang
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]
|
|
}.
|