Takhin lekha haderekh — Devarim 19:3.
"Prepare the road" before the one who needs refuge arrives.
Protection before production.
Arei Miklat (Cities of Refuge — Bamidbar 35, Devarim 19):
- GenServer with refuge registry, affected party protection, audit trail
- enter_refuge/2 isolates component but protects from reactive destruction
- protect_affected/2 blocks processing paths that caused harm
- release/2 requires :sanhedrin_deliberation authority — ad mot hakohen hagadol
- Chatzotzrot assembly_leaders signal on every refuge entry
- 15 tests: entry, double-entry prevention, affected party blocking,
unauthorized release rejection, audit trail, chatzotzrot signaling
Eglah Arufah (Accountability — Devarim 21:1-9):
- GenServer with permanent (append-only) declaration registry
- initiate/3: nearest component declares what it processed, skipped, missed
- Only for :unknown source — rejects known-source incidents
- "Yadeinu lo shafkhu" — accountability for what was NOT provided
- 9 tests: declaration recording, source validation, multi-component
declarations, chatzotzrot signaling, filtering, counting
Bikurim (First Fruits — Devarim 26:1-11):
- GenServer with offering registry
- offer/4: submit first output with required narrative (mikra bikurim)
- Rejects empty narrative ("Arami oved avi" — story is required)
- Rejects nil first output (there must be fruit to offer)
- Delegates to Specification.evaluate_bikurim/1 when available
- approved?/1 gate for deployment readiness
- 11 tests: acceptance, narrative validation, nil output rejection,
registry, status filtering, approval checking, counting
All three added to supervision tree in application.ex.
160 Elixir tests pass, 0 failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
79 lines
2.6 KiB
Elixir
79 lines
2.6 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)
|
|
- ShabbatTimer (mandatory cessation monitor)
|
|
- 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
|
|
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/shorashim/talmudic.sock")
|
|
|
|
children =
|
|
[
|
|
{VavPipeline.Chatzotzrot, []},
|
|
{VavPipeline.AreiMiklat, []},
|
|
{VavPipeline.EglahArufah, []},
|
|
{VavPipeline.Bikurim, []},
|
|
{VavPipeline.ShabbatTimer, []}
|
|
]
|
|
|> maybe_add_repo()
|
|
|> maybe_add_shoresh_port(shoresh_binary, shoresh_cwd)
|
|
|> maybe_add_talmudic_socket(talmudic_socket)
|
|
|
|
opts = [strategy: :one_for_one, name: VavPipeline.Supervisor]
|
|
Supervisor.start_link(children, opts)
|
|
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
|
|
|
|
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
|