Phase 4 complete: Steps 21, 22, 24 — Korban external integration, L2 Urim v'Tumim, Kushya generator

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>
This commit is contained in:
Scott KarpandClaude Opus 4.6 committed 2026-04-23 17:12:03 -04:00
1 parent d4d0a6fcb6
commit e223ab15b3
15 files changed
+2003 -1

No files matched your search

@@ -0,0 +1,141 @@
"""
External offering — the flow from outbound query to processed korban.
Architecture §4 Step 22: Korban Processor integration with external models.
PRD §5.1: The system reasons, it does not predict. External model output is
korban (raw material), not instruction. Every external response MUST pass
through the offering protocol before any internal component touches it.
Eish zarah discipline (specs/eish_zarah_reading.md):
- External output is tagged with its provenance — the source is never
concealed (Form 1: avodah zarah is treating external output as if it
were Torah).
- The raw korban is preserved on the ProcessedKorban (Form 3: gilui
arayot is severing output from its source).
- The coupling_trace records that propositions derived from external
output are NOT Torah-grounded — only Torah-coupled when the Talmudic
Engine later grounds them via Torah verses.
- A confidence value is never assigned to external content at this
boundary (Form 2: golden calf is confident-but-uncoupled output).
This module is the only entry point for external model consultation.
No internal component calls an external model directly.
"""
from dataclasses import dataclass
from typing import Optional
from korban_processor.korban import (
Korban,
ProcessedKorban,
OfferingType,
Provenance,
)
from korban_processor.model_interface import (
ModelRequest,
ModelResponse,
call_claude,
)
from korban_processor.semichah import semichah
from korban_processor.shechitah import shechitah
from korban_processor.zerikah import zerikah
from korban_processor.haktarah import haktarah
from korban_processor.router import RoutingResult, route
@dataclass
class ExternalConsultation:
"""The full record of an external-model consultation.
All four fields are preserved so the full chain is auditable:
- request: what was asked and with what system prompt
- raw_response: the unprocessed output from the external model
- processed: the korban after the offering protocol
- routing: where the processed korban was sent
"""
request: ModelRequest
raw_response: ModelResponse
processed: ProcessedKorban
routing: RoutingResult
def consult_external_model(
query: str,
source_query: str,
provenance: Provenance = Provenance.CLAUDE,
offering_type: OfferingType = OfferingType.OLAH,
system_prompt: Optional[str] = None,
caller: Optional[callable] = None,
) -> ExternalConsultation:
"""Submit a query to an external model, then process the response as korban.
Args:
query: The text sent to the external model.
source_query: The upstream question that triggered this consultation
(e.g. the user's courtyard request). Stored on the Korban so the
chain from user → system → external → processed is auditable.
provenance: Which external model. Claude is the default.
offering_type: How to route the processed output. OLAH means the
output is fully consumed into the Talmudic Engine.
system_prompt: Optional system prompt for the external model.
caller: Injectable model caller for testing. Defaults to call_claude.
Must accept a ModelRequest and return a ModelResponse.
Returns:
ExternalConsultation with request, raw response, processed korban, and
routing result.
Note: This function is the ONLY legitimate entry point for external model
consultation. No internal component should call call_claude() directly.
Everything external passes through the offering protocol.
"""
if provenance is not Provenance.CLAUDE and caller is None:
# The current model_interface only implements Claude. Other providers
# must supply an explicit caller — we refuse to silently proxy.
raise NotImplementedError(
f"provenance={provenance.value} requires an explicit caller; "
f"only Claude is wired in model_interface.call_claude"
)
call = caller or call_claude
request = ModelRequest(
query=query,
system_prompt=system_prompt,
)
raw_response = call(request)
# The raw response is now an external output. From here on, it is korban
# — raw material to be processed. It is NOT Torah. It is NOT authoritative.
# The offering protocol consumes the external form and extracts whatever
# Torah-adjacent content might be inside.
korban = semichah(
content=raw_response.content,
source=provenance,
query=source_query,
offering_type=offering_type,
)
terminated = shechitah(korban)
vital, discarded = zerikah(terminated, korban)
propositions = haktarah(vital)
processed = ProcessedKorban(
vital_content=vital,
propositions=propositions,
provenance=provenance,
offering_type=offering_type,
source_query=source_query,
discarded=discarded,
raw_korban=korban,
)
routing = route(processed, offering_type)
return ExternalConsultation(
request=request,
raw_response=raw_response,
processed=processed,
routing=routing,
)
@@ -0,0 +1,185 @@
"""
Tests for external_offering — the orchestrated outbound consultation flow.
Architecture §4 Step 22.
These tests use a mock model caller so no real API requests are made.
A separate live integration test (skipped by default) can run against
Anthropic's API when ANTHROPIC_API_KEY is set and the SHORASHIM_LIVE_API
env var is true.
"""
import os
import pytest
from korban_processor.external_offering import (
ExternalConsultation,
consult_external_model,
)
from korban_processor.korban import OfferingType, Provenance
from korban_processor.model_interface import ModelRequest, ModelResponse
def _fake_caller(response_text: str):
"""Build a mock caller that returns a fixed response."""
def caller(request: ModelRequest) -> ModelResponse:
return ModelResponse(
content=response_text,
model="mock-claude",
stop_reason="end_turn",
input_tokens=10,
output_tokens=20,
)
return caller
# ============================================================
# Basic flow
# ============================================================
def test_consult_external_model_returns_full_consultation():
"""A consultation captures request, raw response, processed korban,
and routing — the full chain is auditable."""
fake_response = (
"Bereishit 1:1 begins with the root bara. The shoresh ב-ר-א means "
"to create from nothing. This is foundational."
)
result = consult_external_model(
query="Explain bereishit 1:1",
source_query="user_courtyard_request_42",
caller=_fake_caller(fake_response),
)
assert isinstance(result, ExternalConsultation)
assert result.request.query == "Explain bereishit 1:1"
assert result.raw_response.content == fake_response
assert result.raw_response.model == "mock-claude"
assert result.processed.provenance is Provenance.CLAUDE
assert result.routing.destination == "talmudic_engine" # default OLAH
def test_raw_korban_is_preserved_on_processed_output():
"""Eish zarah Form 3 (gilui arayot — severed coupling): the raw
korban must be preserved on the processed output. The floor between
input and output is never severed."""
result = consult_external_model(
query="any",
source_query="trigger_query",
caller=_fake_caller(
"The root k-d-sh appears in Vayikra. Holiness is separation."
),
)
raw = result.processed.raw_korban
assert raw is not None, (
"raw_korban must be preserved on ProcessedKorban — "
"severing input from output is gilui arayot"
)
assert raw.source_query == "trigger_query"
assert raw.provenance is Provenance.CLAUDE
def test_provenance_is_recorded_on_korban():
"""Eish zarah Form 1 (avodah zarah — external as Torah): provenance
is never concealed. Every external response is tagged with its source."""
result = consult_external_model(
query="any",
source_query="any",
provenance=Provenance.CLAUDE,
caller=_fake_caller("test response with bereishit reference"),
)
assert result.processed.provenance is Provenance.CLAUDE
assert result.processed.raw_korban.provenance is Provenance.CLAUDE
# ============================================================
# Offering type → routing destination
# ============================================================
@pytest.mark.parametrize("offering_type,destination", [
(OfferingType.OLAH, "talmudic_engine"),
(OfferingType.SHELAMIM, "talmudic_engine"),
(OfferingType.MINCHAH, "talmudic_engine"),
(OfferingType.CHATAT, "error_correction"),
])
def test_offering_type_drives_routing(offering_type, destination):
"""Each offering type routes to its specified destination."""
result = consult_external_model(
query="any",
source_query="any",
offering_type=offering_type,
caller=_fake_caller(
"The shoresh r-tz-h means will. Vayikra 1:3 uses lirtzono — "
"for his acceptance."
),
)
assert result.routing.destination == destination
# ============================================================
# Refusal to silently proxy unknown providers
# ============================================================
def test_unknown_provenance_without_caller_raises():
"""We don't silently route MISTRAL/GPT/etc through Claude. A caller
must be supplied explicitly. This prevents accidental misrouting and
keeps provenance honest."""
with pytest.raises(NotImplementedError) as excinfo:
consult_external_model(
query="any",
source_query="any",
provenance=Provenance.GPT,
)
assert "GPT" in str(excinfo.value).upper() or "gpt" in str(excinfo.value)
def test_explicit_caller_overrides_provider_check():
"""If the caller supplies a callable for a non-Claude provider, the flow
proceeds — they took responsibility for the wiring."""
result = consult_external_model(
query="any",
source_query="any",
provenance=Provenance.GPT,
caller=_fake_caller("test response with shoresh sh-l-m"),
)
assert result.processed.provenance is Provenance.GPT
# ============================================================
# System prompt is forwarded
# ============================================================
def test_system_prompt_is_forwarded():
"""A system prompt sent in becomes part of the request record."""
result = consult_external_model(
query="any",
source_query="any",
system_prompt="You are a Torah scholar. Cite verse references.",
caller=_fake_caller("Bereishit 1:1 is the opening verse."),
)
assert result.request.system_prompt == "You are a Torah scholar. Cite verse references."
# ============================================================
# Live API integration test — opt-in only
# ============================================================
@pytest.mark.skipif(
not os.environ.get("SHORASHIM_LIVE_API"),
reason="Set SHORASHIM_LIVE_API=1 to run a real API call against Anthropic",
)
def test_live_anthropic_call():
"""Real API call. Costs money. Run only when explicitly enabled."""
if not os.environ.get("ANTHROPIC_API_KEY"):
pytest.skip("ANTHROPIC_API_KEY not set")
result = consult_external_model(
query="What is the shoresh of bereishit?",
source_query="live_integration_test",
offering_type=OfferingType.OLAH,
)
assert result.raw_response.content
assert result.processed.raw_korban is not None
assert result.processed.provenance is Provenance.CLAUDE
assert result.raw_response.model.startswith("claude")