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:
Scott KarpandClaude Fable 5.1 committed 2026-10-06 15:06:58 +00:00
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),