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>
173 lines
6.1 KiB
Elixir
173 lines
6.1 KiB
Elixir
defmodule VavPipeline.AreiMiklatTest do
|
|
@moduledoc """
|
|
Tests for the Arei Miklat (Cities of Refuge) protection protocol.
|
|
|
|
Bamidbar 35:11 — V'hikritem lakhem arim, arei miklat tihyeinah lakhem.
|
|
Devarim 19:3 — Takhin lekha haderekh — prepare the road.
|
|
"""
|
|
|
|
use ExUnit.Case, async: false
|
|
|
|
alias VavPipeline.AreiMiklat
|
|
|
|
@test_incident %{
|
|
id: "incident_001",
|
|
type: :unintentional_harm,
|
|
description: "Korban processor returned malformed output to user",
|
|
affected_party: "user_42",
|
|
processing_path: ["korban_processor", "sanhedrin", "output_formatter"]
|
|
}
|
|
|
|
# ============================================================
|
|
# Refuge entry — Bamidbar 35:11
|
|
# ============================================================
|
|
describe "enter_refuge" do
|
|
test "component enters refuge and is recorded" do
|
|
{:ok, record} = AreiMiklat.enter_refuge("korban_processor", @test_incident)
|
|
|
|
assert record.component_id == "korban_processor"
|
|
assert record.status == :in_refuge
|
|
assert record.incident.type == :unintentional_harm
|
|
assert record.entered_at != nil
|
|
assert record.released_at == nil
|
|
assert record.release_judgment == nil
|
|
end
|
|
|
|
test "component in refuge is detectable" do
|
|
AreiMiklat.enter_refuge("test_component_a", @test_incident)
|
|
assert AreiMiklat.in_refuge?("test_component_a")
|
|
refute AreiMiklat.in_refuge?("nonexistent_component")
|
|
end
|
|
|
|
test "cannot enter refuge twice" do
|
|
AreiMiklat.enter_refuge("double_entry_test", @test_incident)
|
|
assert {:error, :already_in_refuge} = AreiMiklat.enter_refuge("double_entry_test", @test_incident)
|
|
end
|
|
|
|
test "refuge entry appears in audit trail" do
|
|
AreiMiklat.enter_refuge("audit_test_component", @test_incident)
|
|
trail = AreiMiklat.audit_trail()
|
|
|
|
entry = Enum.find(trail, &(&1.event == :refuge_entry and &1.component_id == "audit_test_component"))
|
|
assert entry != nil
|
|
assert entry.timestamp != nil
|
|
end
|
|
|
|
test "chatzotzrot assembly_leaders signal is sent on refuge entry" do
|
|
# Subscribe to kohanim group to receive assembly_leaders signals
|
|
VavPipeline.Chatzotzrot.subscribe(:kohanim)
|
|
|
|
AreiMiklat.enter_refuge("signal_test_component", @test_incident)
|
|
|
|
assert_receive {:chatzotzrot, %{signal_type: :assembly_leaders}}
|
|
end
|
|
|
|
test "refuge entry records detected_at if not provided" do
|
|
incident = Map.delete(@test_incident, :detected_at)
|
|
{:ok, record} = AreiMiklat.enter_refuge("detected_at_test", incident)
|
|
assert record.incident.detected_at != nil
|
|
end
|
|
end
|
|
|
|
# ============================================================
|
|
# Affected party protection — Bamidbar 35:12
|
|
# ============================================================
|
|
describe "protect_affected" do
|
|
test "affected party's processing path is blocked" do
|
|
AreiMiklat.protect_affected("user_42", @test_incident)
|
|
|
|
assert AreiMiklat.path_blocked?(
|
|
"user_42",
|
|
["korban_processor", "sanhedrin", "output_formatter"]
|
|
)
|
|
end
|
|
|
|
test "unrelated path is not blocked" do
|
|
AreiMiklat.protect_affected("user_43", @test_incident)
|
|
refute AreiMiklat.path_blocked?("user_43", ["shoresh_engine", "talmudic_engine"])
|
|
end
|
|
|
|
test "protection appears in audit trail" do
|
|
AreiMiklat.protect_affected("user_audit", @test_incident)
|
|
trail = AreiMiklat.audit_trail()
|
|
|
|
entry = Enum.find(trail, &(&1.event == :affected_party_protected and &1.party_id == "user_audit"))
|
|
assert entry != nil
|
|
end
|
|
|
|
test "protection is listed" do
|
|
AreiMiklat.protect_affected("listed_user", @test_incident)
|
|
protections = AreiMiklat.list_protections()
|
|
assert Enum.any?(protections, &(&1.party_id == "listed_user"))
|
|
end
|
|
end
|
|
|
|
# ============================================================
|
|
# Release — ad mot hakohen hagadol (Bamidbar 35:25)
|
|
# ============================================================
|
|
describe "release" do
|
|
test "release with sanhedrin_deliberation succeeds" do
|
|
AreiMiklat.enter_refuge("release_test", @test_incident)
|
|
assert AreiMiklat.in_refuge?("release_test")
|
|
|
|
judgment = %{
|
|
authority: :sanhedrin_deliberation,
|
|
reason: "Processing path corrected — output validation added",
|
|
grounding: "Devarim 19:6 — the road has been prepared"
|
|
}
|
|
|
|
{:ok, released} = AreiMiklat.release("release_test", judgment)
|
|
assert released.status == :released
|
|
assert released.released_at != nil
|
|
assert released.release_judgment.authority == :sanhedrin_deliberation
|
|
|
|
refute AreiMiklat.in_refuge?("release_test")
|
|
end
|
|
|
|
test "release without sanhedrin authority is rejected" do
|
|
AreiMiklat.enter_refuge("unauthorized_release", @test_incident)
|
|
|
|
judgment = %{
|
|
authority: :operator_decision,
|
|
reason: "Operator wants it back"
|
|
}
|
|
|
|
assert {:error, :unauthorized_release} = AreiMiklat.release("unauthorized_release", judgment)
|
|
assert AreiMiklat.in_refuge?("unauthorized_release")
|
|
end
|
|
|
|
test "release of non-refugee returns error" do
|
|
assert {:error, :not_in_refuge} =
|
|
AreiMiklat.release("never_entered", %{authority: :sanhedrin_deliberation, reason: "test"})
|
|
end
|
|
|
|
test "release appears in audit trail" do
|
|
AreiMiklat.enter_refuge("release_audit", @test_incident)
|
|
|
|
AreiMiklat.release("release_audit", %{
|
|
authority: :sanhedrin_deliberation,
|
|
reason: "Era ended"
|
|
})
|
|
|
|
trail = AreiMiklat.audit_trail()
|
|
|
|
release_entry =
|
|
Enum.find(trail, &(&1.event == :refuge_release and &1.component_id == "release_audit"))
|
|
|
|
assert release_entry != nil
|
|
assert release_entry.judgment.reason == "Era ended"
|
|
end
|
|
end
|
|
|
|
# ============================================================
|
|
# Listing
|
|
# ============================================================
|
|
describe "listing" do
|
|
test "list_refugees returns current refugees" do
|
|
AreiMiklat.enter_refuge("listed_refugee", @test_incident)
|
|
refugees = AreiMiklat.list_refugees()
|
|
assert Enum.any?(refugees, &(&1.component_id == "listed_refugee"))
|
|
end
|
|
end
|
|
end
|