specs §3.1 + engine + Doorpost: a root is the consonants L1 records — two, three, or more

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 <noreply@anthropic.com>
This commit is contained in:
Scott KarpandClaude Fable 5.1 committed 2026-10-04 18:42:52 +00:00
1 parent 4f1879d7ce
commit 5ff1b73d00
8 files changed
+153 -96

No files matched your search

+21 -12
View File
@@ -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`
---