tablet 55: the row that was a label — the Sanhedrin's example request crashed its own gate
Verified live 2026-10-19: Tamid's own example for POST /chatzer/sanhedrin
(a case with `sides`) answered 500. The router passed the sides LIST as
the case context; halt_monitor:check_avodah_zarah/1 reads a map, maps:get
raised, and the halt monitor and escalation gen_servers died with the
caller's input (supervisor restarted them; health stayed 8/8). The router
then matched {:ok, decision} on the error and made a 500 of its own.
- sanhedrin/src/halt_monitor.erl: do_check_case refuses a non-map
context as {halt, malformed_case, _} and stays standing. EUnit 49/0
with the new halt_malformed_context_test.
- router.ex process_sanhedrin: sides validated at the kiyor (422 on a bad
shape), carried as context %{sides: sides}; a Sanhedrin refusal answers
422 SANHEDRIN_HALTED, a failure 503 SANHEDRIN_UNAVAILABLE. Two new
router tests, one asserting the gate processes survive the example.
Elixir 624/0.
- specs/broken_tablets/README.md: tablet 55, including what is NOT fixed
— a panel of one accepting on zero references under a grounded-
sounding sentence, the ignored complexity hint, no panel manna spent,
no L3 record. The row in Tamid is still larger than the thing behind it.
Doorpost pin unchanged (no @spec_files touched; scripts/doorpost_check.py
passes).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
04f61c1238
commit
7c40200dfc
5 files changed
+184
-24
No files matched your search
@@ -145,13 +145,29 @@ handle_info(_Info, State) ->
|
||||
%% Internal — gate-keeper checks
|
||||
%%====================================================================
|
||||
|
||||
do_check_case(Case) ->
|
||||
Context = Case#sanhedrin_case.context,
|
||||
%% The context is a map (authority_source, action, ...). Anything else is
|
||||
%% a malformed case, refused here — never reasoned about, never a crash.
|
||||
%% Tablet 55 (2026-10-19): the Tamid's own example request carried a
|
||||
%% LIST as context; maps:get raised, and this gen_server and escalation
|
||||
%% died with the caller's input. A gate that can be felled by what it is
|
||||
%% handed is not a gate. Fail closed, stay standing.
|
||||
do_check_case(#sanhedrin_case{context = Context}) when is_map(Context) ->
|
||||
with_chain([
|
||||
fun() -> check_avodah_zarah(Context) end,
|
||||
fun() -> check_shfichut_damim(Context) end,
|
||||
fun() -> check_gilui_arayot(Context) end
|
||||
]).
|
||||
]);
|
||||
do_check_case(#sanhedrin_case{context = Other}) ->
|
||||
{halt, malformed_case,
|
||||
iolist_to_binary([<<"case context must be a map, got ">>,
|
||||
atom_to_binary(type_name(Other), utf8)])}.
|
||||
|
||||
type_name(T) when is_list(T) -> list;
|
||||
type_name(T) when is_binary(T) -> binary;
|
||||
type_name(T) when is_atom(T) -> atom;
|
||||
type_name(T) when is_tuple(T) -> tuple;
|
||||
type_name(T) when is_number(T) -> number;
|
||||
type_name(_) -> other.
|
||||
|
||||
do_check_vote(_Vote) ->
|
||||
%% Vote-level halt detection is the responsibility of the upstream
|
||||
|
||||
@@ -58,6 +58,16 @@ halt_clear_test() ->
|
||||
?assertEqual(clear, halt_monitor:check_case(Case)),
|
||||
cleanup(ok).
|
||||
|
||||
%% Tablet 55: a non-map context is refused, and the monitor survives it.
|
||||
halt_malformed_context_test() ->
|
||||
{ok, _} = application:ensure_all_started(sanhedrin),
|
||||
Sides = [#{<<"position">> => <<"a">>, <<"evidence">> => [<<"x">>]}],
|
||||
Case = make_case_with_context(simple, <<"question">>, Sides),
|
||||
?assertMatch({halt, malformed_case, _}, halt_monitor:check_case(Case)),
|
||||
%% still standing, and still judging
|
||||
?assertEqual(clear, halt_monitor:check_case(make_case(simple, <<"q">>))),
|
||||
ok.
|
||||
|
||||
halt_avodah_zarah_test() ->
|
||||
setup(),
|
||||
Case = make_case_with_context(simple, <<"question">>,
|
||||
|
||||
Reference in new issue
Block a user