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>
112 lines
4.4 KiB
Python
Executable File
112 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""build_engine_verses.py — the Talmudic Engine's verse facts, derived from L1.
|
||
|
||
Architecture §3.4: verse(Book, Chapter, Verse, HebrewRoots, Gloss) is "the
|
||
reasoning layer's view of the specification — not a replacement for L1".
|
||
Until 2026-10-19 that view was 32 hand-written verses, so any root the
|
||
engine knew lit the same handful and every reading cited the same list.
|
||
This script writes talmudic_engine/src/specification/l1_verses.pl from
|
||
l1/torah — every verse, its roots as the engine names them
|
||
(Hebrew shoresh → letter names, finals folded: ברא → bet_resh_aleph), the
|
||
consonantal text as the gloss — and records the SHA-256 of the L1 files it
|
||
read in the header, so the facts can be checked against the scroll.
|
||
|
||
scripts/build_engine_verses.py # write the facts
|
||
scripts/build_engine_verses.py --check # exit 1 if the facts do not match L1 as it stands
|
||
|
||
Devarim 4:2 — nothing added, nothing taken: the engine reads the scroll's
|
||
roots, not the builder's memory of them.
|
||
"""
|
||
import argparse, hashlib, json, os, sys
|
||
|
||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||
L1 = os.path.join(ROOT, "l1", "torah")
|
||
OUT = os.path.join(ROOT, "talmudic_engine", "src", "specification", "l1_verses.pl")
|
||
BOOKS = ["bereishit", "shemot", "vayikra", "bamidbar", "devarim"]
|
||
|
||
NAMES = {
|
||
"א": "aleph", "ב": "bet", "ג": "gimel", "ד": "dalet", "ה": "he", "ו": "vav", "ז": "zayin",
|
||
"ח": "chet", "ט": "tet", "י": "yod", "כ": "kaf", "ך": "kaf", "ל": "lamed", "מ": "mem", "ם": "mem",
|
||
"נ": "nun", "ן": "nun", "ס": "samekh", "ע": "ayin", "פ": "pe", "ף": "pe", "צ": "tsadi", "ץ": "tsadi",
|
||
"ק": "qof", "ר": "resh", "ש": "shin", "ת": "tav",
|
||
}
|
||
|
||
|
||
def root_atom(shoresh):
|
||
if not shoresh:
|
||
return None
|
||
names = [NAMES.get(c) for c in shoresh]
|
||
if not names or any(n is None for n in names):
|
||
return None
|
||
return "_".join(names)
|
||
|
||
|
||
def quote(s):
|
||
return "'" + s.replace("\\", "\\\\").replace("'", "\\'") + "'"
|
||
|
||
|
||
def verse_files():
|
||
out = []
|
||
for book in BOOKS:
|
||
bdir = os.path.join(L1, book)
|
||
for ch in sorted((d for d in os.listdir(bdir) if d.isdigit()), key=int):
|
||
cdir = os.path.join(bdir, ch)
|
||
for f in sorted((f for f in os.listdir(cdir) if f.endswith(".json") and f[:-5].isdigit()), key=lambda x: int(x[:-5])):
|
||
out.append((book, int(ch), int(f.split(".")[0]), os.path.join(cdir, f)))
|
||
return out
|
||
|
||
|
||
def build():
|
||
h = hashlib.sha256()
|
||
facts = []
|
||
nverses = nroots = 0
|
||
for book, ch, vs, path in verse_files():
|
||
data = open(path, "rb").read()
|
||
h.update(f"{book}/{ch}/{vs}\n".encode())
|
||
h.update(data)
|
||
d = json.loads(data)
|
||
roots = []
|
||
for w in d.get("words", []):
|
||
a = root_atom(w.get("shoresh"))
|
||
if a and a not in roots:
|
||
roots.append(a)
|
||
gloss = " ".join(w.get("consonants", "") for w in d.get("words", []) if w.get("consonants"))
|
||
facts.append(f"verse({book}, {ch}, {vs}, [{', '.join(roots)}], {quote(gloss)}).")
|
||
nverses += 1
|
||
nroots += len(roots)
|
||
header = f"""%% l1_verses.pl — GENERATED by scripts/build_engine_verses.py from l1/torah.
|
||
%% Do not edit: regenerate. verse(Book, Chapter, Verse, Roots, Gloss) for
|
||
%% every verse of L1; Roots are the engine's letter-name atoms derived from
|
||
%% each word's shoresh (finals folded), once per verse in word order; Gloss
|
||
%% is the consonantal text. Included by torah_text.pl.
|
||
%%
|
||
%% l1_sha256: {h.hexdigest()}
|
||
%% verses: {nverses} root_occurrences: {nroots}
|
||
"""
|
||
return header + "\n".join(facts) + "\n", h.hexdigest(), nverses
|
||
|
||
|
||
def main():
|
||
ap = argparse.ArgumentParser()
|
||
ap.add_argument("--check", action="store_true")
|
||
a = ap.parse_args()
|
||
text, digest, n = build()
|
||
if a.check:
|
||
try:
|
||
current = open(OUT, encoding="utf-8").read()
|
||
except FileNotFoundError:
|
||
print(f"engine verses: {OUT} missing — run scripts/build_engine_verses.py", file=sys.stderr)
|
||
sys.exit(1)
|
||
if current != text:
|
||
print(f"engine verses: {OUT} does not match L1 (l1_sha256 now {digest[:12]}…) — regenerate", file=sys.stderr)
|
||
sys.exit(1)
|
||
print(f"engine verses: match L1 ({n} verses, l1_sha256 {digest[:12]}…)")
|
||
return
|
||
with open(OUT, "w", encoding="utf-8") as f:
|
||
f.write(text)
|
||
print(f"wrote {OUT}: {n} verses, l1_sha256 {digest[:12]}…")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|