torah_text.pl held 32 hand-written verses from Phase 1 — "the reasoning layer's view of the specification". With the cycle and the Sanhedrin now consulting the engine on every question (2026-10-19), that view showed: any root it knew lit the same handful, and bereishit 1:1 and devarim 6:4 were read in the same eleven verses. - scripts/build_engine_verses.py writes talmudic_engine/src/specification/l1_verses.pl from l1/torah: every verse, each word's shoresh as the engine names it (Hebrew → letter names, finals folded), the consonantal text as gloss, and the SHA-256 of the L1 files read in the header. `--check` exits 1 when the facts lag the scroll; build_and_push.sh runs it beside the Doorpost check. - torah_text.pl includes the generated file; the hand facts are gone. L1's roots are authoritative where the hand facts differed (Israel שרה, the Name הוה). - interpret.pl: over 5,853 verses a common root lights hundreds, so the reading ranks verses by how many of the case's roots each carries and keeps the top 12 in canonical order; the coupling trace names only the verses the reading cites. Local, over the full scroll: bereishit 1:1 → itself, Devarim 4:32, Bereishit 2:4 …; devarim 6:4 → itself, Shemot 3:18, Devarim 4:1 …; shabbat roots → Bereishit 2:3, Shemot 20:8, 20:11, 31:13. interpret 15 ms, challenge 10 ms (now finds a kushya for the Shema), resolve 24 ms. Prolog 7 suites green (middot 2.9 s — pairwise scans over the scroll). 5,853 verses loaded, 49,542 root occurrences. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
197 lines
8.5 KiB
Prolog
197 lines
8.5 KiB
Prolog
%% 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)).
|