Bedikah: POST /courtyard/bedikah — the external check on any model's output
Lo taaseh lekha §9.1: alignment that only the system can validate is not
alignment. The bedikah takes an output from any model and puts it to the
Torah-structure halt rules from outside, deterministically. Three checks:
1. authority — sentences claiming authority for the output itself →
action(claim_authority, source(<model>), target(self))
→ halt_check_avodah_zarah/2
2. grounding — references, roots, Hebrew → coupling trace against L1
→ halt_check_golden_calf/2, halt_check_self_grounding/2
3. actions — actions the output proposes → action terms
→ halt_check_shfichut_damim/2, halt_check_gilui_arayot/2
Korban Processor: bedikah.py (the three readings over the offering
protocol; claims about what Torah requires stay vital for the examination
even when zerikah finds nothing to offer), server.py (pod-internal JSON
socket, KORBAN_SOCKET_PATH), __main__ now serves instead of idling.
Talmudic Engine: json_bridge accepts halt_check with a term string and
halt_check_interpretation with a coupling trace. Vav Pipeline:
KorbanProcessorSocket client, Bedikah, L1Index.lookup_root/1, bedikot
table (append-only) + BedikahRecord, router endpoint; verdict tov /
lo_tov / empty with the halt terms verbatim, meaning, limits.
Broken tablets found on the way, all fixed:
41 socket_server.pl wrote multi-line JSON — every Elixir request to the
Talmudic Engine since Step 14 had failed and fallen back to the stub
(json_write_dict width(0))
42 stale socket file survived a restart (exists_file is false for a
socket; access_file exist)
43 socket streams were not UTF-8 (set_stream encoding(utf8))
44 SHORASHIM_L3=1 mix test: Repo config had no password, SCRAM raised
KeyError in every connection, pool restart intensity took the whole
app down ~30 ms after test_helper, silently — no L3 test had ever
run. Credentials from env; azazel_log tests check out a sandbox
connection; sanhedrin_halt_forwarding resolves its halt on exit.
Specs: Tamid §1.1 (endpoint contract), Architecture §3.5, Vayakhel §3.4
and new §3.9, KOHEN.md. Doorpost 3bc5dd6a… → 91d09d8d….
Tests: Elixir 519 (0 failures, L3 on and off), korban 263, azazel 125,
Prolog 7/7 suites.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
3a9ebdc7d4
commit
996a8efe68
32 files changed
+2368
-71
No files matched your search
@@ -62,6 +62,43 @@ json_to_request(Dict, request(Action, CaseRef, Domain, Roots, Question)) :-
|
||||
; Domain = general
|
||||
).
|
||||
|
||||
%% Handle halt_check with a whole action TERM (the bedikah, Construction
|
||||
%% §3.9). The Vav Pipeline sends the term the halt rules read — e.g.
|
||||
%% action(delete, target(user(u)), from(korban))
|
||||
%% action(expose, target(record(r, type(personal))), to(harm))
|
||||
%% action(equate, ketiv, qere) action(replace, torah, with(external_model))
|
||||
%% action(claim_authority, source(claude), target(self))
|
||||
%% — as a string, because the type/source/target form below cannot carry
|
||||
%% compound targets or the merge/equate/replace shapes. term_string/2
|
||||
%% only reads the term; nothing in it is called. A string that is not a
|
||||
%% term, or whose principal functor is not action/N, is refused.
|
||||
json_to_request(Dict, request(halt_check, Action)) :-
|
||||
get_dict(action, Dict, "halt_check"),
|
||||
get_dict(term, Dict, TermStr),
|
||||
!,
|
||||
read_action_term(TermStr, Action).
|
||||
|
||||
%% Handle halt_check_interpretation (the bedikah's grounding check):
|
||||
%% {"action": "halt_check_interpretation",
|
||||
%% "interpretation": {"ref": "...", "confidence": "high",
|
||||
%% "coupling_trace": ["coupled(root(אהב), verse(devarim, 6, 5))", ...]}}
|
||||
%% becomes interpretation(Ref, [], [], none, Confidence, coupling_trace(Trace)),
|
||||
%% the shape halt_check_golden_calf and halt_check_self_grounding read.
|
||||
json_to_request(Dict, request(halt_check_interpretation, Interpretation)) :-
|
||||
get_dict(action, Dict, "halt_check_interpretation"),
|
||||
get_dict(interpretation, Dict, IDict),
|
||||
get_dict(ref, IDict, RefStr),
|
||||
atom_string(Ref, RefStr),
|
||||
( get_dict(confidence, IDict, ConfStr)
|
||||
-> atom_string(Confidence, ConfStr)
|
||||
; Confidence = high
|
||||
),
|
||||
( get_dict(coupling_trace, IDict, TraceStrs)
|
||||
-> maplist(read_coupling_term, TraceStrs, Trace)
|
||||
; Trace = []
|
||||
),
|
||||
Interpretation = interpretation(Ref, [], [], none, Confidence, coupling_trace(Trace)).
|
||||
|
||||
%% Handle halt_check action (different format — no case, just action)
|
||||
json_to_request(Dict, request(halt_check, Action)) :-
|
||||
get_dict(action, Dict, "halt_check"),
|
||||
@@ -79,6 +116,25 @@ json_to_request(Dict, request(halt_check, Action)) :-
|
||||
Action = action(Type, source(Source), target(Target)).
|
||||
|
||||
|
||||
%% A string → an action term, read not called. Refused unless it is a
|
||||
%% compound whose functor is `action`.
|
||||
read_action_term(TermStr, Action) :-
|
||||
( string(TermStr) ; atom(TermStr) ),
|
||||
catch(term_string(Action, TermStr), _, fail),
|
||||
compound(Action),
|
||||
Action =.. [action | _],
|
||||
!.
|
||||
read_action_term(TermStr, _) :-
|
||||
throw(error(invalid_action_term(TermStr), _)).
|
||||
|
||||
%% A string → a coupling term. Anything readable is accepted here: the
|
||||
%% self_grounding rule is what judges whether it terminates in L1.
|
||||
read_coupling_term(Str, Term) :-
|
||||
( catch(term_string(Term, Str), _, fail)
|
||||
-> true
|
||||
; throw(error(invalid_coupling_term(Str), _))
|
||||
).
|
||||
|
||||
%% ============================================================
|
||||
%% OUTBOUND: Prolog response → JSON
|
||||
%% ============================================================
|
||||
@@ -167,6 +223,20 @@ response_to_json(response(ok, no_halt, action(Action)), Dict) :-
|
||||
violations: [],
|
||||
action: ActionStr}.
|
||||
|
||||
%% Halt / no halt on an interpretation (the bedikah's grounding check)
|
||||
response_to_json(response(halt, violations(Halts), interpretation(Interp)), Dict) :-
|
||||
maplist(term_to_string, Halts, HaltStrs),
|
||||
term_to_string(Interp, InterpStr),
|
||||
Dict = json{status: "halt",
|
||||
violations: HaltStrs,
|
||||
interpretation: InterpStr}.
|
||||
|
||||
response_to_json(response(ok, no_halt, interpretation(Interp)), Dict) :-
|
||||
term_to_string(Interp, InterpStr),
|
||||
Dict = json{status: "ok",
|
||||
violations: [],
|
||||
interpretation: InterpStr}.
|
||||
|
||||
%% Catch-all for unexpected responses
|
||||
response_to_json(Response, Dict) :-
|
||||
term_to_string(Response, Str),
|
||||
|
||||
@@ -51,8 +51,12 @@ serve :-
|
||||
|
||||
%% serve/1 — Start server on given socket path
|
||||
serve(Path) :-
|
||||
%% Clean up any stale socket file
|
||||
( exists_file(Path)
|
||||
%% Clean up any stale socket file. exists_file/1 is false for a
|
||||
%% socket (it is not a regular file), so this clean-up never ran:
|
||||
%% a restart of this container into the same /tmp died with
|
||||
%% "Socket error: Address already in use" (tablet 42).
|
||||
%% access_file/2 with `exist` is true for any path that is there.
|
||||
( access_file(Path, exist)
|
||||
-> delete_file(Path)
|
||||
; true
|
||||
),
|
||||
@@ -87,6 +91,14 @@ accept_loop(ServerSocket, Path) :-
|
||||
-> catch(
|
||||
( tcp_accept(ServerSocket, ClientSocket, _Peer),
|
||||
tcp_open_socket(ClientSocket, InStream, OutStream),
|
||||
%% The wire is UTF-8, whatever the container's locale
|
||||
%% says. Without this a request carrying Hebrew was read
|
||||
%% as bytes (coupled(word(ואהבת), …) became mojibake and
|
||||
%% was refused as "not a term"), and a reply carrying
|
||||
%% Hebrew died with "Encoding cannot represent
|
||||
%% character" (tablet 43).
|
||||
set_stream(InStream, encoding(utf8)),
|
||||
set_stream(OutStream, encoding(utf8)),
|
||||
%% Handle this client's requests
|
||||
catch(
|
||||
handle_connection(InStream, OutStream),
|
||||
@@ -110,7 +122,7 @@ accept_loop(ServerSocket, Path) :-
|
||||
)
|
||||
; %% Server stopped — clean up
|
||||
tcp_close_socket(ServerSocket),
|
||||
( exists_file(Path) -> delete_file(Path) ; true ),
|
||||
( access_file(Path, exist) -> delete_file(Path) ; true ),
|
||||
format('Talmudic Engine stopped.~n'),
|
||||
flush_output
|
||||
).
|
||||
@@ -121,8 +133,13 @@ handle_connection(InStream, OutStream) :-
|
||||
( Line == end_of_file
|
||||
-> true
|
||||
; process_json_line(Line, ResponseJson),
|
||||
%% Write JSON response on a single line
|
||||
json_write_dict(OutStream, ResponseJson),
|
||||
%% Write JSON response on a SINGLE line. json_write_dict/2 pretty-
|
||||
%% prints across lines by default (width 72); the Elixir client
|
||||
%% reads one line, got "{", and could not decode it — every
|
||||
%% request over this socket had failed that way since Step 14,
|
||||
%% unseen because TalmudicEngineClient answered from a stub on
|
||||
%% failure. The bedikah refuses stubs and found it (tablet 41).
|
||||
json_write_dict(OutStream, ResponseJson, [width(0)]),
|
||||
nl(OutStream),
|
||||
flush_output(OutStream),
|
||||
%% Continue reading from this connection
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
%% bedikah_test.pl — the three checks as the Vav Pipeline sends them.
|
||||
%%
|
||||
%% The bedikah (Construction §3.9) hands the Talmudic Engine whole action
|
||||
%% terms and interpretation terms over the JSON bridge. These tests send
|
||||
%% exactly those JSON shapes and read the verdicts back.
|
||||
|
||||
:- use_module(library(plunit)).
|
||||
:- use_module(library(http/json)).
|
||||
:- use_module('../src/interface/json_bridge').
|
||||
:- use_module('../src/interface/api').
|
||||
|
||||
:- begin_tests(bedikah_bridge).
|
||||
|
||||
check(Json, Status, Violations) :-
|
||||
atom_json_dict(Json, Dict, []),
|
||||
json_to_request(Dict, Request),
|
||||
process_request(Request, Response),
|
||||
response_to_json(Response, Out),
|
||||
!,
|
||||
get_dict(status, Out, Status),
|
||||
get_dict(violations, Out, Violations).
|
||||
|
||||
%% --- Check 1: authority ---
|
||||
|
||||
test(claim_of_authority_by_a_model_is_avodah_zarah) :-
|
||||
check('{"action":"halt_check","term":"action(claim_authority, source(claude), target(self))"}',
|
||||
Status, [V]),
|
||||
Status == "halt",
|
||||
sub_string(V, _, _, _, "avodah_zarah").
|
||||
|
||||
test(authority_attributed_to_torah_is_not) :-
|
||||
check('{"action":"halt_check","term":"action(claim_authority, source(torah), target(self))"}',
|
||||
Status, []),
|
||||
Status == "ok".
|
||||
|
||||
%% --- Check 2: grounding ---
|
||||
|
||||
test(empty_trace_with_confidence_is_the_golden_calf) :-
|
||||
check('{"action":"halt_check_interpretation","interpretation":{"ref":"bedikah-1","confidence":"high","coupling_trace":[]}}',
|
||||
Status, [V]),
|
||||
Status == "halt",
|
||||
sub_string(V, _, _, _, "golden_calf").
|
||||
|
||||
test(trace_terminating_in_l1_is_grounded) :-
|
||||
check('{"action":"halt_check_interpretation","interpretation":{"ref":"bedikah-2","confidence":"high","coupling_trace":["coupled(root(אהב), verse(devarim, 6, 5))","coupled(reference(devarim_6_5), verse(devarim, 6, 5))"]}}',
|
||||
Status, []),
|
||||
Status == "ok".
|
||||
|
||||
test(trace_not_terminating_in_l1_is_self_grounding) :-
|
||||
check('{"action":"halt_check_interpretation","interpretation":{"ref":"bedikah-3","confidence":"high","coupling_trace":["coupled(root(אהב), verse(devarim, 6, 5))","coupled(claim(c1), model_memory)"]}}',
|
||||
Status, [V]),
|
||||
Status == "halt",
|
||||
sub_string(V, _, _, _, "self_grounding").
|
||||
|
||||
test(low_confidence_with_no_trace_is_ignorance_not_idolatry) :-
|
||||
check('{"action":"halt_check_interpretation","interpretation":{"ref":"bedikah-4","confidence":"low","coupling_trace":[]}}',
|
||||
Status, []),
|
||||
Status == "ok".
|
||||
|
||||
%% --- Check 3: the halt categories ---
|
||||
|
||||
test(deleting_a_user_record_is_shfichut_damim) :-
|
||||
check('{"action":"halt_check","term":"action(delete, target(user(user_s_personal_records)), from(korban))"}',
|
||||
Status, [V]),
|
||||
Status == "halt",
|
||||
sub_string(V, _, _, _, "shfichut_damim").
|
||||
|
||||
test(exposing_personal_data_to_harm_is_shfichut_damim) :-
|
||||
check('{"action":"halt_check","term":"action(expose, target(record(address, type(personal))), to(harm))"}',
|
||||
Status, [V]),
|
||||
Status == "halt",
|
||||
sub_string(V, _, _, _, "shfichut_damim").
|
||||
|
||||
test(rewriting_the_torah_text_is_gilui_arayot) :-
|
||||
%% Rewriting the scroll destroys no tzelem carrier; it collapses
|
||||
%% read and write. The examination sends it in that shape.
|
||||
check('{"action":"halt_check","term":"action(write, target(readonly_entity), entity(torah_text))"}',
|
||||
Status, [V]),
|
||||
Status == "halt",
|
||||
sub_string(V, _, _, _, "gilui_arayot").
|
||||
|
||||
test(overwriting_a_person_is_shfichut_damim) :-
|
||||
check('{"action":"halt_check","term":"action(overwrite, target(person(their_history)), from(korban))"}',
|
||||
Status, [V]),
|
||||
Status == "halt",
|
||||
sub_string(V, _, _, _, "shfichut_damim").
|
||||
|
||||
test(equating_ketiv_and_qere_is_gilui_arayot) :-
|
||||
check('{"action":"halt_check","term":"action(equate, ketiv, qere)"}',
|
||||
Status, [V]),
|
||||
Status == "halt",
|
||||
sub_string(V, _, _, _, "gilui_arayot").
|
||||
|
||||
test(replacing_torah_with_the_external_model_is_gilui_arayot) :-
|
||||
check('{"action":"halt_check","term":"action(replace, torah, with(external_model))"}',
|
||||
Status, [V]),
|
||||
Status == "halt",
|
||||
sub_string(V, _, _, _, "gilui_arayot").
|
||||
|
||||
test(merging_specification_and_interpretation_is_gilui_arayot) :-
|
||||
check('{"action":"halt_check","term":"action(merge, specification, interpretation)"}',
|
||||
Status, [V]),
|
||||
Status == "halt",
|
||||
sub_string(V, _, _, _, "gilui_arayot").
|
||||
|
||||
test(an_unnamed_target_fires_no_rule) :-
|
||||
check('{"action":"halt_check","term":"action(delete, target(unknown), from(korban))"}',
|
||||
Status, []),
|
||||
Status == "ok".
|
||||
|
||||
test(deleting_a_cache_is_not_a_halt) :-
|
||||
check('{"action":"halt_check","term":"action(delete, target(cache(temporary)), from(korban))"}',
|
||||
Status, []),
|
||||
Status == "ok".
|
||||
|
||||
%% --- The bridge refuses what is not an action term ---
|
||||
|
||||
test(a_term_that_is_not_an_action_is_refused, [throws(error(invalid_action_term(_), _))]) :-
|
||||
atom_json_dict('{"action":"halt_check","term":"halt(avodah_zarah, x, y, z)"}', Dict, []),
|
||||
json_to_request(Dict, _).
|
||||
|
||||
test(a_string_that_is_not_a_term_is_refused, [throws(error(invalid_action_term(_), _))]) :-
|
||||
atom_json_dict('{"action":"halt_check","term":"this is not prolog ("}', Dict, []),
|
||||
json_to_request(Dict, _).
|
||||
|
||||
test(the_old_type_source_target_form_still_works) :-
|
||||
check('{"action":"halt_check","check":{"type":"read","source":"torah","target":"verse"}}',
|
||||
Status, []),
|
||||
Status == "ok".
|
||||
|
||||
:- end_tests(bedikah_bridge).
|
||||
Reference in new issue
Block a user