%% interpret.pl — Interpretation generation. %% %% Architecture §3.4: %% interpret(Case, Interpretation) :- %% find_relevant_mitzvot(Case, Mitzvot), %% find_relevant_verses(Case, Verses), %% construct_interpretation(Mitzvot, Verses, Interpretation). %% %% An interpretation is grounded in Torah — it consists of: %% 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. %% FORM 2 architectural commitment (vehaya §3.2; avodah_zarah.pl %% Form 2): construct_interpretation/4 (the no-coupling-trace %% variant) is NO LONGER EXPORTED. Vayikra 10:1's "asher lo tzivah" %% disqualifies output without command-derivation. The arity-4 form %% built interpretation/6 with empty coupling_trace — the bypass %% shape that would let a future caller produce sincere-but- %% uncoupled output, the exact failure mode Form 2 identifies. Only %% construct_interpretation/5 (with Couplings argument) is callable %% from outside this module; production callers go through interpret/2, %% which always supplies real Couplings derived from %% find_relevant_*_traced/3. :- module(interpret, [ interpret/2, find_relevant_mitzvot/2, find_relevant_verses/2, find_relevant_mitzvot_traced/3, find_relevant_verses_traced/3, construct_interpretation/5 ]). :- use_module('../specification/mitzvot'). :- use_module('../specification/torah_text'). :- use_module('../specification/root_meanings'). :- use_module('../specification/relationships'). %% A case is represented as: %% case(Ref, Domain, Roots, Question) %% - Ref: a unique identifier %% - Domain: the semantic domain of the question %% - Roots: list of Hebrew roots involved %% - Question: the question being asked %% ============================================================ %% INTERPRETATION %% ============================================================ %% Propose an interpretation of a case. %% An interpretation is: %% 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_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 %% ============================================================ %% 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) :- 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) ), 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) ), 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) :- find_relevant_verses_traced(case(Ref, Domain, Roots, Question), Verses, _). %% Traced version: returns verses AND the coupling trace for each. %% %% Over the whole scroll (2026-10-19, l1_verses.pl) a common root lights %% hundreds of verses; a reading that cites them all cites nothing. The %% verses are ranked by how many of the case's roots they carry — a %% verse sharing three roots stands closer to the case than one sharing %% one — and the reading keeps the top ?MAX_VERSES, ties broken by %% canonical order. The coupling trace is kept only for the verses the %% reading cites: a trace names what the reading stands on. max_verses(12). find_relevant_verses_traced(case(_Ref, _Domain, Roots, _Question), Verses, Couplings) :- sort(Roots, UniqueRoots), findall(Shared-verse_ref(Book, Ch, Vs, Gloss), ( verse(Book, Ch, Vs, VerseRoots, Gloss), intersection(UniqueRoots, VerseRoots, SharedRoots), SharedRoots \== [], length(SharedRoots, N), Shared is -N ), Scored), %% keysort is stable: equal scores keep the scroll's order keysort(Scored, Ranked), pairs_values(Ranked, RankedVerses), max_verses(Max), take(Max, RankedVerses, Verses), findall(coupled(root(Root), verse(Book, Ch, Vs)), ( member(verse_ref(Book, Ch, Vs, _), Verses), verse(Book, Ch, Vs, VerseRoots, _), member(Root, UniqueRoots), member(Root, VerseRoots) ), Couplings). take(N, List, Prefix) :- length(List, L), ( L =< N -> Prefix = List ; length(Prefix, N), append(Prefix, _, List) ). %% ============================================================ %% CONSTRUCT INTERPRETATION %% ============================================================ %% Build an interpretation from the collected mitzvot and verses. %% Confidence is based on the strength of grounding: %% - high: both mitzvot and verses found %% - medium: only mitzvot or only verses %% - low: neither (but still generates a minimal reading) %% %% Arity-5 only — the arity-4 (no-coupling-trace) variant was %% removed to close Form 2 of avodah zarah architecturally %% (vehaya §3.2). Vayikra 10:1's "asher lo tzivah" disqualifies %% output without command-derivation; constructing interpretation/6 %% with empty coupling_trace is the bypass shape that arity-4 made %% available. Production callers go through interpret/2, which %% always supplies real Couplings from find_relevant_*_traced/3. %% Test code that needs an empty-trace interpretation must %% construct it directly via the interpretation/6 term (visibly %% bypassing the constructor) — making the bypass deliberate, not %% accidental. 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) :- Mitzvot \= [], Verses \= [], !. assess_confidence(Mitzvot, _, medium) :- Mitzvot \= [], !. assess_confidence(_, Verses, medium) :- Verses \= [], !. assess_confidence(_, _, low). %% Build a reading — the interpretive connection between sources build_reading(Mitzvot, Verses, Reading) :- length(Mitzvot, MCount), length(Verses, VCount), Reading = reading(mitzvot_count(MCount), verse_count(VCount), grounded_in(Mitzvot, Verses)).