Files
bikoret/korban_processor/test/test_router.py
Scott KarpandClaude Sonnet 4.6 33f8b03583 Step 13: Korban Processor complete — 7 modules, 181 tests, all passing
- semichah: provenance tagging (6 sources, rejects empty/anonymous)
- shechitah: strip preamble, self-reference, hedging, markdown, sycophancy
- zerikah: extract Torah refs, Hebrew text, root analysis, insight markers
- haktarah: transform prose to Prolog propositions (verse/3, root/2, claim/2, connection/3)
- router: stateless routing — removed module-level _routing_log (kiyyor principle)
- model_interface: outbound Claude API via httpx (Construction §3.4, HTTPS only)
- hebrew_parser: nikkud/teamim stripping, consonant extraction, gematria, root identification

All 5 BUILD_KORBAN.md verification tests pass.
AGENTS.md: added structure_not_content.md to spec table, added Principle 6.
build_log.md: recorded session, broken tablets #18-20, design decisions.

Ka'asher tzivah Hashem et Moshe.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 09:37:25 -04:00

113 lines
4.4 KiB
Python

"""
Tests for router — output routing based on offering type.
Architecture §3.5: Route to appropriate component based on offering type.
Kiyyor principle (structure_not_content.md):
Stateless — no module-level routing log, no state between calls.
"""
from korban_processor.korban import ProcessedKorban, OfferingType, Provenance
from korban_processor.router import route, RoutingResult
_DEFAULT_PROPOSITIONS = ["verse(bereishit, 1, 1)", 'root("ראש", hebrew)']
def make_processed(
offering_type: OfferingType,
propositions: list[str] | None = None,
) -> ProcessedKorban:
props = _DEFAULT_PROPOSITIONS if propositions is None else propositions
return ProcessedKorban(
vital_content="The root ר-א-ש means beginning. Bereishit 1:1.",
propositions=props,
provenance=Provenance.CLAUDE,
offering_type=offering_type,
discarded=[],
source_query="test query",
)
class TestRoutingResults:
def test_olah_routes_to_talmudic_engine(self):
processed = make_processed(OfferingType.OLAH)
result = route(processed, OfferingType.OLAH)
assert result.destination == "talmudic_engine"
assert result.returned_to_source is False
def test_shelamim_routes_to_talmudic_engine_with_return(self):
processed = make_processed(OfferingType.SHELAMIM)
result = route(processed, OfferingType.SHELAMIM)
assert result.destination == "talmudic_engine"
assert result.returned_to_source is True
def test_chatat_routes_to_error_correction(self):
processed = make_processed(OfferingType.CHATAT)
result = route(processed, OfferingType.CHATAT)
assert result.destination == "error_correction"
assert result.returned_to_source is False
def test_minchah_routes_to_talmudic_engine(self):
processed = make_processed(OfferingType.MINCHAH)
result = route(processed, OfferingType.MINCHAH)
assert result.destination == "talmudic_engine"
assert result.returned_to_source is False
def test_returns_routing_result_instance(self):
processed = make_processed(OfferingType.OLAH)
result = route(processed, OfferingType.OLAH)
assert isinstance(result, RoutingResult)
def test_propositions_sent_count_is_correct(self):
propositions = ["verse(bereishit, 1, 1)", 'root("ראש", hebrew)', 'claim("a", "b")']
processed = make_processed(OfferingType.OLAH, propositions)
result = route(processed, OfferingType.OLAH)
assert result.propositions_sent == 3
def test_zero_propositions(self):
processed = make_processed(OfferingType.OLAH, [])
result = route(processed, OfferingType.OLAH)
assert result.propositions_sent == 0
class TestRouterStateless:
"""
BUILD_KORBAN verification §4:
Kiyyor principle — no state leaks between calls.
No module-level routing log.
"""
def test_no_module_level_routing_log(self):
"""router.py must not have a module-level _routing_log accumulator."""
import korban_processor.router as router_module
assert not hasattr(router_module, "_routing_log"), (
"router.py has module-level _routing_log — violates kiyyor principle"
)
def test_successive_calls_independent(self):
"""Second call returns correct result regardless of first call."""
p1 = make_processed(OfferingType.OLAH, ["prop1"])
p2 = make_processed(OfferingType.CHATAT, ["prop2", "prop3"])
result1 = route(p1, OfferingType.OLAH)
result2 = route(p2, OfferingType.CHATAT)
assert result1.destination == "talmudic_engine"
assert result1.propositions_sent == 1
assert result2.destination == "error_correction"
assert result2.propositions_sent == 2
def test_same_input_same_output(self):
"""Identical input always produces identical output — no accumulated state."""
processed = make_processed(OfferingType.SHELAMIM, ["verse(bereishit, 1, 1)"])
result1 = route(processed, OfferingType.SHELAMIM)
# Run an unrelated routing between
route(make_processed(OfferingType.CHATAT, []), OfferingType.CHATAT)
result2 = route(processed, OfferingType.SHELAMIM)
assert result1.destination == result2.destination
assert result1.propositions_sent == result2.propositions_sent
assert result1.returned_to_source == result2.returned_to_source