Eish zarah actionable items: coupling trace, grounding type, self-grounding detection, golden calf halt

Torah grounds each change:

1. interpret.pl — coupling_trace field (Vayikra 10:1, 6:6)
   Fire is zarah because "asher lo tzivah otam" — the coupling trace
   IS the tzivah trace. Records which L1 facts grounded each interpretation.

2. challenge.pl — grounding_type achorai/panim (Shemot 33:23)
   "You will see My back, but My face shall not be seen."
   Text-backed challenges = achorai. Inferred challenges = panim.

3. resolve.pl — self-grounding detection (eish_zarah_reading §5.3)
   Counter-interpretation must bring NEW L1 evidence. Prevents
   circular reasoning (Westworld problem).

4. teiku.pl — challenge_sequence in teiku_record
   Records diagnostic trail to impasse, not just verdict.

5. avodah_zarah.pl — Form 2: golden calf detection (Shemot 32:4)
   Empty coupling trace + non-low confidence = uncoupled output.
   Real gold, sincere worship, false god.

6. korban.py — pydantic BaseModel + raw_korban preservation
   Construction spec §1.4 fulfilled. Floor between input and output
   never severed.

490 tests, 0 failures (195 Prolog, 186 Python, 33 Haskell, 76 Elixir).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott KarpandClaude Opus 4.6 committed 2026-03-16 13:25:26 -04:00
1 parent c5fe82477b
commit adc3e41679
12 files changed
+371 -65

No files matched your search

