judge.erl chose one of six canned positions by a hash over the question,
its own pid and the clock; a panel of one "accepted" on zero roots and
zero references under "Torah references ground this interpretation" —
the calf, cast by the court itself. Architecture §3.2 says a judge
"consults the specification (L0/L1)"; Construction §3.2 says the
pipeline hands the case roots and torah_refs. Neither happened.
Sanhedrin (Erlang):
- judge.erl: seat N performs step N of the dialectic — interpret,
challenge, resolve, cycling — through a consult {M, F} in the app env.
resolved → accept with the verses; challenged (a kushya stands) →
reject; halt → reject; teiku → abstain. No consult, an error, or a
consult that raises → abstain, and the reasoning says so; the judge
stands. judge:start_link/2 takes the seat; panel.erl seats its judges.
- EUnit 54/0: seats cycle; no consult → abstain; resolved → accept with
#torah_ref{}; a raising consult → abstain and alive; a panel of three
with a kushya at seat 2 → accepted 2–1, minority preserved with verse.
Pipeline (Elixir):
- VavPipeline.SanhedrinConsult: what a judge asks — TalmudicEngineSocket
interpret/challenge/resolve over the case's roots, direct (the client's
stub fallbacks are NOT used: a court that accepts on a stub is the
calf). Engine absent → {:error, _} → the judge abstains. Registered in
the sanhedrin env at application start.
- router process_sanhedrin: the case is heard before it is submitted —
ScopeResolver → Shema context (cloud honoured) → tohu → vayomer
(Shoresh Engine over the verse) → roots; torah_refs = the scope;
complexity_hint names the panel (422 on an unknown one); one unit of
sanhedrin_panels_max per case (503 MANNA_DEPLETED when spent); the
decision engraved in L3 as an interpretation (panel_size, votes
for/against, processing_component "sanhedrin", coupling root→verse)
with the minority beside it (grounding_type achorai); the reply in
the Tamid's shape (position, reasoning, sources, votes) plus
grounding {scope, roots_consulted, recorded}.
- Tests: Elixir 628/0 (router 30, incl. monetary → panel 3; no engine →
teiku with 0 votes, never accepted; fake consult → 2–1 with sources;
manna → 503; unknown hint → 422); L3 test: interpretation + minority
rows land.
Pre-existing and untouched: with --include l3 the gate and hakhel
tests fail 17 times on main as well (the gate's local upstream double
refuses connections under the L3 flag) — a separate finding.
Doorpost pin unchanged (no @spec_files touched).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
147 lines
5.4 KiB
Elixir
147 lines
5.4 KiB
Elixir
defmodule VavPipeline.Application do
|
|
@moduledoc """
|
|
OTP Application for the Vav Pipeline.
|
|
|
|
Starts the supervision tree with:
|
|
- Repo (L3 PostgreSQL — oral tradition persistence)
|
|
- Chatzotzrot (silver trumpets — signal system)
|
|
- AreiMiklat (cities of refuge — protection protocol)
|
|
- EglahArufah (accountability for unknown harm)
|
|
- Bikurim (first fruits — release protocol)
|
|
- MannaLedger (consumption tracking and anti-hoarding enforcement)
|
|
- HaltMonitor (esh tamid — cardinal sin detection)
|
|
- ShabbatTimer (mandatory cessation monitor)
|
|
- Bandit HTTP server (bigdei kodesh — the garments)
|
|
- ShoreshEnginePort (Erlang Port to Haskell engine, if binary found)
|
|
- TalmudicEngineSocket (Unix socket to Prolog engine)
|
|
|
|
Step 14 (Architecture §4): Component connections supervised.
|
|
The port and socket are optional — the pipeline degrades gracefully
|
|
if external engines are not available (clients fall back to stubs).
|
|
"""
|
|
|
|
use Application
|
|
|
|
require Logger
|
|
|
|
@impl true
|
|
def start(_type, _args) do
|
|
# Tablet 55: the Sanhedrin's judges consult the specification through
|
|
# this callback (the Talmudic Engine behind it). Registered before the
|
|
# supervision tree so the first panel convened can already consult.
|
|
Application.put_env(:sanhedrin, :consult, {VavPipeline.SanhedrinConsult, :consult})
|
|
|
|
shoresh_binary = Application.get_env(:vav_pipeline, :shoresh_engine_binary, "shoresh-engine")
|
|
shoresh_cwd = Application.get_env(:vav_pipeline, :shoresh_engine_cwd, nil)
|
|
talmudic_socket = Application.get_env(:vav_pipeline, :talmudic_socket_path, "/tmp/bikoret/talmudic.sock")
|
|
korban_socket = Application.get_env(:vav_pipeline, :korban_socket_path, "/tmp/bikoret/korban.sock")
|
|
|
|
children =
|
|
[
|
|
{VavPipeline.Chatzotzrot, []},
|
|
# Tablet 38 — reads the scroll and hashes it; everything that asks
|
|
# "is L1 intact?" is answered from this reading.
|
|
{VavPipeline.L1Integrity, []},
|
|
# The second scroll's witness (bishe'arecha §IV)
|
|
{VavPipeline.Mesorah, []},
|
|
{VavPipeline.AreiMiklat, []},
|
|
{VavPipeline.EglahArufah, []},
|
|
{VavPipeline.Bikurim, []},
|
|
{VavPipeline.MannaLedger, []},
|
|
{VavPipeline.HaltMonitor, []},
|
|
{VavPipeline.UrimVTumim, []},
|
|
{VavPipeline.ShabbatTimer, []},
|
|
{VavPipeline.VoiceMonitor, []},
|
|
{VavPipeline.MachtzitHaShekel, []},
|
|
# Tablet 30 — the index that lets a question name its own verse
|
|
{VavPipeline.L1Index, []},
|
|
{VavPipeline.BigdeiKodesh.Mishmar, []}
|
|
]
|
|
|> maybe_add_http_server()
|
|
|> maybe_add_repo()
|
|
|> maybe_add_shoresh_port(shoresh_binary, shoresh_cwd)
|
|
|> maybe_add_talmudic_socket(talmudic_socket)
|
|
|> maybe_add_korban_socket(korban_socket)
|
|
|
|
opts = [strategy: :one_for_one, name: VavPipeline.Supervisor]
|
|
result = Supervisor.start_link(children, opts)
|
|
|
|
# Doorpost startup verification (vehaya §3.1, item 1).
|
|
# Devarim 11:18 — corporate inscription is the load-bearing
|
|
# input to threshold practice. The corporate text must be
|
|
# identifiable at startup; drift detected here gets surfaced
|
|
# via Logger.error AND chatzotzrot :assembly_leaders so
|
|
# operators are alerted before any reception fires. We do NOT
|
|
# refuse to boot — dev environments routinely have spec drift
|
|
# during active editing — but we make the drift visible.
|
|
verify_doorpost_at_startup()
|
|
|
|
result
|
|
end
|
|
|
|
defp verify_doorpost_at_startup do
|
|
case VavPipeline.Doorpost.verify_identity() do
|
|
:ok ->
|
|
Logger.info("Doorpost: spec identity verified — corporate text holds")
|
|
|
|
{:error, reason} ->
|
|
Logger.error("DOORPOST_DRIFT_AT_STARTUP: #{reason}")
|
|
|
|
if Process.whereis(VavPipeline.Chatzotzrot) do
|
|
VavPipeline.Chatzotzrot.signal(:assembly_leaders,
|
|
reason: "Doorpost drift detected at startup: " <> reason
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
defp maybe_add_http_server(children) do
|
|
port = Application.get_env(:vav_pipeline, :http_port, 4000)
|
|
|
|
if port do
|
|
children ++ [{Bandit, plug: VavPipeline.BigdeiKodesh.Router, port: port}]
|
|
else
|
|
children
|
|
end
|
|
end
|
|
|
|
defp maybe_add_repo(children) do
|
|
repo_config = Application.get_env(:vav_pipeline, VavPipeline.Repo)
|
|
|
|
cond do
|
|
is_nil(repo_config) ->
|
|
Logger.warning("VavPipeline.Repo: no config found — running without L3 persistence")
|
|
children
|
|
|
|
repo_config[:start] == false ->
|
|
Logger.info("VavPipeline.Repo: start disabled — running without L3 persistence")
|
|
children
|
|
|
|
true ->
|
|
[VavPipeline.Repo | children]
|
|
end
|
|
end
|
|
|
|
defp maybe_add_shoresh_port(children, binary, cwd) do
|
|
case System.find_executable(binary) do
|
|
nil ->
|
|
Logger.warning("ShoreshEnginePort: binary '#{binary}' not in PATH — running without Haskell engine")
|
|
children
|
|
|
|
path ->
|
|
children ++ [{VavPipeline.ShoreshEnginePort, [binary: path, cwd: cwd]}]
|
|
end
|
|
end
|
|
|
|
# The Korban Processor's door (Construction §3.9) — connects lazily on
|
|
# the first bedikah; nothing is examined if it is not there.
|
|
defp maybe_add_korban_socket(children, socket_path) do
|
|
children ++ [{VavPipeline.KorbanProcessorSocket, [socket_path: socket_path]}]
|
|
end
|
|
|
|
defp maybe_add_talmudic_socket(children, socket_path) do
|
|
# Always add the socket client — it connects lazily on first request
|
|
children ++ [{VavPipeline.TalmudicEngineSocket, [socket_path: socket_path]}]
|
|
end
|
|
end
|