L3 Oral Tradition: PostgreSQL persistence layer for interpretations, minority opinions, teiku
Devarim 17:9 — v'darashta v'higidu lekha et d'var hamishpat. The inquiry requires that what is found is RECORDED. - Ecto Repo with graceful degradation (maybe_add_repo, like other engines) - Migration: interpretations, minority_opinions, teiku_registry tables - Append-only enforcement via REVOKE UPDATE/DELETE - Includes eish_zarah fields: coupling_trace, grounding_type, challenge_sequence - Ecto schemas: Interpretation, MinorityOpinion, TeikuRecord - Changeset validations: status inclusion, panel_size > 0, grounding_type enum - TeikuRecord.resolve_changeset restricts updates to resolved_at/resolution only - L3.Store bridge module: record, query, count operations - L3 tests tagged @moduletag :l3, excluded by default (no local PostgreSQL) - Enable with: SHORASHIM_L3=1 mix test --include l3 - All 76 existing Elixir tests pass, 195 Prolog tests pass, 33 Haskell tests pass Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
adc3e41679
commit
d34e2799ac
14 files changed
+722
-3
No files matched your search
@@ -0,0 +1,105 @@
|
||||
defmodule VavPipeline.Repo.Migrations.CreateL3Tables do
|
||||
@moduledoc """
|
||||
L3 — Oral Tradition. Three tables, append-only.
|
||||
|
||||
Architecture §3.7:
|
||||
interpretations — case decisions with reasoning and Torah references
|
||||
minority_opinions — preserved dissent (foreign key to interpretations)
|
||||
teiku_registry — unresolvable tensions with both sides preserved
|
||||
|
||||
Construction §3.8 (L3 Write Protocol):
|
||||
Every write is an INSERT. Never UPDATE. Never DELETE.
|
||||
The application user has INSERT-only permissions.
|
||||
Every INSERT includes shema_context_hash and processing_component.
|
||||
|
||||
Append-only enforcement via REVOKE is applied after table creation.
|
||||
The shorashim_app role must be created separately (by the kohen/DBA).
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
# === interpretations ===
|
||||
create table(:interpretations) do
|
||||
add :case_ref, :text, null: false
|
||||
add :interpretation, :text, null: false
|
||||
add :status, :text, null: false
|
||||
add :reasoning, :text, null: false
|
||||
add :torah_references, {:array, :text}, null: false, default: []
|
||||
add :roots_involved, {:array, :text}, null: false, default: []
|
||||
add :panel_size, :integer, null: false
|
||||
add :vote_count_for, :integer, null: false
|
||||
add :vote_count_against, :integer, null: false
|
||||
# Construction §3.8 additions
|
||||
add :shema_context_hash, :text
|
||||
add :processing_component, :text
|
||||
# Coupling trace from eish_zarah_reading.md
|
||||
add :coupling_trace, {:array, :text}, default: []
|
||||
|
||||
timestamps(type: :utc_datetime, updated_at: false)
|
||||
end
|
||||
|
||||
# Status constraint
|
||||
create constraint(:interpretations, :valid_status,
|
||||
check: "status IN ('accepted', 'rejected', 'teiku')")
|
||||
|
||||
create index(:interpretations, [:case_ref])
|
||||
create index(:interpretations, [:status])
|
||||
|
||||
# === minority_opinions ===
|
||||
create table(:minority_opinions) do
|
||||
add :interpretation_id, references(:interpretations, on_delete: :restrict), null: false
|
||||
add :dissenting_reasoning, :text, null: false
|
||||
add :torah_references, {:array, :text}, null: false, default: []
|
||||
# Grounding type from eish_zarah_reading.md (achorai/panim)
|
||||
add :grounding_type, :text
|
||||
|
||||
timestamps(type: :utc_datetime, updated_at: false)
|
||||
end
|
||||
|
||||
create index(:minority_opinions, [:interpretation_id])
|
||||
|
||||
# === teiku_registry ===
|
||||
create table(:teiku_registry) do
|
||||
add :case_ref, :text, null: false
|
||||
add :tension_description, :text, null: false
|
||||
add :interpretation_a, :text, null: false
|
||||
add :interpretation_b, :text, null: false
|
||||
add :torah_refs_a, {:array, :text}, null: false, default: []
|
||||
add :torah_refs_b, {:array, :text}, null: false, default: []
|
||||
# Challenge sequence from eish_zarah_reading.md
|
||||
add :challenge_sequence, {:array, :text}, default: []
|
||||
# Resolution fields — teiku may be resolved later (or never)
|
||||
add :resolved_at, :utc_datetime
|
||||
add :resolution, :text
|
||||
|
||||
timestamps(type: :utc_datetime, updated_at: false)
|
||||
end
|
||||
|
||||
create index(:teiku_registry, [:case_ref])
|
||||
|
||||
# === APPEND-ONLY ENFORCEMENT ===
|
||||
# These execute only if the shorashim_app role exists.
|
||||
# In dev/test, the migration runs as the superuser and these are skipped.
|
||||
# In production, the DBA creates the role first.
|
||||
execute """
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'shorashim_app') THEN
|
||||
REVOKE UPDATE, DELETE ON interpretations FROM shorashim_app;
|
||||
REVOKE UPDATE, DELETE ON minority_opinions FROM shorashim_app;
|
||||
REVOKE DELETE ON teiku_registry FROM shorashim_app;
|
||||
-- teiku_registry allows UPDATE only on resolved_at and resolution
|
||||
-- (Enforced at application level — Ecto schema validates)
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
"""
|
||||
end
|
||||
|
||||
def down do
|
||||
drop table(:teiku_registry)
|
||||
drop table(:minority_opinions)
|
||||
drop table(:interpretations)
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user