From 5ff1b73d000ff17b3ea400f712d81dafc142a1c7 Mon Sep 17 00:00:00 2001 From: Scott Karp Date: Sun, 4 Oct 2026 18:42:52 +0000 Subject: [PATCH] =?UTF-8?q?specs=20=C2=A73.1=20+=20engine=20+=20Doorpost:?= =?UTF-8?q?=20a=20root=20is=20the=20consonants=20L1=20records=20=E2=80=94?= =?UTF-8?q?=20two,=20three,=20or=20more?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture §3.1 typed a root as exactly three consonants (`Shoresh {c1,c2,c3}`) and listed data/roots.json and data/torah_refs.json in the engine's file tree. L1 has 115 roots that are not three letters (32 of two, 83 of four or more), and the engine has no data directory: the roots are in L1. The code had followed the spec's type and paid for it. textToShoresh padded a two-letter root with He — בן became בנה, כל became כלה — and returned Nothing for anything longer, so Lexicon.buildLexiconFromVerses either falsified those roots or dropped them. Shoresh.Roots (tablet 33) had stepped around the type by carrying roots as Text. Spec: - §3.1 data structures: `newtype Shoresh = Shoresh { radicals :: [Consonant] }` — two or more, never padded or truncated; `mkShoresh`, `triliteral`. - relatedRoots / neighbors: same length, differing in exactly one consonant (for a root of three, the old "sharing 2 consonants"). - File tree: Roots.hs and Server.hs added; data/ removed, with a line saying where the roots are; cabal.project.freeze listed; aeson's purpose corrected. Engine: - Types: the newtype, mkShoresh :: [Consonant] -> Maybe Shoresh, triliteral, textToShoresh without padding. - Gematria: sum over the radicals. Graph: neighbors among roots of the same length; neighborsByPosition labels the shared positions ("c1c2" for a triliteral, as before). - Server: consonant names through the type. - Spec.hs: בן stays two letters (gematria 52, not 57); שמאל is held whole; the graph compares only roots of equal length. 59 examples. Doorpost @expected_hash 094b8164… → f653ef05…, in this commit, with the witnessed file that moved it (architecture). Read before committing: Haskell 59/0; Elixir 474/0 on seeds 0 7 42; both images built and run together with the real binary — Shemot 3:14 answers ב-ן (two letters), Bereishit 48:13 answers ש-מ-א-ל (four), Devarim 6:5 unchanged; local specification_identity verified f653ef05…; no engine errors or fallbacks. Not touched: §3.1's dependency list omits bytestring, filepath and directory, which the cabal file has. Co-Authored-By: Claude Fable 5.1 --- shoresh-engine/src/Main.hs | 2 +- shoresh-engine/src/Shoresh/Gematria.hs | 4 +- shoresh-engine/src/Shoresh/Graph.hs | 48 +++++++---- shoresh-engine/src/Shoresh/Server.hs | 13 +-- shoresh-engine/src/Shoresh/Types.hs | 47 +++++----- shoresh-engine/test/Spec.hs | 100 ++++++++++++++-------- specs/shorashim_architecture.md | 33 ++++--- vav_pipeline/lib/vav_pipeline/doorpost.ex | 2 +- 8 files changed, 153 insertions(+), 96 deletions(-) diff --git a/shoresh-engine/src/Main.hs b/shoresh-engine/src/Main.hs index 458ac026..238ba6cf 100644 --- a/shoresh-engine/src/Main.hs +++ b/shoresh-engine/src/Main.hs @@ -62,7 +62,7 @@ runVerification = do -- 1. Root activation through all 7 binyanim putStrLn "--- Test 1: Root activation (ב-ר-א through 7 binyanim) ---" - let bara = mkShoresh Bet Resh Aleph -- ברא = create + let bara = triliteral Bet Resh Aleph -- ברא = create let expanded = expand bara mapM_ (\(b, p) -> do TIO.putStr $ " " <> T.pack (show b) <> ": " diff --git a/shoresh-engine/src/Shoresh/Gematria.hs b/shoresh-engine/src/Shoresh/Gematria.hs index 70b94b7c..663d3003 100644 --- a/shoresh-engine/src/Shoresh/Gematria.hs +++ b/shoresh-engine/src/Shoresh/Gematria.hs @@ -49,9 +49,9 @@ letterValue c = case c of Shin -> 300 Tav -> 400 --- | Gematria of a triliteral root. +-- | Gematria of a root — the sum of its consonants, however many. gematriaRoot :: Shoresh -> Int -gematriaRoot (Shoresh a b d) = letterValue a + letterValue b + letterValue d +gematriaRoot = sum . map letterValue . radicals -- | Gematria of a single Hebrew character. -- Returns 0 for non-consonant characters (vowels, cantillation marks). diff --git a/shoresh-engine/src/Shoresh/Graph.hs b/shoresh-engine/src/Shoresh/Graph.hs index 41918e1c..d73099f8 100644 --- a/shoresh-engine/src/Shoresh/Graph.hs +++ b/shoresh-engine/src/Shoresh/Graph.hs @@ -6,9 +6,11 @@ -- neighbors :: Shoresh -> ShoreshLexicon -> [ShoreshEntry] -- "Find all roots sharing 2 consonants (semantic neighborhood)" -- --- Two roots are neighbors if they share 2 of 3 consonants --- in the same positions. This captures the Hebrew linguistic --- phenomenon where related roots share consonants. +-- Two roots are neighbors if they have the same number of consonants +-- and differ in exactly one position — for a root of three, the spec's +-- "sharing 2 consonants". This captures the Hebrew linguistic phenomenon +-- where related roots share consonants. Roots of different lengths are +-- not compared. module Shoresh.Graph ( neighbors , sharedConsonants @@ -20,30 +22,42 @@ import Shoresh.Lexicon (ShoreshLexicon, allRoots, lookupRoot) import Data.Maybe (mapMaybe) -- | Count shared consonants between two roots (position-sensitive). +-- Roots of different lengths share none: there is no position to align. sharedConsonants :: Shoresh -> Shoresh -> Int -sharedConsonants (Shoresh a1 b1 c1') (Shoresh a2 b2 c2') = - (if a1 == a2 then 1 else 0) + - (if b1 == b2 then 1 else 0) + - (if c1' == c2' then 1 else 0) +sharedConsonants x y + | length rx /= length ry = 0 + | otherwise = length (filter id (zipWith (==) rx ry)) + where + rx = radicals x + ry = radicals y --- | Find all roots sharing exactly 2 consonants in the same positions. +-- | Find all roots of the same length differing in exactly one consonant. neighbors :: Shoresh -> ShoreshLexicon -> [ShoreshEntry] neighbors sh lexicon = mapMaybe (\sh' -> - if sh' /= sh && sharedConsonants sh sh' >= 2 + if sh' /= sh && isNeighbor sh sh' then lookupRoot sh' lexicon else Nothing ) (allRoots lexicon) --- | Find neighbors that share consonants at specific positions. --- Returns (position_shared, neighbor_entry) pairs. +isNeighbor :: Shoresh -> Shoresh -> Bool +isNeighbor x y = + length (radicals x) == length (radicals y) + && sharedConsonants x y == length (radicals x) - 1 + +-- | Find neighbors, labelled by the positions they share. +-- Returns (positions_shared, neighbor_entry) pairs — for a root of +-- three, "c1c2", "c1c3" or "c2c3". neighborsByPosition :: Shoresh -> ShoreshLexicon -> [(String, ShoreshEntry)] -neighborsByPosition sh@(Shoresh a b d) lexicon = +neighborsByPosition sh lexicon = concatMap checkNeighbor (allRoots lexicon) where - checkNeighbor sh'@(Shoresh a' b' d') - | sh' == sh = [] - | a == a' && b == b' = maybe [] (\e -> [("c1c2", e)]) (lookupRoot sh' lexicon) - | a == a' && d == d' = maybe [] (\e -> [("c1c3", e)]) (lookupRoot sh' lexicon) - | b == b' && d == d' = maybe [] (\e -> [("c2c3", e)]) (lookupRoot sh' lexicon) + checkNeighbor sh' + | sh' /= sh && isNeighbor sh sh' = + maybe [] (\e -> [(sharedLabel sh', e)]) (lookupRoot sh' lexicon) | otherwise = [] + + sharedLabel sh' = + concat [ 'c' : show i + | (i, same) <- zip [1 :: Int ..] (zipWith (==) (radicals sh) (radicals sh')) + , same ] diff --git a/shoresh-engine/src/Shoresh/Server.hs b/shoresh-engine/src/Shoresh/Server.hs index 55637078..d5ba9593 100644 --- a/shoresh-engine/src/Shoresh/Server.hs +++ b/shoresh-engine/src/Shoresh/Server.hs @@ -248,10 +248,10 @@ extractScope _ = Nothing -- | Render a root heard from L1 in the port's contract shape -- (Construction spec §3.1), plus where it was heard from. -- --- The shoresh is rendered exactly as L1 records it. A two-letter root --- (כל, שמ) stays two letters: the old path padded it with He to fit the --- triliteral type and reported a root — and a gematria — the text does --- not contain. +-- The shoresh is rendered exactly as L1 records it: a two-letter root +-- (בן, יד) stays two letters and a longer one stays whole. The old path +-- padded two letters with He to fit a triliteral type and reported a +-- root — and a gematria — the text does not contain. -- -- @binyan@ is the stem the verb actually stands in, read from the -- verse's morphology. It is null for a noun and for a word that is not @@ -259,8 +259,11 @@ extractScope _ = Nothing activateRoot :: ResolvedRoot -> Value activateRoot r = let sh = rrShoresh r + -- L1's roots are consonants, so this parse cannot fail; if one + -- ever did, the names are simply absent — never invented. + names = maybe [] (map consonantName . radicals) (textToShoresh sh) in object - [ "shoresh" .= mapMaybe (fmap consonantName . charToConsonant) (T.unpack sh) + [ "shoresh" .= names , "shoresh_hebrew" .= sh , "binyan" .= fmap (T.pack . show) (rrBinyan r) , "domain" .= ("unknown" :: Text) -- Phase 3: domain lookup diff --git a/shoresh-engine/src/Shoresh/Types.hs b/shoresh-engine/src/Shoresh/Types.hs index f364accd..d341e808 100644 --- a/shoresh-engine/src/Shoresh/Types.hs +++ b/shoresh-engine/src/Shoresh/Types.hs @@ -18,6 +18,7 @@ module Shoresh.Types -- * Triliteral Roots , Shoresh(..) , mkShoresh + , triliteral , shoreshToText , textToShoresh -- * Binyanim (Transformation Operators) @@ -96,39 +97,39 @@ consonantName c = case c of Shin -> "shin"; Tav -> "tav" --- | A triliteral root — the atomic semantic unit. --- Most Hebrew roots are three consonants. Some are two (biconsonantal). --- The spec says triliteral; L1 shows some bicons (e.g. "שמ"). --- We model the common case. Bicons get a silent third. -data Shoresh = Shoresh - { c1 :: !Consonant - , c2 :: !Consonant - , c3 :: !Consonant +-- | A root — the atomic semantic unit: the consonants under which the +-- lexicon files a word, exactly as L1 records them. +-- +-- Most roots are three consonants. Torah also has roots of two (בן, יד) +-- and of four or more (שמאל). A root is never padded and never truncated +-- to make it three: until 2026-10-04 this type held exactly three +-- consonants, a two-letter root was given a He the text never wrote +-- (כל became כלה), and a longer one could not be held at all. +newtype Shoresh = Shoresh + { radicals :: [Consonant] -- ^ two or more, in order } deriving (Eq, Ord, Show, Generic) instance ToJSON Shoresh instance FromJSON Shoresh --- | Construct a Shoresh from three consonants. -mkShoresh :: Consonant -> Consonant -> Consonant -> Shoresh -mkShoresh = Shoresh +-- | Construct a root from its consonants. A root has at least two. +mkShoresh :: [Consonant] -> Maybe Shoresh +mkShoresh cs + | length cs >= 2 = Just (Shoresh cs) + | otherwise = Nothing + +-- | A root of three consonants — the common case, and total. +triliteral :: Consonant -> Consonant -> Consonant -> Shoresh +triliteral a b d = Shoresh [a, b, d] -- | Render a Shoresh as Hebrew text. shoreshToText :: Shoresh -> Text -shoreshToText (Shoresh a b d) = - T.pack [consonantToChar a, consonantToChar b, consonantToChar d] +shoreshToText = T.pack . map consonantToChar . radicals --- | Parse a Hebrew text string into a Shoresh. --- Accepts 2 or 3 consonant strings. --- For 2-consonant roots, pads with He (ה) as placeholder. +-- | Parse Hebrew consonants into a Shoresh. Every character must be a +-- consonant and there must be at least two. Nothing is added. textToShoresh :: Text -> Maybe Shoresh -textToShoresh t = - let chars = T.unpack t - consonants = mapM charToConsonant chars - in case consonants of - Just [a, b, d] -> Just (Shoresh a b d) - Just [a, b] -> Just (Shoresh a b He) -- bicons: pad with He - _ -> Nothing +textToShoresh t = mapM charToConsonant (T.unpack t) >>= mkShoresh -- | The seven binyanim — transformation operators on roots. diff --git a/shoresh-engine/test/Spec.hs b/shoresh-engine/test/Spec.hs index 7c7a80e1..6d5909b7 100644 --- a/shoresh-engine/test/Spec.hs +++ b/shoresh-engine/test/Spec.hs @@ -64,32 +64,39 @@ consonantSpec = describe "Consonant" $ do shoreshSpec :: Spec shoreshSpec = describe "Shoresh" $ do it "constructs a triliteral root" $ do - let sh = mkShoresh Bet Resh Aleph - c1 sh `shouldBe` Bet - c2 sh `shouldBe` Resh - c3 sh `shouldBe` Aleph + let sh = triliteral Bet Resh Aleph + radicals sh `shouldBe` [Bet, Resh, Aleph] it "renders as Hebrew text" $ do - shoreshToText (mkShoresh Bet Resh Aleph) `shouldBe` "ברא" - shoreshToText (mkShoresh Qof Dalet Shin) `shouldBe` "קדש" + shoreshToText (triliteral Bet Resh Aleph) `shouldBe` "ברא" + shoreshToText (triliteral Qof Dalet Shin) `shouldBe` "קדש" it "parses from Hebrew text (3 consonants)" $ do - textToShoresh "ברא" `shouldBe` Just (mkShoresh Bet Resh Aleph) - textToShoresh "קדש" `shouldBe` Just (mkShoresh Qof Dalet Shin) + textToShoresh "ברא" `shouldBe` Just (triliteral Bet Resh Aleph) + textToShoresh "קדש" `shouldBe` Just (triliteral Qof Dalet Shin) - it "parses from Hebrew text (2 consonants — biconsonantal)" $ do - -- Build log: some roots in L1 are 2-consonant (e.g. "שמ") - let result = textToShoresh "שמ" - result `shouldSatisfy` isJust - -- Bicons pad with He - case result of - Just sh -> c3 sh `shouldBe` He - Nothing -> expectationFailure "Should have parsed" + it "a two-letter root stays two letters — nothing is added" $ do + -- L1 has roots of two consonants (בן, יד). This type once held + -- exactly three and padded these with He: בן became בנה. + fmap radicals (textToShoresh "בן") `shouldBe` Just [Bet, Nun] + fmap shoreshToText (textToShoresh "בן") `shouldBe` Just "בנ" + fmap gematriaRoot (textToShoresh "בן") `shouldBe` Just 52 + + it "a root of four or more is held whole" $ do + -- שמאל — once rejected as "too long". + fmap radicals (textToShoresh "שמאל") `shouldBe` Just [Shin, Mem, Aleph, Lamed] + fmap gematriaRoot (textToShoresh "שמאל") `shouldBe` Just 371 + + it "mkShoresh needs at least two consonants" $ do + mkShoresh [] `shouldBe` Nothing + mkShoresh [Aleph] `shouldBe` Nothing + mkShoresh [Bet, Nun] `shouldSatisfy` isJust it "rejects invalid input" $ do textToShoresh "" `shouldBe` Nothing - textToShoresh "א" `shouldBe` Nothing -- too short - textToShoresh "אבגד" `shouldBe` Nothing -- too long + textToShoresh "א" `shouldBe` Nothing -- one letter is not a root + textToShoresh "abc" `shouldBe` Nothing + textToShoresh "בר1" `shouldBe` Nothing binyanSpec :: Spec @@ -98,14 +105,14 @@ binyanSpec = describe "Binyanim" $ do length allBinyanim `shouldBe` 7 it "activates a root through all 7 binyanim" $ do - let sh = mkShoresh Bet Resh Aleph + let sh = triliteral Bet Resh Aleph let expanded = expand sh Map.size expanded `shouldBe` 7 -- Each binyan should produce a peulah with the same root mapM_ (\(b, p) -> root p `shouldBe` sh) (Map.toList expanded) it "activate produces correct grammatical features" $ do - let sh = mkShoresh Shin Mem Resh -- שמר = guard + let sh = triliteral Shin Mem Resh -- שמר = guard let p = activate Qal Third Singular Masculine Perfect sh root p `shouldBe` sh binyan p `shouldBe` Qal @@ -115,7 +122,7 @@ binyanSpec = describe "Binyanim" $ do aspect p `shouldBe` Perfect it "activateDefault uses 3ms perfect citation form" $ do - let sh = mkShoresh Kaf Tav Bet -- כתב = write + let sh = triliteral Kaf Tav Bet -- כתב = write let p = activateDefault Qal sh person p `shouldBe` Third number p `shouldBe` Singular @@ -133,11 +140,11 @@ gematriaSpec = describe "Gematria" $ do it "computes root gematria correctly" $ do -- ברא = Bet(2) + Resh(200) + Aleph(1) = 203 - gematriaRoot (mkShoresh Bet Resh Aleph) `shouldBe` 203 + gematriaRoot (triliteral Bet Resh Aleph) `shouldBe` 203 -- קדש = Qof(100) + Dalet(4) + Shin(300) = 404 - gematriaRoot (mkShoresh Qof Dalet Shin) `shouldBe` 404 + gematriaRoot (triliteral Qof Dalet Shin) `shouldBe` 404 -- שלם = Shin(300) + Lamed(30) + Mem(40) = 370 - gematriaRoot (mkShoresh Shin Lamed Mem) `shouldBe` 370 + gematriaRoot (triliteral Shin Lamed Mem) `shouldBe` 370 it "computes word gematria correctly" $ do -- תורה = Tav(400) + Vav(6) + Resh(200) + He(5) = 611 @@ -151,8 +158,8 @@ gematriaSpec = describe "Gematria" $ do gematriaWord "בְּרֵאשִׁ֖ית" `shouldBe` gematriaWord "בראשית" it "finds gematria neighbors" $ do - let bara = mkShoresh Bet Resh Aleph -- 203 - let other = mkShoresh Resh Gimel Gimel -- 200+3+3 = 206, not 203 + let bara = triliteral Bet Resh Aleph -- 203 + let other = triliteral Resh Gimel Gimel -- 200+3+3 = 206, not 203 let lex = Map.fromList [ (bara, ShoreshEntry bara Creation [] [] Map.empty) , (other, ShoreshEntry other Creation [] [] Map.empty) @@ -240,10 +247,10 @@ ketivQereSpec = describe "KetivQere" $ do graphSpec :: Spec graphSpec = describe "Graph" $ do it "finds roots sharing 2 consonants" $ do - let bara = mkShoresh Bet Resh Aleph -- ברא - let barak = mkShoresh Bet Resh Kaf -- ברך (shares ב,ר) - let garaEnt = mkShoresh Gimel Resh Aleph -- גרא (shares ר,א) - let unrelated = mkShoresh Shin Lamed Mem -- שלם (shares nothing) + let bara = triliteral Bet Resh Aleph -- ברא + let barak = triliteral Bet Resh Kaf -- ברך (shares ב,ר) + let garaEnt = triliteral Gimel Resh Aleph -- גרא (shares ר,א) + let unrelated = triliteral Shin Lamed Mem -- שלם (shares nothing) let lex = Map.fromList [ (bara, ShoreshEntry bara Creation [] [] Map.empty) , (barak, ShoreshEntry barak Blessing [] [] Map.empty) @@ -253,25 +260,48 @@ graphSpec = describe "Graph" $ do let ns = neighbors bara lex length ns `shouldBe` 2 -- barak and gara, not unrelated + it "compares only roots of the same length" $ do + let Just ben = textToShoresh "בן" + let Just bat = textToShoresh "בת" + let bana = triliteral Bet Nun He + sharedConsonants ben bat `shouldBe` 1 + sharedConsonants ben bana `shouldBe` 0 + let lex = Map.fromList + [ (ben, ShoreshEntry ben Creation [] [] Map.empty) + , (bat, ShoreshEntry bat Creation [] [] Map.empty) + , (bana, ShoreshEntry bana Creation [] [] Map.empty) + ] + map seShoresh (neighbors ben lex) `shouldBe` [bat] + map fst (neighborsByPosition ben lex) `shouldBe` ["c1"] + + it "labels a triliteral neighbor by the positions it shares" $ do + let bara = triliteral Bet Resh Aleph + let barak = triliteral Bet Resh Kaf + let lex = Map.fromList + [ (bara, ShoreshEntry bara Creation [] [] Map.empty) + , (barak, ShoreshEntry barak Blessing [] [] Map.empty) + ] + map fst (neighborsByPosition bara lex) `shouldBe` ["c1c2"] + it "counts shared consonants correctly" $ do - let sh1 = mkShoresh Bet Resh Aleph - let sh2 = mkShoresh Bet Resh Kaf + let sh1 = triliteral Bet Resh Aleph + let sh2 = triliteral Bet Resh Kaf sharedConsonants sh1 sh2 `shouldBe` 2 - let sh3 = mkShoresh Shin Lamed Mem + let sh3 = triliteral Shin Lamed Mem sharedConsonants sh1 sh3 `shouldBe` 0 kiTovSpec :: Spec kiTovSpec = describe "KiTov" $ do it "returns Tov when all roots exist in lexicon" $ do - let sh = mkShoresh Bet Resh Aleph + let sh = triliteral Bet Resh Aleph let entry = ShoreshEntry sh Creation [] [Reference Bereishit 1 1] Map.empty let lex = Map.fromList [(sh, entry)] let result = ProcessedResult [] [entry] kiTov result lex `shouldBe` Tov it "returns LoTov when roots are missing" $ do - let sh = mkShoresh Bet Resh Aleph + let sh = triliteral Bet Resh Aleph let entry = ShoreshEntry sh Creation [] [Reference Bereishit 1 1] Map.empty let emptyLex = Map.empty let result = ProcessedResult [] [entry] diff --git a/specs/shorashim_architecture.md b/specs/shorashim_architecture.md index 2373e44f..f6904390 100644 --- a/specs/shorashim_architecture.md +++ b/specs/shorashim_architecture.md @@ -119,13 +119,17 @@ data Consonant = Aleph | Bet | Gimel | Dalet | He | Vav | Zayin | Samekh | Ayin | Pe | Tsade | Qof | Resh | Shin | Tav deriving (Eq, Ord, Show, Enum, Bounded) --- A triliteral root — the atomic semantic unit -data Shoresh = Shoresh - { c1 :: Consonant - , c2 :: Consonant - , c3 :: Consonant +-- A root — the atomic semantic unit: the consonants under which the +-- lexicon files a word, exactly as L1 records them. Most roots have +-- three consonants; Torah also has roots of two (בן, יד) and of four or +-- more (שמאל). A root is never padded or truncated to make it three. +newtype Shoresh = Shoresh + { radicals :: [Consonant] -- two or more, in order } deriving (Eq, Ord, Show) +mkShoresh :: [Consonant] -> Maybe Shoresh -- needs at least two +triliteral :: Consonant -> Consonant -> Consonant -> Shoresh -- the common case + -- The seven binyanim — transformation operators data Binyan = Qal -- Simple active | Nifal -- Passive / reflexive @@ -157,7 +161,7 @@ data SemanticDomain = Creation | Separation | Speech | Movement data ShoreshEntry = ShoreshEntry { shoresh :: Shoresh , primaryDomain :: SemanticDomain - , relatedRoots :: [Shoresh] -- roots sharing 2 consonants + , relatedRoots :: [Shoresh] -- same length, differing in one consonant , torahOccurrences :: [Reference] -- where this root appears in Torah , glosses :: Map Binyan Text -- basic meanings per binyan } deriving (Show) @@ -176,7 +180,8 @@ data Reference = Reference -- Apply a binyan to a root (the fundamental operation) activate :: Binyan -> Shoresh -> Peulah --- Find all roots sharing 2 consonants (semantic neighborhood) +-- Find all roots of the same length differing in exactly one consonant +-- (for a root of three: sharing 2 consonants) — the semantic neighborhood neighbors :: Shoresh -> ShoreshLexicon -> [ShoreshEntry] -- Trace a root through all 7 binyanim (full semantic expansion) @@ -316,6 +321,7 @@ shoresh-engine/ │ ├── Shoresh/ │ │ ├── Types.hs -- All data types above │ │ ├── Lexicon.hs -- The root lexicon, read from L1 (1,378 roots) +│ │ ├── Roots.hs -- A word's root, heard from L1 (verse, lemma, Torah-wide) │ │ ├── Binyanim.hs -- Transformation operators │ │ ├── Havdalah.hs -- Separation/distinction engine │ │ ├── Kriah.hs -- Naming/identification @@ -324,23 +330,26 @@ shoresh-engine/ │ │ ├── Teamim.hs -- Te'amim reader (reads raw marks, constructs parse tree) │ │ ├── Gematria.hs -- Numerical value lookup and equivalences │ │ ├── Smikhut.hs -- Construct-state composition (reads attested compounds) -│ │ └── KetivQere.hs -- Dual reading type support +│ │ ├── KetivQere.hs -- Dual reading type support +│ │ └── Server.hs -- JSON-line port for the Vav Pipeline (Construction §3.1) │ ├── Luchot/ │ │ └── Firmware.hs -- L0 immutable constants (compiled in) │ └── Main.hs -├── data/ -│ ├── roots.json -- Root lexicon data -│ └── torah_refs.json -- Torah reference index ├── test/ │ └── Spec.hs +├── cabal.project.freeze └── shoresh-engine.cabal ``` +The engine carries no data files of its own. The roots and the Torah +references are in L1 (`l1/torah/`, found through `L1_MOUNT_POINT`) and +are read from there when the engine starts. + **Dependencies (Haskell packages):** - `base` - `containers` (Map, Set) - `text` -- `aeson` (JSON parsing for lexicon data) +- `aeson` (JSON: L1 verses and the port protocol) - `vector` --- diff --git a/vav_pipeline/lib/vav_pipeline/doorpost.ex b/vav_pipeline/lib/vav_pipeline/doorpost.ex index 0873209a..74e78ae0 100644 --- a/vav_pipeline/lib/vav_pipeline/doorpost.ex +++ b/vav_pipeline/lib/vav_pipeline/doorpost.ex @@ -175,7 +175,7 @@ defmodule VavPipeline.Doorpost do # changes; the update is a deliberate operator commit. A bare # hash-update commit (without a corresponding spec change in the # same commit) is itself a drift signal. - @expected_hash "094b8164dc1e5b280dc797d0e4c351ed67898a2046133e5f00a1972ffbde5661" + @expected_hash "f653ef0520c86bb378727fa877421796e18a424acc3fb396c62082b47c53cd50" @doc "Return the list of files that constitute the category-grade specification." @spec files() :: [String.t()]