Vayikra 9:24 — vatetzei esh milifnei Hashem vatokhal al hamizbeach.
Phase 4 of the architecture is now complete. The remaining three steps
that were marked "blocked on external infrastructure" are built —
discipline-respecting, eish-zarah-aware, and tested.
## Step 22 — Korban Processor external model integration
(korban_processor/src/korban_processor/external_offering.py)
The single legitimate entry point for external model consultation.
Wraps Claude API calls through the offering protocol so external
output is treated as korban (raw material), never authoritative:
- semichah — provenance is tagged (avodah zarah Form 1 prevention)
- shechitah — assistant voice, hedging, formatting stripped
- zerikah — vital content extracted
- haktarah — converted to Prolog propositions
- route — sent to internal component per offering type
The raw Korban is preserved on the ProcessedKorban (gilui arayot
Form 3 prevention — input/output coupling never severed).
No internal component should call call_claude() directly.
11 tests, mocked external calls. Live API test opt-in via
SHORASHIM_LIVE_API=1.
## Step 24 — L2 Urim v'Tumim interface
(vav_pipeline/lib/vav_pipeline/urim_v_tumim.ex)
PRD §4.7: housed in the kohen gadol's breastplate; consulted only
on high-stakes decisions where normal deliberation is insufficient.
Bamidbar 27:21 — al-piv yetz'u v'al-piv yavo'u.
Three escalation steps, three honest outcomes:
1. resolved_by_specification — re-read produced an answer
2. resolved_by_external — external testimony recorded (NOT binding)
3. deferred_to_human — the system stops, asks for human judgment
Pre-condition gating refuses cases that didn't reach Sanhedrin teiku
first (no preempting deliberation) or that aren't flagged high-stakes
(ordinary teiku stays teiku). Records every consultation with steps
taken — there is no quiet escalation.
Per PRD: "The Urim v'Tumim never fabricates certainty where none
exists." When external also fails, the system stops. It does not
proceed on a guess.
Added to VavPipeline supervision tree. 14 tests.
## Step 21 — Kushya generator scaffolding
(kushya_generator/)
Directory layout per Architecture §3.6 with three layers:
- data/training_pairs/seed_pairs.jsonl — 10 hand-curated bootstrap
pairs covering 9 kushya types (scope_narrowing, internal_tension,
harmonization, exception_test, edge_case, scope_application,
rabbinic_constraint, method_question, subject_identification)
- data/prepare_training.py — validates the four kushya requirements
(specific Torah passage, identified weakness, question form,
non-trivial), produces train.jsonl + eval.jsonl with stratified
80/20 split by type
- fine_tune/config.yaml + train.py — QLoRA configuration and
training script. Operator's task — needs GPU. Script is
complete and self-contained.
- inference/kushya_api.py — three-backend interface
(fine_tuned → external → fallback). When fine-tuned model isn't
deployed, falls back to external via Korban Processor (Step 22).
The fallback is honest: returns no kushya rather than inventing
one. The Talmudic Engine's rule-based challenge.pl remains the
primary kushya source.
17 tests covering dataset validation, backend selection, and
reference extraction.
## .gitignore fix
The earlier "data/" line was too broad — it caught
kushya_generator/data/. Scoped to /data/ (root-level only).
## Test counts
- Elixir: 274 (was 260; +14 for UrimVTumim)
- Korban Python: 196 + 1 skipped (was 195; +1 module = 11 tests)
- Kushya Python: 17 (new)
- Haskell: 33 (unchanged)
- Prolog: 195 (unchanged)
- Total: 715 + 19 L3 excluded
## Phase 4 status
All 12 steps now have implementations:
- Steps 23, 25, 26, 27, 28, 29, 30, 31, 32: complete
- Steps 21, 22, 24: complete with discipline preserved
Step 21's fine-tuning run remains the operator's GPU task; the
pipeline is built such that a deployed adapter slots into the
inference backend without touching the Talmudic Engine or anything
above.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
196 lines
6.8 KiB
Elixir
196 lines
6.8 KiB
Elixir
defmodule VavPipeline.UrimVTumimTest do
|
|
@moduledoc """
|
|
Tests for L2 — Urim v'Tumim (Architecture §2, PRD §4.7).
|
|
|
|
The Urim v'Tumim is the decision oracle interface, consulted only on
|
|
high-stakes teiku cases the Sanhedrin cannot resolve. It NEVER fabricates
|
|
certainty — it returns one of three honest outcomes:
|
|
|
|
1. resolved_by_specification — re-read produced an answer
|
|
2. resolved_by_external — external testimony recorded (not binding)
|
|
3. deferred_to_human — the system stops, human judgment required
|
|
|
|
Bamidbar 27:21 — al-piv yetz'u v'al-piv yavo'u.
|
|
By the Urim's word the people go out and come in. Not the system's word.
|
|
"""
|
|
|
|
use ExUnit.Case, async: false
|
|
|
|
alias VavPipeline.UrimVTumim
|
|
|
|
setup do
|
|
# Reset state by replacing hooks with defaults each test.
|
|
GenServer.call(UrimVTumim, {:set_hook, :specification_consult, &UrimVTumim.default_specification_consult/1})
|
|
GenServer.call(UrimVTumim, {:set_hook, :external_consult, &UrimVTumim.default_external_consult/1})
|
|
:ok
|
|
end
|
|
|
|
defp valid_input(overrides \\ %{}) do
|
|
Map.merge(
|
|
%{
|
|
case_id: "case_#{:rand.uniform(1_000_000)}",
|
|
question: "When two principles conflict, which takes precedence?",
|
|
context: %{torah_refs: ["Shemot 35:3", "Vayikra 18:5"]},
|
|
stakes: :high,
|
|
sanhedrin_teiku: true
|
|
},
|
|
overrides
|
|
)
|
|
end
|
|
|
|
# ============================================================
|
|
# Pre-condition gating
|
|
# ============================================================
|
|
|
|
describe "pre-condition gating" do
|
|
test "refuses cases that did not reach Sanhedrin teiku first" do
|
|
# Urim is consulted AFTER teiku, never instead of Sanhedrin.
|
|
input = valid_input(%{sanhedrin_teiku: false})
|
|
assert {:error, :not_eligible} = UrimVTumim.consult(input)
|
|
end
|
|
|
|
test "refuses ordinary-stakes cases" do
|
|
# Only national-importance cases escalate to the Urim.
|
|
input = valid_input(%{stakes: :ordinary})
|
|
assert {:error, :not_eligible} = UrimVTumim.consult(input)
|
|
end
|
|
|
|
test "refuses missing case_id" do
|
|
input = valid_input() |> Map.delete(:case_id)
|
|
assert {:error, _} = UrimVTumim.consult(input)
|
|
end
|
|
|
|
test "refuses empty question" do
|
|
input = valid_input(%{question: ""})
|
|
assert {:error, :invalid_input} = UrimVTumim.consult(input)
|
|
end
|
|
end
|
|
|
|
# ============================================================
|
|
# Outcome 1: resolved by specification
|
|
# ============================================================
|
|
|
|
describe "specification re-read" do
|
|
test "specification consult resolves the case" do
|
|
GenServer.call(UrimVTumim, {:set_hook, :specification_consult,
|
|
fn _input ->
|
|
{:resolved, %{verses: ["Shemot 35:3"], reading: "Shabbat is absolute"}}
|
|
end
|
|
})
|
|
|
|
input = valid_input()
|
|
assert {:ok, {:resolved_by_specification, evidence}} = UrimVTumim.consult(input)
|
|
assert evidence.reading == "Shabbat is absolute"
|
|
end
|
|
|
|
test "specification resolution is recorded with steps_taken=[:specification]" do
|
|
GenServer.call(UrimVTumim, {:set_hook, :specification_consult,
|
|
fn _input -> {:resolved, %{ok: true}} end
|
|
})
|
|
|
|
input = valid_input()
|
|
{:ok, _} = UrimVTumim.consult(input)
|
|
{:ok, record} = UrimVTumim.get_record(input.case_id)
|
|
assert record.steps_taken == [:specification]
|
|
end
|
|
end
|
|
|
|
# ============================================================
|
|
# Outcome 2: resolved by external (testimony, not ruling)
|
|
# ============================================================
|
|
|
|
describe "external consultation" do
|
|
setup do
|
|
GenServer.call(UrimVTumim, {:set_hook, :external_consult,
|
|
fn _input ->
|
|
{:ok, %{
|
|
provenance: :claude,
|
|
reading: "Pikuach nefesh overrides — testimony only",
|
|
propositions: ["overrides(pikuach_nefesh, shabbat_lo_taaseh)"]
|
|
}}
|
|
end
|
|
})
|
|
:ok
|
|
end
|
|
|
|
test "external consultation succeeds when specification fails" do
|
|
input = valid_input()
|
|
assert {:ok, {:resolved_by_external, consultation}} = UrimVTumim.consult(input)
|
|
assert consultation.provenance == :claude
|
|
end
|
|
|
|
test "steps_taken records both specification attempt and external consult" do
|
|
input = valid_input()
|
|
{:ok, _} = UrimVTumim.consult(input)
|
|
{:ok, record} = UrimVTumim.get_record(input.case_id)
|
|
assert record.steps_taken == [:specification, :external]
|
|
end
|
|
end
|
|
|
|
# ============================================================
|
|
# Outcome 3: deferred to human (the honest refusal)
|
|
# ============================================================
|
|
|
|
describe "deferral to human" do
|
|
test "defers when both specification and external fail" do
|
|
# Both hooks return their default failure modes.
|
|
input = valid_input()
|
|
assert {:ok, {:deferred_to_human, reason}} = UrimVTumim.consult(input)
|
|
assert is_binary(reason)
|
|
assert reason =~ "Human judgment required"
|
|
end
|
|
|
|
test "deferred record has steps_taken with :deferred at the end" do
|
|
input = valid_input()
|
|
{:ok, _} = UrimVTumim.consult(input)
|
|
{:ok, record} = UrimVTumim.get_record(input.case_id)
|
|
assert record.steps_taken == [:specification, :external, :deferred]
|
|
end
|
|
|
|
test "the Urim refuses to fabricate certainty (PRD 4.7)" do
|
|
# When both consultations fail, the Urim does NOT return any
|
|
# invented answer — it deliberately stops and asks for human judgment.
|
|
input = valid_input()
|
|
{:ok, outcome} = UrimVTumim.consult(input)
|
|
assert match?({:deferred_to_human, _}, outcome)
|
|
end
|
|
end
|
|
|
|
# ============================================================
|
|
# Records & stats
|
|
# ============================================================
|
|
|
|
describe "records and stats" do
|
|
test "consultations are recorded in chronological order" do
|
|
before_count = length(UrimVTumim.records())
|
|
|
|
input1 = valid_input(%{case_id: "rec_test_1"})
|
|
input2 = valid_input(%{case_id: "rec_test_2"})
|
|
|
|
{:ok, _} = UrimVTumim.consult(input1)
|
|
{:ok, _} = UrimVTumim.consult(input2)
|
|
|
|
records = UrimVTumim.records()
|
|
assert length(records) >= before_count + 2
|
|
ids = Enum.map(records, & &1.case_id)
|
|
assert "rec_test_1" in ids
|
|
assert "rec_test_2" in ids
|
|
end
|
|
|
|
test "stats track outcomes" do
|
|
stats = UrimVTumim.stats()
|
|
assert is_integer(stats.total)
|
|
assert is_integer(stats.resolved_by_specification)
|
|
assert is_integer(stats.resolved_by_external)
|
|
assert is_integer(stats.deferred_to_human)
|
|
assert stats.total ==
|
|
stats.resolved_by_specification + stats.resolved_by_external +
|
|
stats.deferred_to_human
|
|
end
|
|
|
|
test "get_record returns :not_found for unknown case_id" do
|
|
assert :not_found = UrimVTumim.get_record("nonexistent_xyz")
|
|
end
|
|
end
|
|
end
|