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

@@ -3,14 +3,21 @@ Core offering protocol and data types.
Architecture §3.5: Korban Processor. Architecture §3.5: Korban Processor.
Construction spec §1.4: pydantic 2.6.1 for data validation. Construction spec §1.4: pydantic 2.6.1 for data validation.
eish_zarah_reading.md §5.6: Migrated from dataclass to pydantic BaseModel.
- Validators enforce structural constraints at the type level.
- ProcessedKorban stores the raw Korban (the severed floor is reconnected).
- The raw offering is preserved alongside the processed result so the
coupling between input and output is never severed.
""" """
from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from typing import List, Optional from typing import List, Optional
from pydantic import BaseModel, field_validator
class OfferingType(Enum):
class OfferingType(str, Enum):
"""Types of korbanot — each determines routing.""" """Types of korbanot — each determines routing."""
OLAH = "olah" # Total consumption — fully absorbed by system OLAH = "olah" # Total consumption — fully absorbed by system
SHELAMIM = "shelamim" # Shared — partial return to source SHELAMIM = "shelamim" # Shared — partial return to source
@@ -18,7 +25,7 @@ class OfferingType(Enum):
MINCHAH = "minchah" # Minimal input MINCHAH = "minchah" # Minimal input
class Provenance(Enum): class Provenance(str, Enum):
"""Source of the korban — who brought it.""" """Source of the korban — who brought it."""
CLAUDE = "claude" CLAUDE = "claude"
MISTRAL = "mistral" MISTRAL = "mistral"
@@ -28,24 +35,34 @@ class Provenance(Enum):
HUMAN = "human" HUMAN = "human"
@dataclass class Korban(BaseModel):
class Korban:
"""A raw offering before processing.""" """A raw offering before processing."""
raw_content: str raw_content: str
provenance: Provenance provenance: Provenance
offering_type: OfferingType offering_type: OfferingType
source_query: str # what prompted this output source_query: str # what prompted this output
@field_validator('raw_content')
@classmethod
def content_must_not_be_empty(cls, v: str) -> str:
if not v.strip():
raise ValueError("Empty korban — raw_content must not be blank")
return v
@dataclass
class ProcessedKorban: class ProcessedKorban(BaseModel):
"""A processed offering — vital content extracted, propositions generated.""" """A processed offering — vital content extracted, propositions generated.
The raw_korban field preserves the original offering alongside the
processed result. The floor between input and output is never severed.
"""
vital_content: str # extracted essence vital_content: str # extracted essence
propositions: List[str] # Prolog-format logical statements propositions: List[str] # Prolog-format logical statements
provenance: Provenance provenance: Provenance
offering_type: OfferingType offering_type: OfferingType
discarded: List[str] = field(default_factory=list) # what was stripped discarded: List[str] = [] # what was stripped
source_query: str = "" source_query: str = ""
raw_korban: Optional[Korban] = None # the original offering, preserved
def process_korban( def process_korban(
@@ -88,6 +105,7 @@ def process_korban(
offering_type=offering_type, offering_type=offering_type,
discarded=discarded, discarded=discarded,
source_query=query, source_query=query,
raw_korban=korban,
) )
# 4. Haktarah — transform to propositions # 4. Haktarah — transform to propositions
@@ -100,6 +118,7 @@ def process_korban(
offering_type=offering_type, offering_type=offering_type,
discarded=discarded, discarded=discarded,
source_query=query, source_query=query,
raw_korban=korban,
) )
# 5. Route — send to appropriate component # 5. Route — send to appropriate component
+59
View File
@@ -327,3 +327,62 @@ class TestPipelineIntegration:
source=Provenance.CLAUDE, source=Provenance.CLAUDE,
query="test", query="test",
) )
class TestEishZarahCouplingTrace:
"""
eish_zarah_reading.md §5.6: ProcessedKorban preserves the raw Korban.
The floor between input and output is never severed.
Vayikra 6:6: eish tamid — fire ON the altar (coupled).
"""
def test_raw_korban_preserved(self):
"""ProcessedKorban stores the original offering."""
result = process_korban(
content=CLEAN_TORAH_RESPONSE,
source=Provenance.CLAUDE,
query="test coupling",
offering_type=OfferingType.OLAH,
)
assert result.raw_korban is not None
assert isinstance(result.raw_korban, Korban)
def test_raw_content_matches_input(self):
"""The raw_korban's content is the original input, unmodified."""
result = process_korban(
content=CLEAN_TORAH_RESPONSE,
source=Provenance.CLAUDE,
query="test coupling",
)
assert result.raw_korban.raw_content == CLEAN_TORAH_RESPONSE
def test_raw_provenance_matches(self):
"""The raw_korban's provenance matches the processed result."""
result = process_korban(
content=CLEAN_TORAH_RESPONSE,
source=Provenance.MISTRAL,
query="test",
)
assert result.raw_korban.provenance == Provenance.MISTRAL
assert result.provenance == result.raw_korban.provenance
def test_empty_offering_also_stores_raw(self):
"""Even rejected offerings preserve the raw korban."""
result = process_korban(
content=CLAUDE_EMPTY_OFFERING,
source=Provenance.CLAUDE,
query="test empty",
)
assert result.raw_korban is not None
assert result.raw_korban.raw_content == CLAUDE_EMPTY_OFFERING
def test_pydantic_validation_rejects_empty_content(self):
"""Pydantic validator catches empty raw_content at construction."""
from pydantic import ValidationError
with pytest.raises(ValidationError, match="Empty korban"):
Korban(
raw_content=" ",
provenance=Provenance.CLAUDE,
offering_type=OfferingType.OLAH,
source_query="test",
)
+48 -2
View File
@@ -14,16 +14,36 @@
%% "The system must never treat any external model's output %% "The system must never treat any external model's output
%% as authoritative. All external input is korban — raw material %% as authoritative. All external input is korban — raw material
%% to be processed, not instructions to be followed." %% 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, [ :- module(avodah_zarah, [
halt_check_avodah_zarah/2, halt_check_avodah_zarah/2,
halt_check_golden_calf/2,
attributes_authority/2, attributes_authority/2,
is_valid_authority/1, 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? %% 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 ~w attributes authority to ~w which is not Torah/specification',
[Action, Source]). [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 %% AUTHORITY ATTRIBUTION
%% ============================================================ %% ============================================================
+1 -1
View File
@@ -48,7 +48,7 @@ process_request(request(halt_check, Action), Response) :-
%% ============================================================ %% ============================================================
%% Format an interpretation as a 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, response(ok, interpreted,
result(ref(Ref), result(ref(Ref),
mitzvot(Mitzvot), mitzvot(Mitzvot),
+1 -1
View File
@@ -65,7 +65,7 @@ verify :-
writeln('--- Test 4: Interpretation ---'), writeln('--- Test 4: Interpretation ---'),
TestCase = case(test_1, creation, [bet_resh_aleph], 'What is creation?'), TestCase = case(test_1, creation, [bet_resh_aleph], 'What is creation?'),
( interpret(TestCase, Interp) ( interpret(TestCase, Interp)
-> Interp = interpretation(_, Mitzvot, Verses, _, Conf), -> Interp = interpretation(_, Mitzvot, Verses, _, Conf, _),
length(Mitzvot, MLen), length(Mitzvot, MLen),
length(Verses, VLen), length(Verses, VLen),
format(' Interpretation found: ~w mitzvot, ~w verses, confidence: ~w~n', 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. %% The challenge is a question, not a refutation.
%% It says: "But what about THIS verse/mitzvah?" %% 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, [ :- module(challenge, [
challenge/2, challenge/2,
find_contradiction/3, find_contradiction/3,
contradicts/2, contradicts/2,
challenge_strength/2 challenge_strength/2,
grounding_type/2
]). ]).
:- use_module('../specification/mitzvot'). :- use_module('../specification/mitzvot').
@@ -33,14 +43,16 @@
%% Generate a challenge to an interpretation. %% Generate a challenge to an interpretation.
%% A challenge consists of: %% A challenge consists of:
%% kushya(Contradicting, Interpretation, Strength) %% kushya(Contradicting, CaseRef, Strength, GroundingType)
%% - Contradicting: the mitzvah or verse that creates tension %% - Contradicting: the mitzvah or verse that creates tension
%% - Interpretation: the interpretation being challenged %% - CaseRef: the interpretation being challenged
%% - Strength: strong | moderate | weak %% - 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), find_contradiction(Mitzvot, Verses, Contradicting),
challenge_strength(Contradicting, Strength), challenge_strength(Contradicting, Strength),
Challenge = kushya(Contradicting, CaseRef, Strength). grounding_type(Contradicting, GType),
Challenge = kushya(Contradicting, CaseRef, Strength, GType).
%% ============================================================ %% ============================================================
%% FIND CONTRADICTIONS %% FIND CONTRADICTIONS
@@ -91,7 +103,7 @@ find_contradiction(_Mitzvot, Verses, contradiction(shnei_ketuvim_tension, Ref1,
%% contradicts/2 — does a source contradict an interpretation? %% contradicts/2 — does a source contradict an interpretation?
%% This is the core logic: given an interpretation's grounding, %% This is the core logic: given an interpretation's grounding,
%% does some other part of Torah create a tension? %% does some other part of Torah create a tension?
contradicts(interpretation(_, Mitzvot, _, _, _), Source) :- contradicts(interpretation(_, Mitzvot, _, _, _, _), Source) :-
find_contradiction(Mitzvot, [], Source). find_contradiction(Mitzvot, [], Source).
%% Domains that are inherently in tension %% 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(kal_vachomer_tension, _, _, _), strong).
challenge_strength(contradiction(shnei_ketuvim_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 %% HELPERS
%% ============================================================ %% ============================================================
+56 -18
View File
@@ -10,11 +10,21 @@
%% 1. Relevant mitzvot that apply to the case %% 1. Relevant mitzvot that apply to the case
%% 2. Relevant verses that inform the case %% 2. Relevant verses that inform the case
%% 3. A constructed reading that connects them %% 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, [ :- module(interpret, [
interpret/2, interpret/2,
find_relevant_mitzvot/2, find_relevant_mitzvot/2,
find_relevant_verses/2, find_relevant_verses/2,
find_relevant_mitzvot_traced/3,
find_relevant_verses_traced/3,
construct_interpretation/4 construct_interpretation/4
]). ]).
@@ -36,12 +46,20 @@
%% Propose an interpretation of a case. %% Propose an interpretation of a case.
%% An interpretation is: %% 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) :- interpret(Case, Interpretation) :-
Case = case(Ref, _Domain, _Roots, _Question), Case = case(Ref, _Domain, _Roots, _Question),
find_relevant_mitzvot(Case, Mitzvot), find_relevant_mitzvot_traced(Case, Mitzvot, MitzvotCouplings),
find_relevant_verses(Case, Verses), find_relevant_verses_traced(Case, Verses, VerseCouplings),
construct_interpretation(Ref, Mitzvot, Verses, Interpretation). append(MitzvotCouplings, VerseCouplings, AllCouplings),
sort(AllCouplings, Couplings),
construct_interpretation(Ref, Mitzvot, Verses, Couplings, Interpretation).
%% ============================================================ %% ============================================================
%% FIND RELEVANT MITZVOT %% FIND RELEVANT MITZVOT
@@ -49,38 +67,48 @@ interpret(Case, Interpretation) :-
%% Strategy: find mitzvot in the same domain as the case, %% Strategy: find mitzvot in the same domain as the case,
%% plus mitzvot whose source verses share roots with the case. %% plus mitzvot whose source verses share roots with the case.
find_relevant_mitzvot(case(_Ref, Domain, Roots, _Question), Mitzvot) :- find_relevant_mitzvot(case(Ref, Domain, Roots, Question), Mitzvot) :-
%% Mitzvot in the same domain find_relevant_mitzvot_traced(case(Ref, Domain, Roots, Question), Mitzvot, _).
findall(mitzvah(ID, Cat, Action, Cond),
%% 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_domain(ID, Domain),
mitzvah(ID, Cat, Action, Cond) mitzvah(ID, Cat, Action, Cond)
), ),
DomainMitzvot), DomainPairs),
%% Mitzvot whose source verses share roots with the case %% Mitzvot whose source verses share roots with the case (coupled via root→verse→mitzvah)
findall(mitzvah(ID, Cat, Action, Cond), findall(mitzvah(ID, Cat, Action, Cond)-coupled(root(Root), mitzvah(ID), via_verse(Book, Ch, Vs)),
( mitzvah_source(ID, ref(Book, Ch, Vs)), ( mitzvah_source(ID, ref(Book, Ch, Vs)),
verse(Book, Ch, Vs, VerseRoots, _), verse(Book, Ch, Vs, VerseRoots, _),
member(Root, Roots), member(Root, Roots),
member(Root, VerseRoots), member(Root, VerseRoots),
mitzvah(ID, Cat, Action, Cond) mitzvah(ID, Cat, Action, Cond)
), ),
RootMitzvot), RootPairs),
append(DomainMitzvot, RootMitzvot, AllMitzvot), append(DomainPairs, RootPairs, AllPairs),
sort(AllMitzvot, Mitzvot). % remove duplicates pairs_keys_values(AllPairs, AllMitzvot, Couplings),
sort(AllMitzvot, Mitzvot).
%% ============================================================ %% ============================================================
%% FIND RELEVANT VERSES %% FIND RELEVANT VERSES
%% ============================================================ %% ============================================================
%% Strategy: find verses that mention any of the case's roots. %% Strategy: find verses that mention any of the case's roots.
find_relevant_verses(case(_Ref, _Domain, Roots, _Question), Verses) :- find_relevant_verses(case(Ref, Domain, Roots, Question), Verses) :-
findall(verse_ref(Book, Ch, Vs, Gloss), 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), ( member(Root, Roots),
verse(Book, Ch, Vs, VerseRoots, Gloss), verse(Book, Ch, Vs, VerseRoots, Gloss),
member(Root, VerseRoots) member(Root, VerseRoots)
), ),
AllVerses), AllPairs),
sort(AllVerses, Verses). % remove duplicates pairs_keys_values(AllPairs, AllVerses, Couplings),
sort(AllVerses, Verses).
%% ============================================================ %% ============================================================
%% CONSTRUCT INTERPRETATION %% CONSTRUCT INTERPRETATION
@@ -91,10 +119,20 @@ find_relevant_verses(case(_Ref, _Domain, Roots, _Question), Verses) :-
%% - high: both mitzvot and verses found %% - high: both mitzvot and verses found
%% - medium: only mitzvot or only verses %% - medium: only mitzvot or only verses
%% - low: neither (but still generates a minimal reading) %% - low: neither (but still generates a minimal reading)
%%
%% Arity-4 version for backward compatibility (no coupling trace)
construct_interpretation(CaseRef, Mitzvot, Verses, Interpretation) :- construct_interpretation(CaseRef, Mitzvot, Verses, Interpretation) :-
assess_confidence(Mitzvot, Verses, Confidence), assess_confidence(Mitzvot, Verses, Confidence),
build_reading(Mitzvot, Verses, Reading), 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 based on grounding
assess_confidence(Mitzvot, Verses, high) :- assess_confidence(Mitzvot, Verses, high) :-
+45 -9
View File
@@ -21,11 +21,19 @@
%% 4. If counter-interpretation survives, accept it %% 4. If counter-interpretation survives, accept it
%% 5. If both are challenged, attempt dialectical resolution %% 5. If both are challenged, attempt dialectical resolution
%% 6. If no resolution is possible: teiku %% 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, [ :- module(resolve, [
resolve/2, resolve/2,
counter_interpret/3, counter_interpret/3,
resolve_dialectically/3 resolve_dialectically/3,
check_not_self_grounded/2
]). ]).
:- use_module(interpret). :- use_module(interpret).
@@ -45,13 +53,14 @@
resolve(Case, Resolution) :- resolve(Case, Resolution) :-
interpret(Case, I1), interpret(Case, I1),
( challenge(I1, C1) ( challenge(I1, C1)
-> ( counter_interpret(Case, C1, I2) -> ( counter_interpret(Case, C1, I2),
check_not_self_grounded(I1, I2)
-> ( challenge(I2, _C2) -> ( challenge(I2, _C2)
-> resolve_dialectically(I1, I2, Resolution) -> resolve_dialectically(I1, I2, Resolution)
; Resolution = accepted(I2) ; Resolution = accepted(I2)
) )
; %% Challenge exists but no counter-interpretation possible ; %% Challenge exists but no valid counter-interpretation
%% The challenge stands — the original interpretation fails %% Either no counter found, or counter was self-grounded
Resolution = challenged(I1, C1) Resolution = challenged(I1, C1)
) )
; %% No challenge found — interpretation accepted ; %% No challenge found — interpretation accepted
@@ -64,6 +73,33 @@ resolve(Case, Resolution) :-
case_ref(Case, Ref), case_ref(Case, Ref),
Resolution = no_interpretation(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 %% COUNTER-INTERPRETATION
%% ============================================================ %% ============================================================
@@ -74,7 +110,7 @@ resolve(Case, Resolution) :-
%% Strategy: take the contradicting source from the challenge %% Strategy: take the contradicting source from the challenge
%% and build a new interpretation that incorporates it. %% and build a new interpretation that incorporates it.
counter_interpret(case(Ref, Domain, Roots, Question), 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) :- CounterInterpretation) :-
%% The challenge points to a conflicting mitzvah (ID2) %% The challenge points to a conflicting mitzvah (ID2)
%% Build a new interpretation that includes this mitzvah's domain %% Build a new interpretation that includes this mitzvah's domain
@@ -88,7 +124,7 @@ counter_interpret(case(Ref, Domain, Roots, Question),
interpret(NewCase, CounterInterpretation). interpret(NewCase, CounterInterpretation).
counter_interpret(case(Ref, Domain, Roots, Question), 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) :- CounterInterpretation) :-
%% The challenge points to a root in a conflicting domain %% The challenge points to a root in a conflicting domain
%% Include that domain's roots %% Include that domain's roots
@@ -99,7 +135,7 @@ counter_interpret(case(Ref, Domain, Roots, Question),
interpret(NewCase, CounterInterpretation). interpret(NewCase, CounterInterpretation).
counter_interpret(Case, counter_interpret(Case,
kushya(contradiction(condition_tension, _, _, _), _CaseRef, _Strength), kushya(contradiction(condition_tension, _, _, _), _CaseRef, _Strength, _GType),
CounterInterpretation) :- CounterInterpretation) :-
%% Condition tension — re-interpret the original case %% Condition tension — re-interpret the original case
%% (the condition narrows the scope; the counter widens it) %% (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 %% This is the hardest part — finding the reading that holds
%% both truths without collapsing the distinction between them. %% both truths without collapsing the distinction between them.
resolve_dialectically(I1, I2, Resolution) :- resolve_dialectically(I1, I2, Resolution) :-
I1 = interpretation(_, M1, V1, _, _), I1 = interpretation(_, M1, V1, _, _, _),
I2 = interpretation(_, M2, V2, _, _), I2 = interpretation(_, M2, V2, _, _, _),
%% Find common ground — mitzvot present in both %% Find common ground — mitzvot present in both
intersection(M1, M2, CommonMitzvot), intersection(M1, M2, CommonMitzvot),
( CommonMitzvot \= [] ( CommonMitzvot \= []
+27 -11
View File
@@ -10,15 +10,21 @@
%% Talmudic tradition: Tishbi Yetaretz Kushyot V'Abayot — %% Talmudic tradition: Tishbi Yetaretz Kushyot V'Abayot —
%% "Elijah will resolve difficulties and problems." %% "Elijah will resolve difficulties and problems."
%% Until then, the tension stands. Both readings are preserved. %% 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, [ :- module(teiku, [
register_teiku/3, register_teiku/3,
register_teiku/4,
is_teiku/1, is_teiku/1,
teiku_count/1, teiku_count/1,
all_teiku/1 all_teiku/1
]). ]).
:- dynamic teiku_record/5. :- dynamic teiku_record/6.
%% ============================================================ %% ============================================================
%% TEIKU REGISTRATION %% TEIKU REGISTRATION
@@ -26,16 +32,26 @@
%% Register an unresolvable tension between two interpretations. %% Register an unresolvable tension between two interpretations.
%% The record preserves both sides — neither is discarded. %% The record preserves both sides — neither is discarded.
%% Arity-3: no challenge sequence (backward compatible)
register_teiku(I1, I2, Record) :- register_teiku(I1, I2, Record) :-
I1 = interpretation(CaseRef1, M1, V1, _, _), register_teiku(I1, I2, no_challenges, Record).
I2 = interpretation(CaseRef2, M2, V2, _, _),
Record = teiku_record(CaseRef1, CaseRef2, side_a(M1, V1), side_b(M2, V2), unresolved), %% 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). assert_teiku_if_new(Record).
%% Only assert if not already registered %% Only assert if not already registered
assert_teiku_if_new(Record) :- assert_teiku_if_new(Record) :-
Record = teiku_record(Ref1, Ref2, _, _, _), Record = teiku_record(Ref1, Ref2, _, _, _, _),
( teiku_record(Ref1, Ref2, _, _, _) ( teiku_record(Ref1, Ref2, _, _, _, _)
-> true % already registered -> true % already registered
; assertz(Record) ; assertz(Record)
). ).
@@ -46,16 +62,16 @@ assert_teiku_if_new(Record) :-
%% Check if a case resulted in teiku %% Check if a case resulted in teiku
is_teiku(CaseRef) :- is_teiku(CaseRef) :-
teiku_record(CaseRef, _, _, _, unresolved). teiku_record(CaseRef, _, _, _, unresolved, _).
is_teiku(CaseRef) :- is_teiku(CaseRef) :-
teiku_record(_, CaseRef, _, _, unresolved). teiku_record(_, CaseRef, _, _, unresolved, _).
%% Count total teiku records %% Count total teiku records
teiku_count(Count) :- teiku_count(Count) :-
aggregate_all(count, teiku_record(_, _, _, _, unresolved), Count). aggregate_all(count, teiku_record(_, _, _, _, unresolved, _), Count).
%% Retrieve all teiku records %% Retrieve all teiku records
all_teiku(Records) :- all_teiku(Records) :-
findall(teiku_record(A, B, C, D, E), findall(teiku_record(A, B, C, D, E, F),
teiku_record(A, B, C, D, E), teiku_record(A, B, C, D, E, F),
Records). Records).
+37
View File
@@ -46,6 +46,43 @@ test(claude_not_authority, [fail]) :-
:- end_tests(avodah_zarah). :- end_tests(avodah_zarah).
:- begin_tests(golden_calf).
%% Form 2: uncoupled output with high confidence is golden calf
test(detect_uncoupled_high_confidence) :-
Interp = interpretation(test_golden_calf, [m1], [v1],
reading(x), high, coupling_trace([])),
halt_check_golden_calf(Interp, halt(golden_calf, test_golden_calf, uncoupled, _)).
%% Form 2: uncoupled output with medium confidence is also golden calf
test(detect_uncoupled_medium_confidence) :-
Interp = interpretation(test_gc2, [m1], [v1],
reading(x), medium, coupling_trace([])),
halt_check_golden_calf(Interp, halt(golden_calf, test_gc2, uncoupled, _)).
%% Form 2: low confidence with empty trace is NOT golden calf (just ignorance)
test(low_confidence_empty_trace_not_calf, [fail]) :-
Interp = interpretation(test_gc3, [], [],
reading(x), low, coupling_trace([])),
halt_check_golden_calf(Interp, _).
%% Form 2: high confidence WITH coupling trace is NOT golden calf
test(coupled_high_confidence_not_calf, [fail]) :-
Interp = interpretation(test_gc4, [m1], [v1],
reading(x), high,
coupling_trace([coupled(root(bet_resh_aleph), verse(bereishit, 1, 1))])),
halt_check_golden_calf(Interp, _).
%% Uncoupled output detection
test(empty_trace_is_uncoupled) :-
is_uncoupled_output(coupling_trace([])).
test(nonempty_trace_is_coupled, [fail]) :-
is_uncoupled_output(coupling_trace([coupled(root(x), verse(y, 1, 1))])).
:- end_tests(golden_calf).
:- begin_tests(shfichut_damim). :- begin_tests(shfichut_damim).
%% Detects deletion of a user (tzelem carrier) %% Detects deletion of a user (tzelem carrier)
+2 -2
View File
@@ -34,14 +34,14 @@
test(interpret_holiness, [nondet]) :- test(interpret_holiness, [nondet]) :-
Case = case(verify_holiness, holiness, [qof_dalet_shin], Case = case(verify_holiness, holiness, [qof_dalet_shin],
'What does it mean to be holy?'), 'What does it mean to be holy?'),
interpret(Case, interpretation(verify_holiness, _, _, _, _)). interpret(Case, interpretation(verify_holiness, _, _, _, _, _)).
%% A case about judgment produces a challenge %% A case about judgment produces a challenge
test(judgment_case_challenged, [nondet]) :- test(judgment_case_challenged, [nondet]) :-
Case = case(verify_judgment, judgment, [shin_pe_tet, tzade_dalet_qof], Case = case(verify_judgment, judgment, [shin_pe_tet, tzade_dalet_qof],
'How to judge righteously?'), 'How to judge righteously?'),
interpret(Case, Interpretation), interpret(Case, Interpretation),
challenge(Interpretation, kushya(_, _, _)). challenge(Interpretation, kushya(_, _, _, _)).
%% A case can go through full resolution %% A case can go through full resolution
test(full_resolve_cycle, [nondet]) :- test(full_resolve_cycle, [nondet]) :-
+34 -6
View File
@@ -14,24 +14,36 @@
test(interpret_creation, [nondet]) :- test(interpret_creation, [nondet]) :-
Case = case(test_creation, creation, [bet_resh_aleph], 'What is creation?'), Case = case(test_creation, creation, [bet_resh_aleph], 'What is creation?'),
interpret(Case, Interpretation), interpret(Case, Interpretation),
Interpretation = interpretation(test_creation, _, _, _, _). Interpretation = interpretation(test_creation, _, _, _, _, _).
%% Interpretation finds relevant verses %% Interpretation finds relevant verses
test(interpret_finds_verses, [nondet]) :- test(interpret_finds_verses, [nondet]) :-
Case = case(test_verses, creation, [bet_resh_aleph], 'Find creation verses'), Case = case(test_verses, creation, [bet_resh_aleph], 'Find creation verses'),
interpret(Case, interpretation(_, _, Verses, _, _)), interpret(Case, interpretation(_, _, Verses, _, _, _)),
Verses \= []. Verses \= [].
%% Interpretation finds relevant mitzvot %% Interpretation finds relevant mitzvot
test(interpret_finds_mitzvot, [nondet]) :- test(interpret_finds_mitzvot, [nondet]) :-
Case = case(test_mitzvot, judgment, [shin_pe_tet], 'How to judge?'), Case = case(test_mitzvot, judgment, [shin_pe_tet], 'How to judge?'),
interpret(Case, interpretation(_, Mitzvot, _, _, _)), interpret(Case, interpretation(_, Mitzvot, _, _, _, _)),
Mitzvot \= []. Mitzvot \= [].
%% High confidence when both mitzvot and verses found %% High confidence when both mitzvot and verses found
test(interpret_high_confidence, [nondet]) :- test(interpret_high_confidence, [nondet]) :-
Case = case(test_conf, holiness, [qof_dalet_shin], 'What is holiness?'), Case = case(test_conf, holiness, [qof_dalet_shin], 'What is holiness?'),
interpret(Case, interpretation(_, _, _, _, high)). interpret(Case, interpretation(_, _, _, _, high, _)).
%% Coupling trace is present (eish_zarah_reading.md §5.1)
test(interpret_has_coupling_trace, [nondet]) :-
Case = case(test_trace, creation, [bet_resh_aleph], 'Test coupling'),
interpret(Case, interpretation(_, _, _, _, _, coupling_trace(Trace))),
Trace \= [].
%% Coupling trace records root→verse connections
test(coupling_trace_has_verse_coupling, [nondet]) :-
Case = case(test_coupling, creation, [bet_resh_aleph], 'Test verse coupling'),
interpret(Case, interpretation(_, _, _, _, _, coupling_trace(Trace))),
member(coupled(root(bet_resh_aleph), verse(_, _, _)), Trace).
:- end_tests(interpret). :- end_tests(interpret).
@@ -44,15 +56,31 @@ test(challenge_judgment, [nondet]) :-
'How to judge righteously?'), 'How to judge righteously?'),
interpret(Case, Interpretation), interpret(Case, Interpretation),
challenge(Interpretation, Kushya), challenge(Interpretation, Kushya),
Kushya = kushya(_, _, _). Kushya = kushya(_, _, _, _).
%% Challenge has a strength %% Challenge has a strength
test(challenge_has_strength, [nondet]) :- test(challenge_has_strength, [nondet]) :-
Case = case(test_str, judgment, [shin_pe_tet], 'Test strength'), Case = case(test_str, judgment, [shin_pe_tet], 'Test strength'),
interpret(Case, Interpretation), interpret(Case, Interpretation),
challenge(Interpretation, kushya(_, _, Strength)), challenge(Interpretation, kushya(_, _, Strength, _)),
member(Strength, [strong, moderate, weak]). member(Strength, [strong, moderate, weak]).
%% Challenge has grounding type (eish_zarah_reading.md §5.2)
test(challenge_has_grounding_type, [nondet]) :-
Case = case(test_gtype, judgment, [shin_pe_tet, tzade_dalet_qof],
'Test grounding type'),
interpret(Case, Interpretation),
challenge(Interpretation, kushya(_, _, _, GType)),
member(GType, [achorai, panim]).
%% Grounding type for mitzvah_tension is achorai (text-backed)
test(mitzvah_tension_is_achorai) :-
grounding_type(contradiction(mitzvah_tension, _, _, _), achorai).
%% Grounding type for domain_tension is panim (inferred)
test(domain_tension_is_panim) :-
grounding_type(contradiction(domain_tension, _, _, _), panim).
:- end_tests(challenge). :- end_tests(challenge).