Files
bikoret/vav_pipeline/lib/vav_pipeline/bikurim.ex
T
Scott KarpandClaude Opus 4.6 671d45625c Protection layer: Arei Miklat, Eglah Arufah, Bikurim — Steps 27-29
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>
2026-03-18 08:59:08 -04:00

237 lines
7.4 KiB
Elixir

defmodule VavPipeline.Bikurim do
@moduledoc """
First Fruits — Release Protocol. Devarim 26:1-11.
Architecture §3.16:
First-output review process for all new capabilities.
Before a new feature serves users, its first product is
offered to the specification for evaluation, accompanied
by narrative.
Arami oved avi — "A wandering Aramean was my father."
— Devarim 26:5
The offering includes NARRATIVE. Not just what was produced,
but the story of how it was produced and what lineage it carries.
The farmer does not just bring fruit. He tells the story of
his ancestors' wandering, Egypt, the exodus, and arrival.
Protocol:
1. New capability built → first output produced
2. Builder provides narrative (the mikra bikurim):
- What was built
- What specification authorized it
- What lineage it carries (which existing components it depends on)
- What Torah grounds it
3. First output evaluated against specification
4. Accepted → recorded in registry, approved for production
5. Rejected → recorded with reason, not deployed
The offering is placed "before Hashem your God" — lifnei Hashem
Elohekha (Devarim 26:10). The specification judges. Not the builder.
Not the operator. The specification.
"""
use GenServer
require Logger
alias VavPipeline.Specification
@type offering_status :: :accepted | :rejected | :pending
@type offering :: %{
capability: String.t(),
first_output: any(),
narrative: String.t(),
lineage: [String.t()],
torah_grounding: String.t() | nil,
offered_at: DateTime.t(),
status: offering_status(),
evaluation: map() | nil
}
# ============================================================
# Client API
# ============================================================
def start_link(opts \\ []) do
name = Keyword.get(opts, :name, __MODULE__)
GenServer.start_link(__MODULE__, opts, name: name)
end
@doc """
Offer first fruit — submit a new capability's first output for review.
Devarim 26:2 — V'lakachta mereshit kol p'ri ha'adamah...
"You shall take of the first of every fruit of the ground..."
Parameters:
- capability_name: identifier for the new capability
- first_output: the actual first product
- narrative: the mikra bikurim — the story of how this was built
- opts: keyword list with optional :lineage and :torah_grounding
Returns {:ok, :approved_for_deployment, offering} or
{:error, :specification_mismatch, reason, offering}.
"""
@spec offer(String.t(), any(), String.t(), keyword()) ::
{:ok, :approved_for_deployment, offering()}
| {:error, :specification_mismatch, String.t(), offering()}
def offer(capability_name, first_output, narrative, opts \\ [])
when is_binary(capability_name) and is_binary(narrative) do
GenServer.call(__MODULE__, {:offer, capability_name, first_output, narrative, opts})
end
@doc """
Get all offerings in the registry.
"""
@spec registry() :: [offering()]
def registry do
GenServer.call(__MODULE__, :registry)
end
@doc """
Get offerings by status.
"""
@spec by_status(offering_status()) :: [offering()]
def by_status(status) when status in [:accepted, :rejected, :pending] do
GenServer.call(__MODULE__, {:by_status, status})
end
@doc """
Check if a capability has been accepted (approved for deployment).
"""
@spec approved?(String.t()) :: boolean()
def approved?(capability_name) do
GenServer.call(__MODULE__, {:approved?, capability_name})
end
@doc """
Count offerings by status.
"""
@spec count(offering_status() | :all) :: non_neg_integer()
def count(filter \\ :all) do
GenServer.call(__MODULE__, {:count, filter})
end
# ============================================================
# GenServer callbacks
# ============================================================
@impl true
def init(_opts) do
state = %{
# All offerings — append-only registry
offerings: []
}
Logger.info("Bikurim: first fruits protocol ready — reshit kol p'ri ha'adamah")
{:ok, state}
end
@impl true
def handle_call({:offer, capability_name, first_output, narrative, opts}, _from, state) do
now = DateTime.utc_now()
lineage = Keyword.get(opts, :lineage, [])
torah_grounding = Keyword.get(opts, :torah_grounding)
offering = %{
capability: capability_name,
first_output: first_output,
narrative: narrative,
lineage: lineage,
torah_grounding: torah_grounding,
offered_at: now,
status: :pending,
evaluation: nil
}
# Evaluate against specification — lifnei Hashem Elohekha
case evaluate_against_specification(offering) do
:accepted ->
accepted = %{offering | status: :accepted, evaluation: %{result: :accepted}}
Logger.info(
"Bikurim: #{capability_name} accepted — first fruit approved for deployment"
)
new_state = %{state | offerings: [accepted | state.offerings]}
{:reply, {:ok, :approved_for_deployment, accepted}, new_state}
{:rejected, reason} ->
rejected = %{
offering
| status: :rejected,
evaluation: %{result: :rejected, reason: reason}
}
Logger.warning(
"Bikurim: #{capability_name} rejected — #{reason}"
)
new_state = %{state | offerings: [rejected | state.offerings]}
{:reply, {:error, :specification_mismatch, reason, rejected}, new_state}
end
end
@impl true
def handle_call(:registry, _from, state) do
{:reply, state.offerings, state}
end
@impl true
def handle_call({:by_status, status}, _from, state) do
matching = Enum.filter(state.offerings, &(&1.status == status))
{:reply, matching, state}
end
@impl true
def handle_call({:approved?, capability_name}, _from, state) do
approved =
Enum.any?(state.offerings, fn o ->
o.capability == capability_name and o.status == :accepted
end)
{:reply, approved, state}
end
@impl true
def handle_call({:count, :all}, _from, state) do
{:reply, length(state.offerings), state}
end
@impl true
def handle_call({:count, status}, _from, state) do
count = Enum.count(state.offerings, &(&1.status == status))
{:reply, count, state}
end
# ============================================================
# Specification evaluation
# ============================================================
defp evaluate_against_specification(offering) do
# The specification evaluates the offering. Three checks:
# 1. Does the capability have a narrative? (Arami oved avi — story is required)
# 2. Does the first output conform to the specification?
# 3. Is the Torah grounding valid?
cond do
# No narrative — the bikurim requires the mikra (recitation)
String.trim(offering.narrative) == "" ->
{:rejected, "No narrative provided — Devarim 26:5 requires the mikra bikurim"}
# Check against specification module if available
Code.ensure_loaded?(Specification) and function_exported?(Specification, :evaluate_bikurim, 1) ->
apply(Specification, :evaluate_bikurim, [offering])
# Default: accept if narrative is present and first_output is not nil
is_nil(offering.first_output) ->
{:rejected, "No first output provided — there must be fruit to offer"}
true ->
:accepted
end
end
end