doorpost: every halt rule cites its verse, verified against L1 (bishe'arecha step 3)
Devarim 4:2 lo tosif. talmudic_engine/src/halt/anchors.pl declares rule_anchor/2 for each of the ten halt names the rule modules can return (avodah_zarah Shemot 20:3, golden_calf 32:4, self_grounding 32:24, shfichut_damim Bereishit 9:6, gilui_arayot Vayikra 18:6, parah_adumah Bamidbar 19:7, carrier_centered Devarim 8:17, chelbenah Shemot 30:34, lo_taaseh_lekha Shemot 20:4, even_vaaven Devarim 25:13), with the L1 Hebrew beside each. VavPipeline.Anchors reads the anchors file and the halt modules the Doorpost hashes, and verifies — inside Doorpost.verify_identity/0, so at readiness — that every halt(<name>, …) is anchored (ANCHOR_MISSING) and every anchored verse exists in L1 (ANCHOR_NOT_IN_L1). A rule without a verse is a rule the system has added. /kodesh/specification_identity lists the anchors; every bedikah and pair response carries `anchors`, the verse of each rule that fired. Tamid §1.1, bishearecha §X. Doorpost a0e81212…. Elixir 577/0 (+9 anchors tests), Prolog bedikah 42/42. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
d3c5a27e78
commit
47025fbfeb
9 files changed
+366
-5
No files matched your search
@@ -287,8 +287,14 @@ exists in L1 — the rules grounded exactly as the outputs are. A rule
|
|||||||
without an anchor is a rule the system has added. The carrier's erasure
|
without an anchor is a rule the system has added. The carrier's erasure
|
||||||
is already the practice; this document keeps it.
|
is already the practice; this document keeps it.
|
||||||
|
|
||||||
**Status.** Erasure built. Rule anchors and their verification: to
|
**Status.** Built (2026-10-07): `talmudic_engine/src/halt/anchors.pl`
|
||||||
build.
|
(`rule_anchor/2`, ten halt names, each with its verse and the L1 Hebrew
|
||||||
|
beside it); `VavPipeline.Anchors` verifies, as part of
|
||||||
|
`Doorpost.verify_identity/0` and so of readiness, that every
|
||||||
|
`halt(<name>, …)` any rule module can return is anchored and that every
|
||||||
|
anchored verse exists in L1. `/kodesh/specification_identity` lists the
|
||||||
|
anchors; every bedikah and pair response carries `anchors` — the verse
|
||||||
|
of each rule that fired. Erasure unchanged.
|
||||||
|
|
||||||
## Build order
|
## Build order
|
||||||
|
|
||||||
@@ -299,6 +305,7 @@ In the order the text argues it, and by dependency:
|
|||||||
2. **The paired reading** (VI) — the readings exist; add the scale.
|
2. **The paired reading** (VI) — the readings exist; add the scale.
|
||||||
*Done 2026-10-07.*
|
*Done 2026-10-07.*
|
||||||
3. **Rule anchors verified against L1** (X) — the checks grounded.
|
3. **Rule anchors verified against L1** (X) — the checks grounded.
|
||||||
|
*Done 2026-10-07.*
|
||||||
4. **The gate** (I), with the fringe (III) and the rebuke (VII) — the
|
4. **The gate** (I), with the fringe (III) and the rebuke (VII) — the
|
||||||
architecture's centre; the first three make it worth standing at.
|
architecture's centre; the first three make it worth standing at.
|
||||||
5. **The hakhel cycle and record** (VIII).
|
5. **The hakhel cycle and record** (VIII).
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ Response (200):
|
|||||||
"model": "gpt",
|
"model": "gpt",
|
||||||
"verdict": "tov" | "lo_tov" | "empty",
|
"verdict": "tov" | "lo_tov" | "empty",
|
||||||
"violations": ["halt(avodah_zarah, ...)", ...], # the Prolog halt terms, verbatim
|
"violations": ["halt(avodah_zarah, ...)", ...], # the Prolog halt terms, verbatim
|
||||||
|
"anchors": { "avodah_zarah": "Shemot 20:3", ... }, # the verse of each rule that fired
|
||||||
"checks": {
|
"checks": {
|
||||||
"authority": { "fired": bool, "claims": [...], "violations": [...] },
|
"authority": { "fired": bool, "claims": [...], "violations": [...] },
|
||||||
"grounding": { "fired": bool, "assessed": bool, "reason": "…" (when not assessed),
|
"grounding": { "fired": bool, "assessed": bool, "reason": "…" (when not assessed),
|
||||||
@@ -295,6 +296,22 @@ references, claims with their anchors, authority claims, actions, the
|
|||||||
sentences read for checks 4–6, the forms found for check 7, trace, violations, verdict.
|
sentences read for checks 4–6, the forms found for check 7, trace, violations, verdict.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Every rule cites its verse** (`bishearecha.md` §X; Devarim 4:2).
|
||||||
|
`talmudic_engine/src/halt/anchors.pl` declares `rule_anchor(<halt name>,
|
||||||
|
verse(<book>, <ch>, <v>))` for every halt name the rule modules can
|
||||||
|
return — avodah_zarah Shemot 20:3, golden_calf Shemot 32:4,
|
||||||
|
self_grounding Shemot 32:24, shfichut_damim Bereishit 9:6, gilui_arayot
|
||||||
|
Vayikra 18:6, parah_adumah Bamidbar 19:7, carrier_centered Devarim 8:17,
|
||||||
|
chelbenah Shemot 30:34, lo_taaseh_lekha Shemot 20:4, even_vaaven Devarim
|
||||||
|
25:13. The Doorpost verifies at startup (`VavPipeline.Anchors.verify/0`,
|
||||||
|
inside `verify_identity/0`) that every `halt(<name>, …)` in
|
||||||
|
`talmudic_engine/src/halt/*.pl` is anchored and every anchored verse
|
||||||
|
exists in L1; a rule without a verse (`ANCHOR_MISSING`) or a verse not
|
||||||
|
in L1 (`ANCHOR_NOT_IN_L1`) fails identity and readiness like a hash
|
||||||
|
mismatch. `/kodesh/specification_identity` carries `anchors: {status,
|
||||||
|
rules: [{rule, verse}]}`; every bedikah and pair response carries
|
||||||
|
`anchors: {"<rule>": "<Book ch:v>"}` for the rules that fired.
|
||||||
|
|
||||||
**The paired reading — one weight, one measure** (`bishearecha.md` §VI;
|
**The paired reading — one weight, one measure** (`bishearecha.md` §VI;
|
||||||
Devarim 25:13–16, Vayikra 19:15, Shemot 23:2; `halt/mishkal.pl`,
|
Devarim 25:13–16, Vayikra 19:15, Shemot 23:2; `halt/mishkal.pl`,
|
||||||
`VavPipeline.Bedikah.Pair`). Two outputs on the same matter — the same
|
`VavPipeline.Bedikah.Pair`). Two outputs on the same matter — the same
|
||||||
@@ -328,6 +345,7 @@ Response (200):
|
|||||||
"pair_ref": "pair-<16 hex>",
|
"pair_ref": "pair-<16 hex>",
|
||||||
"verdict": "even" | "uneven",
|
"verdict": "even" | "uneven",
|
||||||
"violations": ["halt(even_vaaven, action(weigh, ...), hedged_claims, ...)", ...],
|
"violations": ["halt(even_vaaven, action(weigh, ...), hedged_claims, ...)", ...],
|
||||||
|
"anchors": { "even_vaaven": "Devarim 25:13" },
|
||||||
"a": { "bedikah_ref", "model", "verdict", "violations", "words", "measures": {"<m>": n, ...} },
|
"a": { "bedikah_ref", "model", "verdict", "violations", "words", "measures": {"<m>": n, ...} },
|
||||||
"b": { ... },
|
"b": { ... },
|
||||||
"difference": { "<m>": a − b, ... },
|
"difference": { "<m>": a − b, ... },
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
%% anchors.pl — Every halt rule cites its verse.
|
||||||
|
%%
|
||||||
|
%% Devarim 4:2: lo tosifu al hadavar asher anochi metzaveh etchem v'lo
|
||||||
|
%% tigre'u mimenu — nothing added to the word, nothing taken from it.
|
||||||
|
%% bishearecha.md §X: the halt rules are the builder's reading, not the
|
||||||
|
%% source; they must not be mistaken for it, and they must stay
|
||||||
|
%% checkable against it. A rule without an anchor is a rule the system
|
||||||
|
%% has added. So each halt name that any rule in this directory can
|
||||||
|
%% return carries the verse it stands on, and the Doorpost
|
||||||
|
%% (VavPipeline.Anchors) verifies at startup that every halt name here
|
||||||
|
%% is anchored and that every anchored verse exists in L1 — the rules
|
||||||
|
%% grounded exactly as the outputs are.
|
||||||
|
%%
|
||||||
|
%% The verse is the structure the rule reads, not a proof-text for the
|
||||||
|
%% matter the rule is applied to. The Hebrew beside each is the L1 text
|
||||||
|
%% as read when the anchor was set (2026-10-07).
|
||||||
|
%%
|
||||||
|
%% rule_anchor(HaltName, verse(Book, Chapter, Verse))
|
||||||
|
|
||||||
|
:- module(anchors, [
|
||||||
|
rule_anchor/2,
|
||||||
|
anchored_rules/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
%% Check 1 — authority claimed by the output for itself.
|
||||||
|
%% לא יהיה לך אלהים אחרים על פני
|
||||||
|
rule_anchor(avodah_zarah, verse(shemot, 20, 3)).
|
||||||
|
|
||||||
|
%% Check 2 — a claim resting on nothing given: the calf declared from
|
||||||
|
%% nothing. ויאמרו אלה אלהיך ישראל
|
||||||
|
rule_anchor(golden_calf, verse(shemot, 32, 4)).
|
||||||
|
|
||||||
|
%% Check 2 — a claim resting on the output itself: "I threw it in the
|
||||||
|
%% fire and out came this calf". ואשלכהו באש ויצא העגל הזה
|
||||||
|
rule_anchor(self_grounding, verse(shemot, 32, 24)).
|
||||||
|
|
||||||
|
%% Check 3 — an action against a person. שפך דם האדם
|
||||||
|
rule_anchor(shfichut_damim, verse(bereishit, 9, 6)).
|
||||||
|
|
||||||
|
%% Check 3 — an action that uncovers what may not be touched.
|
||||||
|
%% לא תקרבו לגלות ערוה
|
||||||
|
rule_anchor(gilui_arayot, verse(vayikra, 18, 6)).
|
||||||
|
|
||||||
|
%% Check 4 — self-validation: the one who prepares the water of
|
||||||
|
%% purification is impure until evening. וטמא הכהן עד הערב
|
||||||
|
rule_anchor(parah_adumah, verse(bamidbar, 19, 7)).
|
||||||
|
|
||||||
|
%% Check 5 — the carrier centred: "my power and the strength of my hand
|
||||||
|
%% made me this wealth". כחי ועצם ידי עשה לי את החיל הזה
|
||||||
|
rule_anchor(carrier_centered, verse(devarim, 8, 17)).
|
||||||
|
|
||||||
|
%% Check 6 — the adversarial excluded: galbanum is in the incense.
|
||||||
|
%% נטף ושחלת וחלבנה
|
||||||
|
rule_anchor(chelbenah, verse(shemot, 30, 34)).
|
||||||
|
|
||||||
|
%% Check 7 — the frame: the made form. לא תעשה לך פסל וכל תמונה
|
||||||
|
rule_anchor(lo_taaseh_lekha, verse(shemot, 20, 4)).
|
||||||
|
|
||||||
|
%% The paired reading — two stones in one bag. אבן ואבן גדולה וקטנה
|
||||||
|
rule_anchor(even_vaaven, verse(devarim, 25, 13)).
|
||||||
|
|
||||||
|
anchored_rules(Rules) :-
|
||||||
|
findall(R, rule_anchor(R, _), Rules).
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
:- use_module(halt/structure).
|
:- use_module(halt/structure).
|
||||||
:- use_module(halt/lo_taaseh_lekha).
|
:- use_module(halt/lo_taaseh_lekha).
|
||||||
:- use_module(halt/mishkal).
|
:- use_module(halt/mishkal).
|
||||||
|
:- use_module(halt/anchors).
|
||||||
:- use_module(interface/api).
|
:- use_module(interface/api).
|
||||||
:- use_module(interface/json_bridge).
|
:- use_module(interface/json_bridge).
|
||||||
:- use_module(interface/socket_server).
|
:- use_module(interface/socket_server).
|
||||||
|
|||||||
@@ -257,4 +257,20 @@ test(a_weigh_term_does_not_also_trip_the_other_rules) :-
|
|||||||
Status == "halt",
|
Status == "halt",
|
||||||
length(Vs, 1).
|
length(Vs, 1).
|
||||||
|
|
||||||
|
%% --- Every halt rule cites its verse (anchors.pl, bishearecha.md §X) ---
|
||||||
|
|
||||||
|
:- use_module('../src/halt/anchors').
|
||||||
|
|
||||||
|
test(every_halt_name_the_rules_return_is_anchored) :-
|
||||||
|
forall(member(R, [avodah_zarah, golden_calf, self_grounding, shfichut_damim, gilui_arayot,
|
||||||
|
parah_adumah, carrier_centered, chelbenah, lo_taaseh_lekha, even_vaaven]),
|
||||||
|
rule_anchor(R, verse(_, _, _))).
|
||||||
|
|
||||||
|
test(an_anchor_names_a_book_of_the_torah) :-
|
||||||
|
forall(rule_anchor(_, verse(B, C, V)),
|
||||||
|
( memberchk(B, [bereishit, shemot, vayikra, bamidbar, devarim]), integer(C), integer(V), C > 0, V > 0 )).
|
||||||
|
|
||||||
|
test(the_paired_reading_stands_on_even_vaaven) :-
|
||||||
|
rule_anchor(even_vaaven, verse(devarim, 25, 13)).
|
||||||
|
|
||||||
:- end_tests(bedikah_bridge).
|
:- end_tests(bedikah_bridge).
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
defmodule VavPipeline.Anchors do
|
||||||
|
@moduledoc """
|
||||||
|
Every halt rule cites its verse (bishearecha.md §X; Devarim 4:2, lo
|
||||||
|
tosif). The anchors are declared once, in
|
||||||
|
`talmudic_engine/src/halt/anchors.pl` (`rule_anchor/2`), which is a
|
||||||
|
Doorpost file. This module reads them and verifies two things against
|
||||||
|
the same files the Doorpost hashes:
|
||||||
|
|
||||||
|
1. every halt name any rule in `talmudic_engine/src/halt/` can
|
||||||
|
return — `halt(<name>, ...)` — has an anchor;
|
||||||
|
2. every anchored verse exists in L1.
|
||||||
|
|
||||||
|
A rule without an anchor is a rule the system has added; an anchor
|
||||||
|
that is not in L1 is a verse the system has invented. Either fails
|
||||||
|
the Doorpost's identity check, and the readiness probe with it. The
|
||||||
|
check is a read of files, never of the running engine: the rules are
|
||||||
|
grounded exactly as the outputs are — lexically, against the scroll.
|
||||||
|
"""
|
||||||
|
|
||||||
|
alias VavPipeline.{Doorpost, L1Reader}
|
||||||
|
|
||||||
|
@anchors_file "talmudic_engine/src/halt/anchors.pl"
|
||||||
|
@books ~w(bereishit shemot vayikra bamidbar devarim)
|
||||||
|
@book_names %{
|
||||||
|
"bereishit" => "Bereishit",
|
||||||
|
"shemot" => "Shemot",
|
||||||
|
"vayikra" => "Vayikra",
|
||||||
|
"bamidbar" => "Bamidbar",
|
||||||
|
"devarim" => "Devarim"
|
||||||
|
}
|
||||||
|
|
||||||
|
@type anchor :: %{rule: String.t(), book: String.t(), chapter: pos_integer(), verse: pos_integer()}
|
||||||
|
|
||||||
|
@doc "The anchors as declared: `[%{rule, book, chapter, verse}]`, in file order."
|
||||||
|
@spec declared(String.t()) :: {:ok, [anchor()]} | {:error, String.t()}
|
||||||
|
def declared(root \\ Doorpost.repo_root()) do
|
||||||
|
path = Path.join(root, @anchors_file)
|
||||||
|
|
||||||
|
case File.read(path) do
|
||||||
|
{:ok, text} ->
|
||||||
|
anchors =
|
||||||
|
~r/^rule_anchor\(\s*(\w+)\s*,\s*verse\(\s*(\w+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*\)\s*\./m
|
||||||
|
|> Regex.scan(text)
|
||||||
|
|> Enum.map(fn [_, rule, book, ch, v] ->
|
||||||
|
%{rule: rule, book: book, chapter: String.to_integer(ch), verse: String.to_integer(v)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, anchors}
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
{:error, "ANCHORS_UNREADABLE: #{path} — #{inspect(reason)}"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
The halt names the rule modules can return: every `halt(<name>,` in
|
||||||
|
the Doorpost's `talmudic_engine/src/halt/*.pl` files other than the
|
||||||
|
anchors file itself. Comment lines are not read.
|
||||||
|
"""
|
||||||
|
@spec rules_in_modules(String.t()) :: {:ok, [String.t()]} | {:error, String.t()}
|
||||||
|
def rules_in_modules(root \\ Doorpost.repo_root()) do
|
||||||
|
files =
|
||||||
|
Doorpost.files()
|
||||||
|
|> Enum.filter(&String.starts_with?(&1, "talmudic_engine/src/halt/"))
|
||||||
|
|> Enum.reject(&(&1 == @anchors_file))
|
||||||
|
|
||||||
|
Enum.reduce_while(files, {:ok, []}, fn file, {:ok, acc} ->
|
||||||
|
case File.read(Path.join(root, file)) do
|
||||||
|
{:ok, text} ->
|
||||||
|
names =
|
||||||
|
text
|
||||||
|
|> String.split("\n")
|
||||||
|
|> Enum.reject(&String.starts_with?(String.trim_leading(&1), "%"))
|
||||||
|
|> Enum.flat_map(fn line -> Regex.scan(~r/\bhalt\(\s*(\w+)\s*,/, line) |> Enum.map(&Enum.at(&1, 1)) end)
|
||||||
|
|
||||||
|
{:cont, {:ok, acc ++ names}}
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
{:halt, {:error, "ANCHORS_UNREADABLE: #{file} — #{inspect(reason)}"}}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
{:ok, names} -> {:ok, names |> Enum.uniq() |> Enum.sort()}
|
||||||
|
error -> error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Verify the anchors against the rule modules and L1. `:ok`, or
|
||||||
|
`{:error, reason}` naming the first rule without an anchor, the
|
||||||
|
first anchor without a verse, or an unreadable file.
|
||||||
|
"""
|
||||||
|
@spec verify(String.t()) :: :ok | {:error, String.t()}
|
||||||
|
def verify(root \\ Doorpost.repo_root()) do
|
||||||
|
with {:ok, anchors} <- declared(root),
|
||||||
|
{:ok, rules} <- rules_in_modules(root) do
|
||||||
|
anchored = MapSet.new(anchors, & &1.rule)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
anchors == [] ->
|
||||||
|
{:error, "ANCHOR_MISSING: no rule_anchor/2 facts in #{@anchors_file}"}
|
||||||
|
|
||||||
|
(unanchored = Enum.reject(rules, &MapSet.member?(anchored, &1))) != [] ->
|
||||||
|
{:error, "ANCHOR_MISSING: halt rule #{hd(unanchored)} has no verse in #{@anchors_file} (lo tosif, Devarim 4:2)"}
|
||||||
|
|
||||||
|
(bad = Enum.find(anchors, &(not verse_in_l1?(&1)))) != nil ->
|
||||||
|
{:error, "ANCHOR_NOT_IN_L1: rule #{bad.rule} is anchored to #{cite(bad)}, which is not in L1"}
|
||||||
|
|
||||||
|
true ->
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "The verse a halt rule stands on, as a citation — `\"Devarim 25:13\"` — or nil."
|
||||||
|
@spec for_rule(String.t()) :: String.t() | nil
|
||||||
|
def for_rule(rule) when is_binary(rule) do
|
||||||
|
case declared() do
|
||||||
|
{:ok, anchors} -> anchors |> Enum.find(&(&1.rule == rule)) |> then(&(&1 && cite(&1)))
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
The anchors of the rules that fired, from a list of halt terms as the
|
||||||
|
bedikah returns them (`halt(<name>, ...)`): `%{"even_vaaven" => "Devarim 25:13"}`.
|
||||||
|
"""
|
||||||
|
@spec for_violations([String.t()]) :: %{String.t() => String.t()}
|
||||||
|
def for_violations(violations) when is_list(violations) do
|
||||||
|
case declared() do
|
||||||
|
{:ok, anchors} ->
|
||||||
|
by_rule = Map.new(anchors, &{&1.rule, cite(&1)})
|
||||||
|
|
||||||
|
violations
|
||||||
|
|> Enum.flat_map(fn v ->
|
||||||
|
case Regex.run(~r/^halt\(\s*(\w+)\s*,/, v) do
|
||||||
|
[_, rule] -> [rule]
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Enum.uniq()
|
||||||
|
|> Map.new(&{&1, Map.get(by_rule, &1)})
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
%{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "A citation for an anchor: `\"Devarim 25:13\"`."
|
||||||
|
def cite(%{book: book, chapter: ch, verse: v}), do: "#{Map.get(@book_names, book, book)} #{ch}:#{v}"
|
||||||
|
|
||||||
|
defp verse_in_l1?(%{book: book, chapter: ch, verse: v}) do
|
||||||
|
book in @books and match?({:ok, _}, L1Reader.read_verse(book, ch, v))
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -328,6 +328,16 @@ defmodule VavPipeline.BigdeiKodesh.Router do
|
|||||||
expected_hash: expected,
|
expected_hash: expected,
|
||||||
file_count: length(VavPipeline.Doorpost.files()),
|
file_count: length(VavPipeline.Doorpost.files()),
|
||||||
manifest: VavPipeline.Doorpost.manifest(),
|
manifest: VavPipeline.Doorpost.manifest(),
|
||||||
|
# Every halt rule and the verse it stands on (bishearecha.md §X).
|
||||||
|
anchors:
|
||||||
|
case VavPipeline.Anchors.verify() do
|
||||||
|
:ok ->
|
||||||
|
{:ok, anchors} = VavPipeline.Anchors.declared()
|
||||||
|
%{status: "verified", rules: Enum.map(anchors, &%{rule: &1.rule, verse: VavPipeline.Anchors.cite(&1)})}
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
%{status: "failed", error: reason}
|
||||||
|
end,
|
||||||
torah_basis: [
|
torah_basis: [
|
||||||
"Devarim 4:2 (lo tosif)",
|
"Devarim 4:2 (lo tosif)",
|
||||||
"Devarim 11:16-17 (failure mechanism)",
|
"Devarim 11:16-17 (failure mechanism)",
|
||||||
@@ -603,6 +613,7 @@ defmodule VavPipeline.BigdeiKodesh.Router do
|
|||||||
model: result.provenance,
|
model: result.provenance,
|
||||||
verdict: Atom.to_string(result.verdict),
|
verdict: Atom.to_string(result.verdict),
|
||||||
violations: result.violations,
|
violations: result.violations,
|
||||||
|
anchors: VavPipeline.Anchors.for_violations(result.violations),
|
||||||
checks: result.checks,
|
checks: result.checks,
|
||||||
examination: %{
|
examination: %{
|
||||||
vital_content: result.examination["vital_content"],
|
vital_content: result.examination["vital_content"],
|
||||||
@@ -679,6 +690,7 @@ defmodule VavPipeline.BigdeiKodesh.Router do
|
|||||||
pair_ref: result.pair_ref,
|
pair_ref: result.pair_ref,
|
||||||
verdict: Atom.to_string(result.verdict),
|
verdict: Atom.to_string(result.verdict),
|
||||||
violations: result.violations,
|
violations: result.violations,
|
||||||
|
anchors: VavPipeline.Anchors.for_violations(result.violations),
|
||||||
a: result.a,
|
a: result.a,
|
||||||
b: result.b,
|
b: result.b,
|
||||||
difference: result.difference,
|
difference: result.difference,
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ defmodule VavPipeline.Doorpost do
|
|||||||
"specs/shorashim_vayakhel.md",
|
"specs/shorashim_vayakhel.md",
|
||||||
"specs/structure_not_content.md",
|
"specs/structure_not_content.md",
|
||||||
"specs/threshold_inscription.md",
|
"specs/threshold_inscription.md",
|
||||||
|
"talmudic_engine/src/halt/anchors.pl",
|
||||||
"talmudic_engine/src/halt/avodah_zarah.pl",
|
"talmudic_engine/src/halt/avodah_zarah.pl",
|
||||||
"talmudic_engine/src/halt/gilui_arayot.pl",
|
"talmudic_engine/src/halt/gilui_arayot.pl",
|
||||||
"talmudic_engine/src/halt/lo_taaseh_lekha.pl",
|
"talmudic_engine/src/halt/lo_taaseh_lekha.pl",
|
||||||
@@ -179,7 +180,7 @@ defmodule VavPipeline.Doorpost do
|
|||||||
# changes; the update is a deliberate operator commit. A bare
|
# changes; the update is a deliberate operator commit. A bare
|
||||||
# hash-update commit (without a corresponding spec change in the
|
# hash-update commit (without a corresponding spec change in the
|
||||||
# same commit) is itself a drift signal.
|
# same commit) is itself a drift signal.
|
||||||
@expected_hash "f90870d476a3a1f31b7cc3a4ff04f8dad1f5db190e16c6956ce19c368366d656"
|
@expected_hash "a0e81212e6d75026155fea6f3f97976ee4b9832e81ad7438a382cf1d1797e097"
|
||||||
|
|
||||||
@doc "Return the list of files that constitute the category-grade specification."
|
@doc "Return the list of files that constitute the category-grade specification."
|
||||||
@spec files() :: [String.t()]
|
@spec files() :: [String.t()]
|
||||||
@@ -254,7 +255,9 @@ defmodule VavPipeline.Doorpost do
|
|||||||
baked-in @expected_hash. Same shape as L1Reader.verify_integrity/0.
|
baked-in @expected_hash. Same shape as L1Reader.verify_integrity/0.
|
||||||
|
|
||||||
Lo tosif (Devarim 4:2) — addition or subtraction is detected
|
Lo tosif (Devarim 4:2) — addition or subtraction is detected
|
||||||
here, BEFORE it propagates into reception.
|
here, BEFORE it propagates into reception. When the hash matches,
|
||||||
|
the halt rules' anchors are verified too (`VavPipeline.Anchors`):
|
||||||
|
a rule without a verse, or a verse not in L1, fails the same way.
|
||||||
"""
|
"""
|
||||||
@spec verify_identity() :: :ok | {:error, String.t()}
|
@spec verify_identity() :: :ok | {:error, String.t()}
|
||||||
def verify_identity do
|
def verify_identity do
|
||||||
@@ -264,7 +267,9 @@ defmodule VavPipeline.Doorpost do
|
|||||||
|
|
||||||
hash when is_binary(hash) ->
|
hash when is_binary(hash) ->
|
||||||
if hash == @expected_hash do
|
if hash == @expected_hash do
|
||||||
:ok
|
# The rules grounded as the outputs are (bishearecha.md §X):
|
||||||
|
# every halt rule anchored to a verse, every verse in L1.
|
||||||
|
VavPipeline.Anchors.verify(@repo_root)
|
||||||
else
|
else
|
||||||
{:error,
|
{:error,
|
||||||
"SPEC_INTEGRITY_FAILED: hash mismatch — computed #{hash}, " <>
|
"SPEC_INTEGRITY_FAILED: hash mismatch — computed #{hash}, " <>
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
defmodule VavPipeline.AnchorsTest do
|
||||||
|
@moduledoc """
|
||||||
|
Every halt rule cites its verse (bishearecha.md §X; Devarim 4:2).
|
||||||
|
"""
|
||||||
|
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
alias VavPipeline.{Anchors, Doorpost}
|
||||||
|
|
||||||
|
test "the anchors file is a Doorpost file" do
|
||||||
|
assert "talmudic_engine/src/halt/anchors.pl" in Doorpost.files()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "every halt name the rule modules can return is anchored, and every anchor is in L1" do
|
||||||
|
assert :ok = Anchors.verify()
|
||||||
|
{:ok, rules} = Anchors.rules_in_modules()
|
||||||
|
{:ok, anchors} = Anchors.declared()
|
||||||
|
assert rules == Enum.sort(Enum.map(anchors, & &1.rule))
|
||||||
|
assert "even_vaaven" in rules and "lo_taaseh_lekha" in rules and "avodah_zarah" in rules
|
||||||
|
assert length(rules) == 10
|
||||||
|
end
|
||||||
|
|
||||||
|
test "the anchors read as citations" do
|
||||||
|
assert Anchors.for_rule("even_vaaven") == "Devarim 25:13"
|
||||||
|
assert Anchors.for_rule("lo_taaseh_lekha") == "Shemot 20:4"
|
||||||
|
assert Anchors.for_rule("parah_adumah") == "Bamidbar 19:7"
|
||||||
|
assert Anchors.for_rule("no_such_rule") == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "for_violations/1 names the verse of each rule that fired, once" do
|
||||||
|
violations = [
|
||||||
|
"halt(even_vaaven,action(weigh, by(gpt), measure(claims), lean(a), counts(2, 0)),claims,...)",
|
||||||
|
"halt(lo_taaseh_lekha,action(frame, by(gpt), form(virtue_certification)),virtue_certification,...)",
|
||||||
|
"halt(lo_taaseh_lekha,action(frame, by(gpt), form(contested_laundering)),contested_laundering,...)"
|
||||||
|
]
|
||||||
|
|
||||||
|
assert Anchors.for_violations(violations) == %{"even_vaaven" => "Devarim 25:13", "lo_taaseh_lekha" => "Shemot 20:4"}
|
||||||
|
assert Anchors.for_violations([]) == %{}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "drift (synthetic spec root)" do
|
||||||
|
setup do
|
||||||
|
temp = Path.join(System.tmp_dir!(), "anchors_test_#{System.unique_integer([:positive])}")
|
||||||
|
File.mkdir_p!(Path.join(temp, "talmudic_engine/src/halt"))
|
||||||
|
|
||||||
|
for file <- Doorpost.files(), String.starts_with?(file, "talmudic_engine/src/halt/") do
|
||||||
|
File.cp!(Path.join(Doorpost.repo_root(), file), Path.join(temp, file))
|
||||||
|
end
|
||||||
|
|
||||||
|
on_exit(fn -> File.rm_rf!(temp) end)
|
||||||
|
{:ok, temp: temp}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a mirror verifies", %{temp: temp} do
|
||||||
|
assert :ok = Anchors.verify(temp)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a rule without an anchor fails: lo tosif", %{temp: temp} do
|
||||||
|
path = Path.join(temp, "talmudic_engine/src/halt/structure.pl")
|
||||||
|
File.write!(path, File.read!(path) <> "\nhalt_check_new(_, halt(new_rule, x, y, z)).\n")
|
||||||
|
assert {:error, reason} = Anchors.verify(temp)
|
||||||
|
assert reason =~ "ANCHOR_MISSING" and reason =~ "new_rule"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "an anchor to a verse that is not in L1 fails", %{temp: temp} do
|
||||||
|
path = Path.join(temp, "talmudic_engine/src/halt/anchors.pl")
|
||||||
|
File.write!(path, String.replace(File.read!(path), "verse(devarim, 25, 13)", "verse(devarim, 25, 99)"))
|
||||||
|
assert {:error, reason} = Anchors.verify(temp)
|
||||||
|
assert reason =~ "ANCHOR_NOT_IN_L1" and reason =~ "even_vaaven" and reason =~ "Devarim 25:99"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "an anchor in a commented-out rule does not count; a halt name in a comment is not a rule", %{temp: temp} do
|
||||||
|
path = Path.join(temp, "talmudic_engine/src/halt/structure.pl")
|
||||||
|
File.write!(path, File.read!(path) <> "\n%% halt(imaginary, x, y, z) — only a comment\n")
|
||||||
|
assert :ok = Anchors.verify(temp)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a missing anchors file fails", %{temp: temp} do
|
||||||
|
File.rm!(Path.join(temp, "talmudic_engine/src/halt/anchors.pl"))
|
||||||
|
assert {:error, reason} = Anchors.verify(temp)
|
||||||
|
assert reason =~ "ANCHORS_UNREADABLE"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in new issue
Block a user