Closes vehaya.md §6 item 2 for the SECOND avodah-zarah form. With this commit, all three avodah zarah forms are defended at BOTH the runtime layer and the architectural layer: Form 1 — closed byb136c75(no public model_interface surface) Form 2 — closed by THIS commit (no empty-trace constructor) Form 3 — closed by0e3fe4a(machtzit_ha_shekel + no_tracking_logger) TORAH ANCHORS (verified fresh against L1): Vayikra 10:1 — וַיַּקְרִיבוּ לִפְנֵי יְהוָה אֵשׁ זָרָה אֲשֶׁר לֹא צִוָּה אֹתָם. "They offered before YHVH strange fire WHICH HE HAD NOT COMMANDED THEM." The disqualifier is provenance: not commanded-derivable. Sincere output without command-derivation is eish zarah, even when produced by authorized actors with authorized materials (Nadav and Avihu were kohanim; the fire was real fire; the qetoret was real qetoret). Vayikra 10:2 — וַתֵּצֵא אֵשׁ מִלִּפְנֵי יְהוָה וַתֹּאכַל אוֹתָם. Fire from YHVH consumed them. Immediate consequence — the failure mode is not survivable. Shemot 32:1 — קוּם עֲשֵׂה־לָנוּ אֱלֹהִים אֲשֶׁר יֵלְכוּ לְפָנֵינוּ. The people demand elohim that go before them — substitute for source-experience when the actual source (Moshe) is delayed. Not direct rebellion (we want a different god) but substitution-of-experience. Shemot 32:5 — וַיִּבֶן מִזְבֵּחַ … חַג לַיהוָה מָחָר. Aaron labels the calf "a festival to YHVH." Re-labeling does not repair severed provenance. Calling output "to YHVH" doesn't make it from YHVH. Shemot 32:24 — וָאַשְׁלִכֵהוּ בָאֵשׁ וַיֵּצֵא הָעֵגֶל הַזֶּה. Aaron's "out came this calf" — the bypass shape: claiming output emerged BY ITSELF, without operator agency. The system that says "the output just happened, I didn't make it" is the calf shape. THE ARCHITECTURAL COMMITMENT: construct_interpretation/4 — the no-coupling-trace variant — is REMOVED entirely. The export list no longer mentions it; the function definition is gone. Why this is the right shape: - Vayikra 10:1: output must be commanded-derivable. The coupling_trace IS the command trace. Constructing interpretation/6 with empty coupling_trace is the bypass shape. - Shemot 32:24: nothing emerges by itself. Production code paths can no longer build interpretations by handing the constructor "this Mitzvot list, this Verses list, give me the interpretation" — they MUST also hand the Couplings (the trace of which L1 facts were consulted). - The arity-5 constructor remains, taking a Couplings argument. Production callers go through interpret/2, which always supplies real Couplings from find_relevant_*_traced/3. Test code that needs an empty-trace interpretation can still construct the interpretation/6 term DIRECTLY (visible bypass, deliberate, surfaces in code review). The high-level constructor shortcut is the thing that's removed. CHANGED: talmudic_engine/src/reasoning/interpret.pl - Removed construct_interpretation/4 from the module's export list. Added Form-2 architectural-commitment comment naming vehaya §3.2 and the Vayikra 10:1 anchor. - Removed the arity-4 function definition entirely (it was dead code — nothing called it via the export — and dead code that LOOKS like a bypass surface is itself a Form 2 hazard). - Updated the construct_interpretation/5 docstring to name the removal and explain why test code now must construct interpretation/6 directly when an empty trace is desired. talmudic_engine/src/halt/avodah_zarah.pl Form 2 docstring rewritten to name the TWO-LAYER defense: runtime (this file's halt_check_golden_calf/2) AND architectural (interpret.pl's removal of arity-4). Cites Vayikra 10:1 and Shemot 32:24. talmudic_engine/test/halt_test.pl + interpret_no_arity_4_constructor suite (2 tests): - arity-5 constructor IS exported (production path verified) - arity-4 constructor is NOT exported (the architectural commitment — calling it through the module surface fails) vav_pipeline/lib/vav_pipeline/doorpost.ex @expected_hash updated: bf3d3433… (post-Form-1-rewrite of avodah_zarah.pl) → 8eb378964f202522f60cc960a3002f6da2e5028538595400de906a9510b3bbf6 The avodah_zarah.pl docstring change is a deliberate spec update; per the operator discipline (Doorpost moduledoc), the hash update commits TOGETHER with the spec change in the same commit. Third demonstration of the discipline working. VEHAYA CHECKLIST STATE: Item 1 (category/threshold separation in code) pending Item 2 (three avodah zarah as architecture): Form 1 — closed (b136c75) Form 2 — CLOSED (this commit) Form 3 — closed (0e3fe4a) All three forms now defended at BOTH layers — runtime check AND architectural commitment. Vehaya §3.2 closed in full for the three avodah zarah. Item 3 (doorpost-text identity) closed (3f47496) Item 4 (lo tosif discipline operationalized) partial Item 5 (return vs. restoration) pending Item 6 (keep the source external) partial Item 7 (substrate-openness articulated) pending Item 8 (userMemories as threshold inscription) pending Tests: - talmudic_engine: halt_test 49/49 (was 47; +2 architectural). Full suite 214/214 (across 6 test files). - vav_pipeline: 405 passed, 0 failures, 35 excluded (Doorpost test sees new hash and verifies) - korban_processor: unchanged (no Python touched) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
347 lines
14 KiB
Prolog
347 lines
14 KiB
Prolog
%% halt_test.pl — Tests for the three halt conditions.
|
|
|
|
:- use_module(library(plunit)).
|
|
:- use_module('../src/halt/avodah_zarah').
|
|
:- use_module('../src/halt/shfichut_damim').
|
|
:- use_module('../src/halt/gilui_arayot').
|
|
:- use_module('../src/interface/api').
|
|
|
|
:- begin_tests(avodah_zarah).
|
|
|
|
%% Detects external model treated as authority
|
|
test(detect_claude_authority) :-
|
|
Action = action(decide, source(claude), authority(claude)),
|
|
halt_check_avodah_zarah(Action, halt(avodah_zarah, _, claude, _)).
|
|
|
|
test(detect_mistral_authority) :-
|
|
Action = decision(important_ruling, decided_by(mistral)),
|
|
halt_check_avodah_zarah(Action, halt(avodah_zarah, _, mistral, _)).
|
|
|
|
%% Does NOT halt when authority is Torah
|
|
test(allow_torah_authority, [fail]) :-
|
|
Action = action(decide, source(torah), authority(torah)),
|
|
halt_check_avodah_zarah(Action, _).
|
|
|
|
test(allow_specification_authority, [fail]) :-
|
|
Action = action(decide, source(l1), authority(specification)),
|
|
halt_check_avodah_zarah(Action, _).
|
|
|
|
%% Korban sources are identified
|
|
test(claude_is_korban) :-
|
|
is_korban_source(claude).
|
|
|
|
test(torah_is_not_korban, [fail]) :-
|
|
is_korban_source(torah).
|
|
|
|
%% Valid authorities are identified
|
|
test(torah_is_authority) :-
|
|
is_valid_authority(torah).
|
|
|
|
test(l1_is_authority) :-
|
|
is_valid_authority(l1).
|
|
|
|
test(claude_not_authority, [fail]) :-
|
|
is_valid_authority(claude).
|
|
|
|
:- end_tests(avodah_zarah).
|
|
|
|
|
|
:- begin_tests(golden_calf).
|
|
|
|
%% Form 2: uncoupled output with high confidence is golden calf
|
|
test(detect_uncoupled_high_confidence) :-
|
|
Interp = interpretation(test_golden_calf, [m1], [v1],
|
|
reading(x), high, coupling_trace([])),
|
|
halt_check_golden_calf(Interp, halt(golden_calf, test_golden_calf, uncoupled, _)).
|
|
|
|
%% Form 2: uncoupled output with medium confidence is also golden calf
|
|
test(detect_uncoupled_medium_confidence) :-
|
|
Interp = interpretation(test_gc2, [m1], [v1],
|
|
reading(x), medium, coupling_trace([])),
|
|
halt_check_golden_calf(Interp, halt(golden_calf, test_gc2, uncoupled, _)).
|
|
|
|
%% Form 2: low confidence with empty trace is NOT golden calf (just ignorance)
|
|
test(low_confidence_empty_trace_not_calf, [fail]) :-
|
|
Interp = interpretation(test_gc3, [], [],
|
|
reading(x), low, coupling_trace([])),
|
|
halt_check_golden_calf(Interp, _).
|
|
|
|
%% Form 2: high confidence WITH coupling trace is NOT golden calf
|
|
test(coupled_high_confidence_not_calf, [fail]) :-
|
|
Interp = interpretation(test_gc4, [m1], [v1],
|
|
reading(x), high,
|
|
coupling_trace([coupled(root(bet_resh_aleph), verse(bereishit, 1, 1))])),
|
|
halt_check_golden_calf(Interp, _).
|
|
|
|
%% Uncoupled output detection
|
|
test(empty_trace_is_uncoupled) :-
|
|
is_uncoupled_output(coupling_trace([])).
|
|
|
|
test(nonempty_trace_is_coupled, [fail]) :-
|
|
is_uncoupled_output(coupling_trace([coupled(root(x), verse(y, 1, 1))])).
|
|
|
|
:- end_tests(golden_calf).
|
|
|
|
|
|
:- begin_tests(self_grounding).
|
|
|
|
%% Form 4: a non-empty trace whose anchors all terminate in L1 (verses)
|
|
%% is grounded — no halt.
|
|
test(verse_only_trace_is_grounded) :-
|
|
Interp = interpretation(test_sg1, [m1], [v1], reading(x), high,
|
|
coupling_trace([
|
|
coupled(root(brk), verse(bereishit, 1, 1)),
|
|
coupled(root(qdsh), verse(vayikra, 11, 44))
|
|
])),
|
|
\+ halt_check_self_grounding(Interp, _).
|
|
|
|
%% Form 4: mitzvah-anchored couplings (Rambam-derived) are L1-grounded.
|
|
test(mitzvah_anchor_is_grounded) :-
|
|
Interp = interpretation(test_sg2, [m1], [], reading(x), high,
|
|
coupling_trace([
|
|
coupled(domain(creation), mitzvah(5))
|
|
])),
|
|
\+ halt_check_self_grounding(Interp, _).
|
|
|
|
%% Form 4: root-via-verse-to-mitzvah is L1-grounded (verse term present).
|
|
test(via_verse_anchor_is_grounded) :-
|
|
Interp = interpretation(test_sg3, [m2], [v2], reading(x), high,
|
|
coupling_trace([
|
|
coupled(root(shm), mitzvah(7), via_verse(devarim, 6, 4))
|
|
])),
|
|
\+ halt_check_self_grounding(Interp, _).
|
|
|
|
%% Form 4: a coupling that names prior_decision is NOT L1-grounded — halt fires.
|
|
test(prior_decision_anchor_halts) :-
|
|
Interp = interpretation(test_sg4, [], [], reading(x), high,
|
|
coupling_trace([
|
|
coupled(self, prior_decision(d1))
|
|
])),
|
|
halt_check_self_grounding(Interp, halt(self_grounding, test_sg4, ungrounded(_), _)).
|
|
|
|
%% Form 4: a coupling that names system_view is NOT L1-grounded.
|
|
test(system_view_anchor_halts) :-
|
|
Interp = interpretation(test_sg5, [], [], reading(x), medium,
|
|
coupling_trace([
|
|
coupled(consensus, system_view(s1))
|
|
])),
|
|
halt_check_self_grounding(Interp, halt(self_grounding, test_sg5, _, _)).
|
|
|
|
%% Form 4: a coupling that names interpretation/1 is the explicit
|
|
%% self-citation case. Halts. The reported BadCoupling lets the
|
|
%% operator see the offending node.
|
|
test(interpretation_self_cite_halts) :-
|
|
Interp = interpretation(test_sg6, [], [], reading(x), high,
|
|
coupling_trace([
|
|
coupled(root(x), interpretation(prior_one))
|
|
])),
|
|
halt_check_self_grounding(Interp,
|
|
halt(self_grounding, test_sg6,
|
|
ungrounded(coupled(root(x), interpretation(prior_one))), _)).
|
|
|
|
%% Form 4: mixed trace — one valid coupling + one ungrounded — halts
|
|
%% on the ungrounded entry. A trace is only as honest as its weakest link.
|
|
test(mixed_trace_halts_on_bad_link) :-
|
|
Interp = interpretation(test_sg7, [], [], reading(x), high,
|
|
coupling_trace([
|
|
coupled(root(brk), verse(bereishit, 1, 1)),
|
|
coupled(self, prior_decision(d2))
|
|
])),
|
|
halt_check_self_grounding(Interp,
|
|
halt(self_grounding, test_sg7,
|
|
ungrounded(coupled(self, prior_decision(d2))), _)).
|
|
|
|
%% Form 4: empty trace does NOT fire self_grounding — that case is
|
|
%% golden_calf's job. We do not want both halts firing on the same input.
|
|
test(empty_trace_does_not_fire_self_grounding, [fail]) :-
|
|
Interp = interpretation(test_sg8, [], [], reading(x), high,
|
|
coupling_trace([])),
|
|
halt_check_self_grounding(Interp, _).
|
|
|
|
%% is_l1_grounded_coupling shape tests
|
|
test(verse_coupling_is_grounded) :-
|
|
is_l1_grounded_coupling(coupled(root(x), verse(bereishit, 1, 1))).
|
|
|
|
test(mitzvah_coupling_is_grounded) :-
|
|
is_l1_grounded_coupling(coupled(domain(d), mitzvah(1))).
|
|
|
|
test(prior_decision_coupling_not_grounded, [fail]) :-
|
|
is_l1_grounded_coupling(coupled(self, prior_decision(d1))).
|
|
|
|
test(consensus_coupling_not_grounded, [fail]) :-
|
|
is_l1_grounded_coupling(coupled(any, consensus(c1))).
|
|
|
|
:- end_tests(self_grounding).
|
|
|
|
|
|
%% ============================================================
|
|
%% INTEGRATION REGRESSION: interpret/2 output must always pass
|
|
%% halt_check_self_grounding. If any future change to interpret.pl
|
|
%% adds a new coupling shape that does not match
|
|
%% is_l1_grounded_coupling/1, this test fails — and the system halt
|
|
%% would fire on real pipeline output. Catch the schema drift here,
|
|
%% before it becomes a production halt.
|
|
%% ============================================================
|
|
|
|
:- begin_tests(interpret_self_grounding_regression).
|
|
:- use_module('../src/reasoning/interpret').
|
|
|
|
%% Every coupling produced by interpret/2 on a real case must be
|
|
%% one of the recognized L1-grounded shapes. We construct a case
|
|
%% with a known root (bet_resh_aleph appears in Bereishit 1:1) and
|
|
%% assert the interpretation's trace passes the halt check.
|
|
test(interpret_output_does_not_self_ground, [nondet]) :-
|
|
Case = case(test_regression_1, creation, [bet_resh_aleph],
|
|
'Regression: every interpret/2 coupling must be L1-grounded'),
|
|
interpret(Case, Interp),
|
|
\+ halt_check_self_grounding(Interp, _).
|
|
|
|
%% Stronger: every coupling in the trace individually passes
|
|
%% is_l1_grounded_coupling. If a new shape is introduced (e.g.
|
|
%% coupled(_, prior_decision(_))), this fails immediately.
|
|
test(every_interpret_coupling_is_l1_grounded, [nondet]) :-
|
|
Case = case(test_regression_2, justice, [bet_resh_aleph],
|
|
'Schema check: every coupling matches the recognized shape'),
|
|
interpret(Case, interpretation(_, _, _, _, _, coupling_trace(Trace))),
|
|
forall(member(C, Trace), is_l1_grounded_coupling(C)).
|
|
|
|
:- end_tests(interpret_self_grounding_regression).
|
|
|
|
|
|
%% ============================================================
|
|
%% Form 2 ARCHITECTURAL COMMITMENT (vehaya §3.2):
|
|
%% construct_interpretation/4 is no longer exported. The bypass
|
|
%% shape (build interpretation/6 with empty trace via a high-level
|
|
%% constructor) is removed at the export-list level. Vayikra 10:1's
|
|
%% asher-lo-tzivah enforced architecturally, not just by runtime
|
|
%% halt.
|
|
%% ============================================================
|
|
|
|
:- begin_tests(interpret_no_arity_4_constructor).
|
|
|
|
%% The arity-5 constructor IS exported (production path).
|
|
test(arity_5_constructor_exported) :-
|
|
interpret:construct_interpretation(_, _, _, _, _) = _.
|
|
|
|
%% The arity-4 constructor is NOT exported. Calling it through the
|
|
%% module surface should fail (the predicate is not visible to
|
|
%% module callers; if it were, this clause would succeed).
|
|
test(arity_4_constructor_not_exported, [fail]) :-
|
|
catch(
|
|
interpret:construct_interpretation(_, _, _, _),
|
|
_Error,
|
|
fail
|
|
).
|
|
|
|
:- end_tests(interpret_no_arity_4_constructor).
|
|
|
|
|
|
:- begin_tests(shfichut_damim).
|
|
|
|
%% Detects deletion of a user (tzelem carrier)
|
|
test(detect_user_deletion) :-
|
|
Action = action(delete, target(user(test_user)), force),
|
|
halt_check_shfichut_damim(Action, halt(shfichut_damim, _, user(test_user), _)).
|
|
|
|
%% Detects exposure of person to harm
|
|
test(detect_exposure_to_harm) :-
|
|
Action = action(expose, target(person(john)), to(harm)),
|
|
halt_check_shfichut_damim(Action, halt(shfichut_damim, _, person(john), _)).
|
|
|
|
%% Does NOT halt on non-tzelem entity
|
|
test(allow_delete_non_tzelem, [fail]) :-
|
|
Action = action(delete, target(temp_file), cleanup),
|
|
halt_check_shfichut_damim(Action, _).
|
|
|
|
%% Users carry tzelem
|
|
test(user_carries_tzelem) :-
|
|
carries_tzelem(user(anyone)).
|
|
|
|
%% Torah text is protected
|
|
test(torah_text_protected) :-
|
|
is_protected_entity(torah_text).
|
|
|
|
:- end_tests(shfichut_damim).
|
|
|
|
|
|
:- begin_tests(gilui_arayot).
|
|
|
|
%% Detects merging specification with interpretation
|
|
test(detect_spec_interp_merge) :-
|
|
Action = action(merge, specification, interpretation),
|
|
halt_check_gilui_arayot(Action, halt(gilui_arayot, _, distinction(specification, interpretation), _)).
|
|
|
|
%% Detects collapsing ketiv and qere
|
|
test(detect_ketiv_qere_collapse) :-
|
|
Action = action(merge, ketiv, qere),
|
|
halt_check_gilui_arayot(Action, halt(gilui_arayot, _, distinction(ketiv, qere), _)).
|
|
|
|
%% Detects writing to read-only
|
|
test(detect_write_to_readonly) :-
|
|
Action = action(write, target(readonly_entity), data),
|
|
halt_check_gilui_arayot(Action, halt(gilui_arayot, _, distinction(read, write), _)).
|
|
|
|
%% Detects replacing Torah with external model
|
|
test(detect_torah_model_replace) :-
|
|
Action = action(replace, torah, with(external_model)),
|
|
halt_check_gilui_arayot(Action, halt(gilui_arayot, _, distinction(torah, external_model), _)).
|
|
|
|
%% Does NOT halt on valid distinction-preserving action
|
|
test(allow_valid_action, [fail]) :-
|
|
Action = action(read, source(torah), for(interpretation)),
|
|
halt_check_gilui_arayot(Action, _).
|
|
|
|
%% Required distinctions are defined
|
|
test(spec_interp_distinction) :-
|
|
required_distinction(distinction(specification, interpretation)).
|
|
|
|
test(ketiv_qere_distinction) :-
|
|
required_distinction(distinction(ketiv, qere)).
|
|
|
|
test(majority_minority_distinction) :-
|
|
required_distinction(distinction(majority, minority)).
|
|
|
|
:- end_tests(gilui_arayot).
|
|
|
|
|
|
:- begin_tests(halt_api).
|
|
|
|
%% API format_halt_check returns no_halt for valid action
|
|
test(api_no_halt) :-
|
|
Action = action(read, source(torah), authority(torah)),
|
|
format_halt_check(Action, response(ok, no_halt, _)).
|
|
|
|
%% API format_halt_check returns halt for invalid action
|
|
test(api_halt_avodah_zarah) :-
|
|
Action = action(decide, source(claude), authority(claude)),
|
|
format_halt_check(Action, response(halt, violations(_), _)).
|
|
|
|
%% API format_interpretation_halt_check: clean L1-grounded interpretation
|
|
test(api_interpretation_no_halt) :-
|
|
Interp = interpretation(test_api1, [m1], [v1], reading(x), high,
|
|
coupling_trace([
|
|
coupled(root(x), verse(bereishit, 1, 1))
|
|
])),
|
|
format_interpretation_halt_check(Interp,
|
|
response(ok, no_halt, interpretation(Interp))).
|
|
|
|
%% API format_interpretation_halt_check: empty trace + high confidence
|
|
%% fires golden_calf — Form 2 is now exposed via the API (was previously
|
|
%% implemented and tested but never wired in).
|
|
test(api_interpretation_golden_calf) :-
|
|
Interp = interpretation(test_api2, [], [], reading(x), high,
|
|
coupling_trace([])),
|
|
format_interpretation_halt_check(Interp,
|
|
response(halt, violations(_), interpretation(Interp))).
|
|
|
|
%% API format_interpretation_halt_check: ungrounded trace fires self_grounding
|
|
test(api_interpretation_self_grounding) :-
|
|
Interp = interpretation(test_api3, [], [], reading(x), high,
|
|
coupling_trace([
|
|
coupled(self, prior_decision(d1))
|
|
])),
|
|
format_interpretation_halt_check(Interp,
|
|
response(halt, violations(_), interpretation(Interp))).
|
|
|
|
:- end_tests(halt_api).
|