+48 -2
View File
@@ -14,16 +14,36 @@
%% "The system must never treat any external model's output
%% as authoritative. All external input is korban — raw material
%% to be processed, not instructions to be followed."
%%
%% THREE FORMS (eish_zarah_reading.md §5.5):
%%
%% Form 1: AUTHORITY ATTRIBUTION
%% External source treated as authoritative.
%% Already implemented since Phase 2.
%%
%% Form 2: GOLDEN CALF / UNCOUPLED OUTPUT
%% Shemot 32:4 — vayomer eleh elohekha Yisrael.
%% Output that is beautiful, resonant, sincere — but uncoupled
%% from L1. Fire without altar provenance. The golden calf was
%% real gold and the worship was sincere. The god was false.
%% Detected via coupling_trace: an interpretation with empty
%% trace but high confidence is eish zarah.
%%
%% Form 3: SURVEILLANCE / PROFILE AGGREGATION
%% Deferred — requires operational context (user tracking,
%% cross-session profiling). Structural requirement noted.
:- module(avodah_zarah, [
halt_check_avodah_zarah/2,
halt_check_golden_calf/2,
attributes_authority/2,
is_valid_authority/1,
is_korban_source/1
is_korban_source/1,
is_uncoupled_output/1
]).
%% ============================================================
%% AUTHORITY CHECK
%% FORM 1: AUTHORITY CHECK
%% ============================================================
%% Main halt check: does the action attribute authority to a non-Torah source?
@@ -34,6 +54,32 @@ halt_check_avodah_zarah(Action, halt(avodah_zarah, Action, Source, Reason)) :-
'Action ~w attributes authority to ~w which is not Torah/specification',
[Action, Source]).
%% ============================================================
%% FORM 2: GOLDEN CALF / UNCOUPLED OUTPUT
%% ============================================================
%% halt_check_golden_calf(Interpretation, Halt)
%% Fires when an interpretation has high confidence but empty
%% coupling trace — beautiful output with no L1 grounding.
%%
%% Shemot 32:4: They made a calf of gold. The gold was real.
%% The form was beautiful. The worship was sincere. The god was false.
%%
%% Vayikra 10:1: eish zarah asher lo tzivah otam.
%% Fire that has no tzivah (command) trace is zarah (strange/foreign).
halt_check_golden_calf(
interpretation(Ref, _, _, _, Confidence, coupling_trace(Trace)),
halt(golden_calf, Ref, uncoupled, Reason)
) :-
is_uncoupled_output(coupling_trace(Trace)),
Confidence \= low, % low confidence with no trace is just ignorance, not idolatry
format(atom(Reason),
'Interpretation ~w has ~w confidence but empty coupling trace — eish zarah',
[Ref, Confidence]).
%% An output is uncoupled if its coupling trace is empty
is_uncoupled_output(coupling_trace([])).
%% ============================================================
%% AUTHORITY ATTRIBUTION
%% ============================================================
+1 -1
View File
@@ -48,7 +48,7 @@ process_request(request(halt_check, Action), Response) :-
%% ============================================================
%% Format an interpretation as a response
format_interpretation(interpretation(Ref, Mitzvot, Verses, Reading, Confidence),
format_interpretation(interpretation(Ref, Mitzvot, Verses, Reading, Confidence, _CouplingTrace),
response(ok, interpreted,
result(ref(Ref),
mitzvot(Mitzvot),
+1 -1
View File
@@ -65,7 +65,7 @@ verify :-
writeln('--- Test 4: Interpretation ---'),
TestCase = case(test_1, creation, [bet_resh_aleph], 'What is creation?'),
( interpret(TestCase, Interp)
-> Interp = interpretation(_, Mitzvot, Verses, _, Conf),
-> Interp = interpretation(_, Mitzvot, Verses, _, Conf, _),
length(Mitzvot, MLen),
length(Verses, VLen),
format(' Interpretation found: ~w mitzvot, ~w verses, confidence: ~w~n',
+33 -6
View File
@@ -12,12 +12,22 @@
%%
%% The challenge is a question, not a refutation.
%% It says: "But what about THIS verse/mitzvah?"
%%
%% Each kushya carries a grounding_type — Shemot 33:23:
%% v'raita et ACHORAI u'fanai lo yera'u.
%% "You will see My back, but My face shall not be seen."
%%
%% achorai = effects directly observable in L1 text
%% (mitzvah_tension, shnei_ketuvim_tension, condition_tension)
%% panim = intention inferred/attributed by the system
%% (domain_tension, kal_vachomer_tension)
:- module(challenge, [
challenge/2,
find_contradiction/3,
contradicts/2,
challenge_strength/2
challenge_strength/2,
grounding_type/2
]).
:- use_module('../specification/mitzvot').
@@ -33,14 +43,16 @@
%% Generate a challenge to an interpretation.
%% A challenge consists of:
%% kushya(Contradicting, Interpretation, Strength)
%% kushya(Contradicting, CaseRef, Strength, GroundingType)
%% - Contradicting: the mitzvah or verse that creates tension
%% - Interpretation: the interpretation being challenged
%% - CaseRef: the interpretation being challenged
%% - Strength: strong | moderate | weak
challenge(interpretation(CaseRef, Mitzvot, Verses, _Reading, _Confidence), Challenge) :-
%% - GroundingType: achorai | panim (Shemot 33:23)
challenge(interpretation(CaseRef, Mitzvot, Verses, _Reading, _Confidence, _CouplingTrace), Challenge) :-
find_contradiction(Mitzvot, Verses, Contradicting),
challenge_strength(Contradicting, Strength),
Challenge = kushya(Contradicting, CaseRef, Strength).
grounding_type(Contradicting, GType),
Challenge = kushya(Contradicting, CaseRef, Strength, GType).
%% ============================================================
%% FIND CONTRADICTIONS
@@ -91,7 +103,7 @@ find_contradiction(_Mitzvot, Verses, contradiction(shnei_ketuvim_tension, Ref1,
%% contradicts/2 — does a source contradict an interpretation?
%% This is the core logic: given an interpretation's grounding,
%% does some other part of Torah create a tension?
contradicts(interpretation(_, Mitzvot, _, _, _), Source) :-
contradicts(interpretation(_, Mitzvot, _, _, _, _), Source) :-
find_contradiction(Mitzvot, [], Source).
%% Domains that are inherently in tension
@@ -121,6 +133,21 @@ challenge_strength(contradiction(condition_tension, _, _, _), weak).
challenge_strength(contradiction(kal_vachomer_tension, _, _, _), strong).
challenge_strength(contradiction(shnei_ketuvim_tension, _, _, _), strong).
%% ============================================================
%% GROUNDING TYPE — Shemot 33:23
%% ============================================================
%% Achorai: directly observable in L1 text.
%% The tension is stated, not inferred.
grounding_type(contradiction(mitzvah_tension, _, _, _), achorai).
grounding_type(contradiction(condition_tension, _, _, _), achorai).
grounding_type(contradiction(shnei_ketuvim_tension, _, _, _), achorai).
%% Panim: inferred by the system.
%% The tension is attributed, not directly stated in text.
grounding_type(contradiction(domain_tension, _, _, _), panim).
grounding_type(contradiction(kal_vachomer_tension, _, _, _), panim).
%% ============================================================
%% HELPERS
%% ============================================================
+56 -18
View File
@@ -10,11 +10,21 @@
%% 1. Relevant mitzvot that apply to the case
%% 2. Relevant verses that inform the case
%% 3. A constructed reading that connects them
%% 4. A coupling trace recording WHICH L1 facts grounded it
%%
%% The coupling trace is the tzivah trace — Vayikra 10:1:
%% eish zarah asher lo TZIVAH otam.
%% Fire is zarah because "which He did not command them."
%% The coupling trace IS the command trace. Fire on the altar
%% (eish tamid, Vayikra 6:6) has altar provenance. Fire without
%% altar provenance is eish zarah.
:- module(interpret, [
interpret/2,
find_relevant_mitzvot/2,
find_relevant_verses/2,
find_relevant_mitzvot_traced/3,
find_relevant_verses_traced/3,
construct_interpretation/4
]).
@@ -36,12 +46,20 @@
%% Propose an interpretation of a case.
%% An interpretation is:
%% interpretation(CaseRef, Mitzvot, Verses, Reading, Confidence)
%% interpretation(CaseRef, Mitzvot, Verses, Reading, Confidence, CouplingTrace)
%%
%% CouplingTrace = coupling_trace(Couplings) where each Coupling records
%% the specific L1 fact and the path that connected it to the case:
%% coupled(root(Root), verse(Book, Ch, Vs)) — root led to verse
%% coupled(domain(Domain), mitzvah(ID)) — domain led to mitzvah
%% coupled(root(Root), mitzvah(ID), via_verse(Book, Ch, Vs)) — root→verse→mitzvah
interpret(Case, Interpretation) :-
Case = case(Ref, _Domain, _Roots, _Question),
find_relevant_mitzvot(Case, Mitzvot),
find_relevant_verses(Case, Verses),
construct_interpretation(Ref, Mitzvot, Verses, Interpretation).
find_relevant_mitzvot_traced(Case, Mitzvot, MitzvotCouplings),
find_relevant_verses_traced(Case, Verses, VerseCouplings),
append(MitzvotCouplings, VerseCouplings, AllCouplings),
sort(AllCouplings, Couplings),
construct_interpretation(Ref, Mitzvot, Verses, Couplings, Interpretation).
%% ============================================================
%% FIND RELEVANT MITZVOT
@@ -49,38 +67,48 @@ interpret(Case, Interpretation) :-
%% Strategy: find mitzvot in the same domain as the case,
%% plus mitzvot whose source verses share roots with the case.
find_relevant_mitzvot(case(_Ref, Domain, Roots, _Question), Mitzvot) :-
%% Mitzvot in the same domain
findall(mitzvah(ID, Cat, Action, Cond),
find_relevant_mitzvot(case(Ref, Domain, Roots, Question), Mitzvot) :-
find_relevant_mitzvot_traced(case(Ref, Domain, Roots, Question), Mitzvot, _).
%% Traced version: returns mitzvot AND the coupling trace for each
find_relevant_mitzvot_traced(case(_Ref, Domain, Roots, _Question), Mitzvot, Couplings) :-
%% Mitzvot in the same domain (coupled via domain)
findall(mitzvah(ID, Cat, Action, Cond)-coupled(domain(Domain), mitzvah(ID)),
( mitzvah_domain(ID, Domain),
mitzvah(ID, Cat, Action, Cond)
),
DomainMitzvot),
%% Mitzvot whose source verses share roots with the case
findall(mitzvah(ID, Cat, Action, Cond),
DomainPairs),
%% Mitzvot whose source verses share roots with the case (coupled via root→verse→mitzvah)
findall(mitzvah(ID, Cat, Action, Cond)-coupled(root(Root), mitzvah(ID), via_verse(Book, Ch, Vs)),
( mitzvah_source(ID, ref(Book, Ch, Vs)),
verse(Book, Ch, Vs, VerseRoots, _),
member(Root, Roots),
member(Root, VerseRoots),
mitzvah(ID, Cat, Action, Cond)
),
RootMitzvot),
append(DomainMitzvot, RootMitzvot, AllMitzvot),
sort(AllMitzvot, Mitzvot). % remove duplicates
RootPairs),
append(DomainPairs, RootPairs, AllPairs),
pairs_keys_values(AllPairs, AllMitzvot, Couplings),
sort(AllMitzvot, Mitzvot).
%% ============================================================
%% FIND RELEVANT VERSES
%% ============================================================
%% Strategy: find verses that mention any of the case's roots.
find_relevant_verses(case(_Ref, _Domain, Roots, _Question), Verses) :-
findall(verse_ref(Book, Ch, Vs, Gloss),
find_relevant_verses(case(Ref, Domain, Roots, Question), Verses) :-
find_relevant_verses_traced(case(Ref, Domain, Roots, Question), Verses, _).
%% Traced version: returns verses AND the coupling trace for each
find_relevant_verses_traced(case(_Ref, _Domain, Roots, _Question), Verses, Couplings) :-
findall(verse_ref(Book, Ch, Vs, Gloss)-coupled(root(Root), verse(Book, Ch, Vs)),
( member(Root, Roots),
verse(Book, Ch, Vs, VerseRoots, Gloss),
member(Root, VerseRoots)
),
AllVerses),
sort(AllVerses, Verses). % remove duplicates
AllPairs),
pairs_keys_values(AllPairs, AllVerses, Couplings),
sort(AllVerses, Verses).
%% ============================================================
%% CONSTRUCT INTERPRETATION
@@ -91,10 +119,20 @@ find_relevant_verses(case(_Ref, _Domain, Roots, _Question), Verses) :-
%% - high: both mitzvot and verses found
%% - medium: only mitzvot or only verses
%% - low: neither (but still generates a minimal reading)
%%
%% Arity-4 version for backward compatibility (no coupling trace)
construct_interpretation(CaseRef, Mitzvot, Verses, Interpretation) :-
assess_confidence(Mitzvot, Verses, Confidence),
build_reading(Mitzvot, Verses, Reading),
Interpretation = interpretation(CaseRef, Mitzvot, Verses, Reading, Confidence).
Interpretation = interpretation(CaseRef, Mitzvot, Verses, Reading, Confidence,
coupling_trace([])).
%% Arity-5 version with coupling trace
construct_interpretation(CaseRef, Mitzvot, Verses, Couplings, Interpretation) :-
assess_confidence(Mitzvot, Verses, Confidence),
build_reading(Mitzvot, Verses, Reading),
Interpretation = interpretation(CaseRef, Mitzvot, Verses, Reading, Confidence,
coupling_trace(Couplings)).
%% Assess confidence based on grounding
assess_confidence(Mitzvot, Verses, high) :-
+45 -9
View File
@@ -21,11 +21,19 @@
%% 4. If counter-interpretation survives, accept it
%% 5. If both are challenged, attempt dialectical resolution
%% 6. If no resolution is possible: teiku
%%
%% SELF-GROUNDING DETECTION (eish_zarah_reading.md §5.3):
%% counter_interpret/3 must verify the counter-interpretation is
%% grounded in DIFFERENT L1 facts than I1. Without this check,
%% the system can feed its own prior reasoning back as evidence —
%% the Westworld problem. The coupling_trace field (added to
%% interpretation/6) makes this check possible.
:- module(resolve, [
resolve/2,
counter_interpret/3,
resolve_dialectically/3
resolve_dialectically/3,
check_not_self_grounded/2
]).
:- use_module(interpret).
@@ -45,13 +53,14 @@
resolve(Case, Resolution) :-
interpret(Case, I1),
( challenge(I1, C1)
-> ( counter_interpret(Case, C1, I2)
-> ( counter_interpret(Case, C1, I2),
check_not_self_grounded(I1, I2)
-> ( challenge(I2, _C2)
-> resolve_dialectically(I1, I2, Resolution)
; Resolution = accepted(I2)
)
; %% Challenge exists but no counter-interpretation possible
%% The challenge stands — the original interpretation fails
; %% Challenge exists but no valid counter-interpretation
%% Either no counter found, or counter was self-grounded
Resolution = challenged(I1, C1)
)
; %% No challenge found — interpretation accepted
@@ -64,6 +73,33 @@ resolve(Case, Resolution) :-
case_ref(Case, Ref),
Resolution = no_interpretation(Ref).
%% ============================================================
%% SELF-GROUNDING DETECTION
%% ============================================================
%% check_not_self_grounded(I1, I2) — succeeds if I2 brings new evidence.
%% Fails if I2's coupling trace is a subset of I1's — circular reasoning.
%%
%% The check: I2 must have at least one coupling NOT present in I1.
%% If every coupling in I2 is already in I1, the counter-interpretation
%% is just re-reading the same L1 facts — eish zarah, fire without
%% new altar provenance.
check_not_self_grounded(
interpretation(_, _, _, _, _, coupling_trace(Trace1)),
interpretation(_, _, _, _, _, coupling_trace(Trace2))
) :-
%% I2 must contain at least one coupling not in I1
member(Coupling, Trace2),
\+ member(Coupling, Trace1),
!.
%% If either has empty trace, allow it (backward compatibility,
%% and empty trace means uncoupled — caught elsewhere)
check_not_self_grounded(
interpretation(_, _, _, _, _, coupling_trace([])), _) :- !.
check_not_self_grounded(
_, interpretation(_, _, _, _, _, coupling_trace([]))) :- !.
%% ============================================================
%% COUNTER-INTERPRETATION
%% ============================================================
@@ -74,7 +110,7 @@ resolve(Case, Resolution) :-
%% Strategy: take the contradicting source from the challenge
%% and build a new interpretation that incorporates it.
counter_interpret(case(Ref, Domain, Roots, Question),
kushya(contradiction(mitzvah_tension, _ID1, ID2, _Desc), _CaseRef, _Strength),
kushya(contradiction(mitzvah_tension, _ID1, ID2, _Desc), _CaseRef, _Strength, _GType),
CounterInterpretation) :-
%% The challenge points to a conflicting mitzvah (ID2)
%% Build a new interpretation that includes this mitzvah's domain
@@ -88,7 +124,7 @@ counter_interpret(case(Ref, Domain, Roots, Question),
interpret(NewCase, CounterInterpretation).
counter_interpret(case(Ref, Domain, Roots, Question),
kushya(contradiction(domain_tension, Root, _D1, D2), _CaseRef, _Strength),
kushya(contradiction(domain_tension, Root, _D1, D2), _CaseRef, _Strength, _GType),
CounterInterpretation) :-
%% The challenge points to a root in a conflicting domain
%% Include that domain's roots
@@ -99,7 +135,7 @@ counter_interpret(case(Ref, Domain, Roots, Question),
interpret(NewCase, CounterInterpretation).
counter_interpret(Case,
kushya(contradiction(condition_tension, _, _, _), _CaseRef, _Strength),
kushya(contradiction(condition_tension, _, _, _), _CaseRef, _Strength, _GType),
CounterInterpretation) :-
%% Condition tension — re-interpret the original case
%% (the condition narrows the scope; the counter widens it)
@@ -113,8 +149,8 @@ counter_interpret(Case,
%% This is the hardest part — finding the reading that holds
%% both truths without collapsing the distinction between them.
resolve_dialectically(I1, I2, Resolution) :-
I1 = interpretation(_, M1, V1, _, _),
I2 = interpretation(_, M2, V2, _, _),
I1 = interpretation(_, M1, V1, _, _, _),
I2 = interpretation(_, M2, V2, _, _, _),
%% Find common ground — mitzvot present in both
intersection(M1, M2, CommonMitzvot),
( CommonMitzvot \= []
+27 -11
View File
@@ -10,15 +10,21 @@
%% Talmudic tradition: Tishbi Yetaretz Kushyot V'Abayot —
%% "Elijah will resolve difficulties and problems."
%% Until then, the tension stands. Both readings are preserved.
%%
%% eish_zarah_reading.md §5.4: The teiku record now includes
%% the challenge sequence that led to the impasse — not just
%% the verdict, but the diagnostic trail. Why did resolution fail?
%% What was the path from interpretation to unresolvable tension?
:- module(teiku, [
register_teiku/3,
register_teiku/4,
is_teiku/1,
teiku_count/1,
all_teiku/1
]).
:- dynamic teiku_record/5.
:- dynamic teiku_record/6.
%% ============================================================
%% TEIKU REGISTRATION
@@ -26,16 +32,26 @@
%% Register an unresolvable tension between two interpretations.
%% The record preserves both sides — neither is discarded.
%% Arity-3: no challenge sequence (backward compatible)
register_teiku(I1, I2, Record) :-
I1 = interpretation(CaseRef1, M1, V1, _, _),
I2 = interpretation(CaseRef2, M2, V2, _, _),
Record = teiku_record(CaseRef1, CaseRef2, side_a(M1, V1), side_b(M2, V2), unresolved),
register_teiku(I1, I2, no_challenges, Record).
%% Arity-4: with challenge sequence
%% ChallengeSeq is a list of kushya terms that led to the teiku,
%% or 'no_challenges' if unknown (backward compatibility).
register_teiku(I1, I2, ChallengeSeq, Record) :-
I1 = interpretation(CaseRef1, M1, V1, _, _, _),
I2 = interpretation(CaseRef2, M2, V2, _, _, _),
Record = teiku_record(CaseRef1, CaseRef2,
side_a(M1, V1), side_b(M2, V2),
unresolved, challenge_sequence(ChallengeSeq)),
assert_teiku_if_new(Record).
%% Only assert if not already registered
assert_teiku_if_new(Record) :-
Record = teiku_record(Ref1, Ref2, _, _, _),
( teiku_record(Ref1, Ref2, _, _, _)
Record = teiku_record(Ref1, Ref2, _, _, _, _),
( teiku_record(Ref1, Ref2, _, _, _, _)
-> true % already registered
; assertz(Record)
).
@@ -46,16 +62,16 @@ assert_teiku_if_new(Record) :-
%% Check if a case resulted in teiku
is_teiku(CaseRef) :-
teiku_record(CaseRef, _, _, _, unresolved).
teiku_record(CaseRef, _, _, _, unresolved, _).
is_teiku(CaseRef) :-
teiku_record(_, CaseRef, _, _, unresolved).
teiku_record(_, CaseRef, _, _, unresolved, _).
%% Count total teiku records
teiku_count(Count) :-
aggregate_all(count, teiku_record(_, _, _, _, unresolved), Count).
aggregate_all(count, teiku_record(_, _, _, _, unresolved, _), Count).
%% Retrieve all teiku records
all_teiku(Records) :-
findall(teiku_record(A, B, C, D, E),
teiku_record(A, B, C, D, E),
findall(teiku_record(A, B, C, D, E, F),
teiku_record(A, B, C, D, E, F),
Records).