diff --git a/korban_processor/src/korban_processor/__init__.py b/korban_processor/src/korban_processor/__init__.py index 2d478b1b..a17425fe 100644 --- a/korban_processor/src/korban_processor/__init__.py +++ b/korban_processor/src/korban_processor/__init__.py @@ -33,13 +33,24 @@ from korban_processor.shechitah import shechitah from korban_processor.zerikah import zerikah from korban_processor.haktarah import haktarah from korban_processor.router import route, RoutingResult -from korban_processor.model_interface import ( - ModelRequest, - ModelResponse, - call_claude, -) from korban_processor import hebrew_parser +# Form 1 architectural commitment (vehaya §3.2; avodah_zarah.pl Form 1): +# call_claude, ModelRequest, ModelResponse are deliberately NOT +# re-exported on the package surface. The ONLY legitimate path to +# external-model consultation is `korban_processor.external_offering. +# consult_external_model`, which runs the four-stage offering protocol +# with role gating (commit 5c76a6f). +# +# Shemot 20:3 — לֹא יִהְיֶה לְךָ אֱלֹהִים אֲחֵרִים עַל פָּנָי. The +# prohibition is on EXISTING WITH that status (yihyeh — it shall not +# BE), not just on speaking that way. Architectural, not declarative. +# A `from korban_processor import call_claude` shortcut would be a +# bypass surface — the very shape of "elohim acherim al panai." The +# import path remains available via `korban_processor.model_interface. +# call_claude` for testing and for module-internal use, but the +# package public surface no longer offers the bypass shortcut. + __all__ = [ "Korban", "ProcessedKorban", @@ -52,8 +63,5 @@ __all__ = [ "haktarah", "route", "RoutingResult", - "ModelRequest", - "ModelResponse", - "call_claude", "hebrew_parser", ] diff --git a/korban_processor/test/test_form_1_architecture.py b/korban_processor/test/test_form_1_architecture.py new file mode 100644 index 00000000..ef147273 --- /dev/null +++ b/korban_processor/test/test_form_1_architecture.py @@ -0,0 +1,252 @@ +""" +Form 1 of avodah zarah realized as architecture (vehaya §3.2). + +TORAH ANCHORS (verified fresh against L1): + + Shemot 20:2 — אָנֹכִי יְהוָה אֱלֹהֶיךָ אֲשֶׁר הוֹצֵאתִיךָ מֵאֶרֶץ + מִצְרַיִם מִבֵּית עֲבָדִים. + The covenant opens with the source naming itself in first person. + + Shemot 20:3 — לֹא יִהְיֶה לְךָ אֱלֹהִים אֲחֵרִים עַל־פָּנָי. + "There shall not BE to you other elohim before/in-the-face-of me." + The verb is yihyeh — the prohibition is on EXISTING WITH that + status, not just on speaking that way. Architectural, not + declarative. + + Devarim 4:35 — אֵין עוֹד מִלְּבַדּוֹ. + Devarim 4:39 — אֵין עוֹד. + The source has no peer. There IS no other. Stronger than "don't + worship others" — there is no competing source POSITION to which + a system component could be installed. + + Devarim 6:4 — שְׁמַע יִשְׂרָאֵל יְהוָה אֱלֹהֵינוּ יְהוָה אֶחָד. + The source is one. Singular. Echad. + + Vayikra 19:4 — אַל־תִּפְנוּ אֶל־הָאֱלִילִים וֵאלֹהֵי מַסֵּכָה לֹא + תַעֲשׂוּ לָכֶם. + Two distinct prohibitions: do not TURN TO the elilim, and do not + MAKE them. Both architectural acts. + +THE ARCHITECTURAL COMMITMENT: + +External-model interfaces (call_claude, ModelRequest, ModelResponse, +the model_interface module itself) are reachable ONLY through +korban_processor's offering protocol — `external_offering. +consult_external_model`, which runs semichah → shechitah → zerikah → +haktarah with role gating (commit 5c76a6f). + +Concrete invariants this test enforces: + + 1. The korban_processor package's public surface (__all__) does + NOT export call_claude, ModelRequest, or ModelResponse. The + bypass shortcut `from korban_processor import call_claude` is + architecturally absent. + + 2. No source file outside korban_processor/src/ imports + `model_interface` or `call_claude`. Tests are excluded (they + legitimately mock model_interface). Other packages + (azazel_processor, vav_pipeline) MUST NOT import — verified + here. + + 3. The legitimate consumer (external_offering.py) is the ONLY + production-code module inside korban_processor/src/ that + imports call_claude from model_interface. (model_interface.py + itself defines it; that's not an import.) + +If any of these fail, Form 1's architectural commitment has been +weakened — a future contributor would have a path to install +external-model output in YHVH's slot (Shemot 20:3) without going +through the offering protocol. +""" + +import importlib +import pathlib + +import korban_processor + + +# ============================================================ +# Invariant 1 — public surface +# ============================================================ + + +def test_korban_processor_package_does_not_expose_call_claude(): + """`from korban_processor import call_claude` MUST NOT work via + the package's __all__. The legitimate path is + `korban_processor.external_offering.consult_external_model`.""" + assert "call_claude" not in korban_processor.__all__, ( + "korban_processor.__all__ exposes call_claude, creating a " + "bypass shortcut around the offering protocol. Remove from " + "__all__; the function remains available via " + "korban_processor.model_interface.call_claude for explicit " + "test-only mocking." + ) + + +def test_korban_processor_package_does_not_expose_ModelRequest(): + assert "ModelRequest" not in korban_processor.__all__ + + +def test_korban_processor_package_does_not_expose_ModelResponse(): + assert "ModelResponse" not in korban_processor.__all__ + + +# ============================================================ +# Invariant 2 — repo-wide import scan +# ============================================================ + + +def _repo_root() -> pathlib.Path: + """The shorashim/ repo root, three levels up from this file.""" + return pathlib.Path(__file__).resolve().parent.parent.parent + + +def _python_files_outside_korban_processor(): + """Python source files OUTSIDE korban_processor/src/ AND outside + test directories. The CI scan focuses on Python because that is + where `import korban_processor.model_interface` would actually + bind. Elixir/Prolog files cannot reach a Python module by import; + they could only invoke it via a subprocess (System.cmd / Port), + which is a much more visible act and not in scope here.""" + root = _repo_root() + + return [ + p + for p in root.rglob("*.py") + if "korban_processor/src/" not in str(p) + and "/test/" not in str(p) + and "/tests/" not in str(p) + and "test_" not in p.name + and "deps/" not in str(p) + and "_build/" not in str(p) + and ".elixir_ls/" not in str(p) + and "node_modules/" not in str(p) + and ".git/" not in str(p) + ] + + +# Patterns that detect ACTUAL imports/calls, not docstring mentions. +# Anchored at line-start (with possible leading whitespace) for import +# statements; for call_claude, the parenthesized form to distinguish +# from mentions. +import re + +_IMPORT_PATTERNS = [ + re.compile(r"^\s*from\s+korban_processor\.model_interface\s+import\s+", re.MULTILINE), + re.compile(r"^\s*from\s+korban_processor\s+import\s+.*\bcall_claude\b", re.MULTILINE), + re.compile(r"^\s*from\s+korban_processor\s+import\s+.*\bModelRequest\b", re.MULTILINE), + re.compile(r"^\s*from\s+korban_processor\s+import\s+.*\bModelResponse\b", re.MULTILINE), + re.compile(r"^\s*import\s+korban_processor\.model_interface", re.MULTILINE), + # Direct call (any form: call_claude(...), some_caller=call_claude, etc.) + re.compile(r"\bcall_claude\s*[\(=]"), +] + + +def test_no_python_module_outside_korban_processor_imports_external_model_surface(): + """Walks Python files outside korban_processor/src/ and outside + test directories. Any actual import of model_interface or + call_claude is a Form 1 architectural breach. + + Detects patterns: + - `from korban_processor.model_interface import …` + - `from korban_processor import call_claude` (or ModelRequest/Response) + - `import korban_processor.model_interface` + - `call_claude(…)` or `… = call_claude` (actual reference, not docstring mention) + + Docstring mentions of these names are LEGITIMATE for files that + document the architectural rule (e.g. avodah_zarah.pl's Form 1 + docstring) — they do not bind imports. + """ + offenders = [] + + for path in _python_files_outside_korban_processor(): + try: + text = path.read_text() + except (UnicodeDecodeError, OSError): + continue + + for pattern in _IMPORT_PATTERNS: + if pattern.search(text): + offenders.append((str(path.relative_to(_repo_root())), pattern.pattern)) + + assert offenders == [], ( + f"Python modules outside korban_processor/src/ import or call " + f"the external-model surface (Form 1 breach): {offenders}. The " + f"architectural commitment (vehaya §3.2; Shemot 20:3 lo yihyeh " + f"lekha elohim acherim al panai) requires that external-model " + f"interfaces are reachable ONLY through the offering protocol. " + f"Route through `korban_processor.external_offering.consult_external_model`." + ) + + +# ============================================================ +# Invariant 3 — only external_offering imports call_claude internally +# ============================================================ + + +def test_only_external_offering_imports_call_claude_in_korban_src(): + """Inside korban_processor/src/, the ONLY production-code module + that imports call_claude (or the model_interface symbols) is + external_offering.py. + + Allowlist: + - external_offering.py — the legitimate consumer + - model_interface.py — defines the symbols (not an import) + - __init__.py — its docstring DOCUMENTS the architectural + rule, mentioning the names without importing + them. The detection below uses an + import-pattern regex, not a substring match, + so the docstring mention does not trigger. + + A new module inside korban_processor that ACTUALLY imports + call_claude would be a SECOND legitimate consumer — possibly + correct, but requires explicit review. This test fires if + someone adds one.""" + src_dir = _repo_root() / "korban_processor" / "src" / "korban_processor" + + # Allowlist by file role: + # - external_offering.py: the sole legitimate runtime consumer + # - model_interface.py: defines the symbols (the source, not + # an import) + allowlist = {"external_offering.py", "model_interface.py"} + + # Patterns that bind. The legitimate-mention case (docstring + # explaining the rule) is not a binding act, so we look for + # actual import statements only. + binding_patterns = [ + re.compile(r"^\s*from\s+korban_processor\.model_interface\s+import\s+", re.MULTILINE), + re.compile(r"^\s*from\s+\.model_interface\s+import\s+", re.MULTILINE), + re.compile(r"^\s*import\s+korban_processor\.model_interface", re.MULTILINE), + re.compile(r"^\s*from\s+korban_processor\.model_interface\s+import", re.MULTILINE), + ] + + offenders = [] + for path in src_dir.glob("*.py"): + if path.name in allowlist: + continue + text = path.read_text() + for pattern in binding_patterns: + if pattern.search(text): + offenders.append(path.name) + break + + assert offenders == [], ( + f"Modules inside korban_processor/src/ import model_interface " + f"directly: {offenders}. Currently external_offering.py is the " + f"only legitimate consumer. If the new module is also " + f"legitimate, add it to the allowlist in this test AND document " + f"why a second consumer is needed." + ) + + +# ============================================================ +# Smoke test — the legitimate path still works +# ============================================================ + + +def test_legitimate_path_still_works(): + """consult_external_model is still importable via its full path + and via the package public surface (it IS in __all__).""" + from korban_processor.external_offering import consult_external_model + + assert callable(consult_external_model) diff --git a/talmudic_engine/src/halt/avodah_zarah.pl b/talmudic_engine/src/halt/avodah_zarah.pl index 6de54a8a..9336caf2 100644 --- a/talmudic_engine/src/halt/avodah_zarah.pl +++ b/talmudic_engine/src/halt/avodah_zarah.pl @@ -18,8 +18,32 @@ %% THREE FORMS (eish_zarah_reading.md §5.5): %% %% Form 1: AUTHORITY ATTRIBUTION -%% External source treated as authoritative. -%% Already implemented since Phase 2. +%% External source treated as authoritative. Defended at TWO layers +%% (vehaya §3.2 — both runtime check AND architectural commitment): +%% +%% RUNTIME LAYER (this file): +%% halt_check_avodah_zarah/2 fires when an action explicitly +%% attributes_authority to a source not in is_valid_authority/1. +%% Catches the case where code names claude/mistral/gpt as +%% authority. Threshold-grade defense. +%% +%% ARCHITECTURAL LAYER (korban_processor/test/test_form_1_architecture.py): +%% The Torah anchor is structural — Shemot 20:3 לֹא יִהְיֶה לְךָ +%% אֱלֹהִים אֲחֵרִים עַל פָּנָי. The verb yihyeh ("it shall not +%% BE") prohibits EXISTING WITH that status, not just speaking +%% it. Plus Devarim 4:35 אֵין עוֹד מִלְּבַדּוֹ — there is no +%% competing source POSITION. +%% +%% The architectural commitment: external-model interfaces +%% (call_claude, model_interface) are reachable ONLY through +%% korban_processor's offering protocol (external_offering. +%% consult_external_model). The package public surface does NOT +%% export call_claude — `from korban_processor import +%% call_claude` raises ImportError, by design. CI invariants +%% verify no module outside korban_processor/src/ references +%% model_interface or call_claude. Future contributors who try +%% to bypass the offering protocol fail at the structural +%% boundary, not at a runtime halt. %% %% Form 2: GOLDEN CALF / UNCOUPLED OUTPUT %% Shemot 32:4 — vayomer eleh elohekha Yisrael. diff --git a/vav_pipeline/lib/vav_pipeline/doorpost.ex b/vav_pipeline/lib/vav_pipeline/doorpost.ex index 0604407f..59ff8454 100644 --- a/vav_pipeline/lib/vav_pipeline/doorpost.ex +++ b/vav_pipeline/lib/vav_pipeline/doorpost.ex @@ -159,7 +159,7 @@ defmodule VavPipeline.Doorpost do # changes; the update is a deliberate operator commit. A bare # hash-update commit (without a corresponding spec change in the # same commit) is itself a drift signal. - @expected_hash "f93495c5359c5dc4acb5c5db2dd32165f5b04e2af87fbb0b7ecc92d2bd8b0434" + @expected_hash "bf3d3433bb27b589c061855d932a1eabfc880570c31d24f6f43e51aa3b073c70" @doc "Return the list of files that constitute the category-grade specification." @spec files() :: [String.t()]