Companion commit tob254ee9(Prolog Form 4 self_grounding) and6b96250(Prolog regression canary). Closes the Elixir half of Pass 2 finding L. THE GAP: The Prolog halt rules validate coupling traces as Prolog terms: a trace whose anchor is prior_decision/1, system_view/1, consensus/1, or interpretation/1 fires the self_grounding halt. But L3 stores traces as a list of strings (schema: {:array, :string}), serialized from Prolog. Inert string traces in L3 cannot be re-checked by Prolog halts. So the safety property the Prolog rules enforce stops at the Erlang/Elixir boundary — once a trace serializes into L3, no rule re-validates it. Worse: L3 is APPEND-ONLY (Architecture §3.7). Once a trace is in L3 it is permanent oral tradition; there is no path to retroactive correction. A future reader who pulls an L3 record and trusts the trace must be able to trust it was valid at the moment it became permanent. THE FIX: VavPipeline.L3.CouplingTrace (new) — Elixir-side validator that mirrors is_l1_grounded_coupling/1 in talmudic_engine/src/halt/ avodah_zarah.pl. Parses the string form (with a small balanced-paren parser so nested subjects like root(בער) don't break splitting) and recognizes exactly the three valid shapes: coupled(_, verse(BOOK, CHAPTER, VERSE)) coupled(_, mitzvah(ID)) coupled(_, mitzvah(ID), via_verse(BOOK, CHAPTER, VERSE)) Anything else fails. Empty traces are allowed at this layer (the empty-trace case is the golden_calf halt's domain in Prolog Form 2; L3 may legitimately store empty-trace records). L3.Store.record_interpretation/1 — gated. The new write path: case CouplingTrace.validate(coupling_trace) do :ok -> %Interpretation{} |> changeset(attrs) |> Repo.insert() {:error, :ungrounded, bad} -> {:error, {:ungrounded_coupling, bad}} end Validation fires BEFORE Repo.insert. An ungrounded trace can never become permanent oral tradition. CROSS-LANGUAGE INVARIANT: CouplingTrace.l1_grounded?/1 (Elixir) and is_l1_grounded_coupling/1 (Prolog) MUST stay aligned. If Prolog adds a new recognized shape, Elixir must add it too. Both module docstrings name this dependency explicitly. The next time CouplingTraceTest runs after a Prolog schema change, the Elixir tests catch the drift. ONE BUG CAUGHT WHILE WRITING: The first parser cut Hebrew root subjects mid-character because it used String.slice/3 with byte_size/1 as the codepoint length. Fixed to use binary_part/3 (since the trailing `)` is always one byte and the rest is opaque UTF-8). Test for Hebrew-root anchors now passes; documented inline so the trap is visible to future readers. TESTS: test/coupling_trace_test.exs (new, 22 tests): - all 3 valid shapes (verse, mitzvah, via_verse) including Hebrew root subject (catches the byte/codepoint bug) - all the predictable ungrounded shapes - malformed inputs (missing parens, wrong functor, wrong arity, empty string, non-string) - validate/1 list semantics (empty allowed, first-failure reported, mixed-trace caught) - parser robustness with deeply-nested subject parens test/l3_store_test.exs (extended, 5 new :l3-tagged tests): - record_interpretation refuses ungrounded trace - record_interpretation refuses mixed (good + bad) trace - record_interpretation accepts empty trace - record_interpretation accepts mitzvah-anchored trace - record_interpretation accepts 3-arg via_verse trace Full vav_pipeline suite: 315 tests, 0 failures, 24 excluded (24 = 19 pre-existing :l3 + 5 new :l3 store-gate tests; both need PostgreSQL). The 22 CouplingTrace unit tests run unconditionally and all pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VavPipeline
TODO: Add description
Installation
If available in Hex, the package can be installed
by adding vav_pipeline to your list of dependencies in mix.exs:
def deps do
[
{:vav_pipeline, "~> 0.1.0"}
]
end
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/vav_pipeline.