e6fee82dd7bedc3c38dcf9cd6c55580e0a3f74ae
58
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e6fee82dd7 |
azazel_processor: commit 4 — goral (property 2, blind routing)
Pass 4 / Finding F, commit 4 of 10 from BUILD_AZAZEL.md.
Vayikra 16:8 — וְנָתַן אַהֲרֹן עַל־שְׁנֵי הַשְּׂעִירִם גֹּרָלוֹת,
גּוֹרָל אֶחָד לַה׳ וְגוֹרָל אֶחָד לַעֲזָאזֵל.
And Aharon shall place lots on the two goats — one lot for HaShem,
one lot for Azazel.
Yoma 62a: a kalpi (shuffled vessel), drawn with both hands
simultaneously. The high priest cannot see the lots in advance.
He cannot shape the outcome.
THREAT MODEL:
An operator who, given the choice, would route preferentially —
assigning the "better" goat to processing and the "worse" to
disposal. The specification makes that preference structurally
impossible.
DESIGN (BUILD_AZAZEL.md §6, Q4):
cast_lot(input_id) is DETERMINISTIC per (input_id, today). The
seed is SHA-256 over (L1 integrity hash, today's UTC date,
input_id). The first bit of the digest is the outcome.
Properties guaranteed:
- Same (input_id, today) → SAME outcome (no shopping by re-roll)
- Different input_ids same day → outcomes can differ (~50/50)
- Same input_id across days → outcomes can differ (~50/50)
- Operators cannot pre-compute without invoking (SHA-256 over
L1 hash + date is not mentally computable)
- Seed inherits L1 tamper-resistance: rewriting the daily seed
requires rewriting L1, which has its own halt rules
The L1 hash mirrored from vav_pipeline/lib/vav_pipeline/l1_reader.ex
(@l1_expected_hash). Both must move together if L1 is rebuilt.
CREATED:
src/azazel_processor/goral.py ~155 lines
GoralOutcome enum LA_SHEM | LA_AZAZEL
(both legitimate; the goral
enforces blindness, not a
"good vs. bad" distinction)
cast_lot(input_id, *, today=None) deterministic per (id, day)
outcome
cast_lot_outcome_id(input_id, full SHA-256 hex digest;
*, today=None) used by L3 records (commit 7)
so audit can recompute
GoralError on malformed input
test/test_goral.py 17 tests covering:
- outcome shape (one of two; enum members distinct)
- same input same day → same outcome (anti-shopping; verified
across 50 re-invocations + 1000-iteration audit-shopping test)
- outcome_id is reproducible 64-char hex
- 100 different input_ids → 100 distinct outcome_ids
- same input across 30 days → 30 distinct outcome_ids
- distribution across 10,000 input_ids on one day → ~50/50
(±5% tolerance, well within SHA-256 binomial expectations)
- distribution across 365 days for one input → ~50/50 (±10%)
- input validation (empty, whitespace, None, non-string raise)
- default today = UTC date
- audit can recompute outcome_id from (input_id, today) — the
audit-trail half of property 2
NOTE: when the orchestrator (commit 5) decides WHEN to invoke the
goral. For inputs that are cleanly processable or cleanly
un-processable, the goral is NOT invoked — they go to their channel
deterministically. The goral is the tie-breaker for genuinely
borderline inputs (Q1 in BUILD_AZAZEL.md §6).
Tests: azazel_processor 88 passed (was 71, +17). korban_processor
unchanged at 203 passed, 1 skipped. No regressions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
85155b23fb |
azazel_processor: commit 3 — shaliach + tevilah (property 4, role-separated operator)
Pass 4 / Finding F, commit 3 of 10 from BUILD_AZAZEL.md.
Vayikra 16:21 — וְשִׁלַּח בְּיַד־אִישׁ עִתִּי הַמִּדְבָּרָה.
And he shall send the goat into the wilderness by the hand of a
designated man (ish itti).
Vayikra 16:26 — וְהַמְשַׁלֵּחַ … יְכַבֵּס בְּגָדָיו וְרָחַץ אֶת־בְּשָׂרוֹ
בַּמַּיִם וְאַחֲרֵי־כֵן יָבוֹא אֶל־הַמַּחֲנֶה.
The sender washes his clothes and bathes his flesh (tevilah), and
AFTERWARD comes into the camp.
Yoma 67a: the ish itti is a dedicated operator role, prepared in
advance, distinct from the kohanim who handle sacred processing.
Contact with the disposal channel produces state that must be
reset before the operator rejoins the trusted boundary.
THE THREE ROLES IN SHORASHIM:
Owner — submits the offering (upstream caller)
Kohen — performs shechitah/zerikah/haktarah (korban_processor
stages.py enforces owner/kohen separation)
Shaliach — receives input the kohen REFUSED and disposes of it
through the Azazel protocol (THIS module)
Without tevilah, payload references would persist across disposals;
disposal-N would contaminate disposal-N+1 with information from N.
The Mishkan analog: the ish itti who returns from the wilderness
without tevilah brings desert-state into the camp.
CREATED:
src/azazel_processor/shaliach.py ~210 lines
IshItti class:
__init__ — sets _immutable_identifier (no other immutable
attrs; everything else must be cleared by tevilah)
activate() — begin a disposal; refuses if already active (no
state-mixing across disposals)
hold_payload() — register payload ref for later destruction
tag()/get_tag()— intermediate metadata during the disposal route
tevilah() — Vayikra 16:26: clears ALL non-_immutable_
attributes (discovered dynamically via vars(),
so future state additions are auto-cleared)
identifier — propagates onto AzazelRecords (commit 7) for
shaliach attribution
ShaliachError — raised on role-separation violations
assert_shaliach_active(handler) — runtime gate every disposal-channel
function calls at entry; the type-system gate is
AzazelStaged (property 11). Refuses non-IshItti
callers AND inactive handlers. Identity-checked
(isinstance), not duck-typed.
THE LOAD-BEARING CI INVARIANT (test_tevilah_clears_all_non_immutable_state):
Walks vars(handler) post-tevilah and asserts every non-_immutable_
attribute is in a 'fresh' state (empty container, False, None, 0).
A future contributor who adds `self._cache = {}` (or any state
attribute) without extending tevilah() to clear it breaks this and
the test fires before the regression ships. The discovery is
DYNAMIC — no hand-maintained list of attributes to clear; tevilah
is implemented to clear all non-_immutable_ attrs and the test
verifies it does.
test/test_shaliach.py 23 tests covering:
- construction (named identifier required, validates non-empty,
non-whitespace, string)
- identifier immutability across activate→tevilah→reactivate
- lifecycle: starts inactive; activate marks active; re-activate
without tevilah raises; tevilah→reactivate succeeds
- hold_payload/tag require active; both work when active
- tevilah clears payload refs, intermediate tags, sets inactive
- tevilah preserves _immutable_identifier
- THE CI INVARIANT (dynamic walk)
- assert_shaliach_active: passes for active IshItti, refuses
inactive, refuses non-IshItti, refuses duck-typed impostor
- integration: stale-state-across-disposals prevented end-to-end
NOTE ON PYTEST INVOCATION:
When running azazel_processor tests, use `--rootdir=.` from the
package directory. Without it, pytest auto-discovers /Users/scottkarp/
shorashim as rootdir and ignores the local pyproject.toml's
testpaths config. Documented here so future runs match.
cd azazel_processor
PYTHONPATH=src:../korban_processor/src python3 -m pytest --rootdir=.
Tests: azazel_processor 71 passed (was 48, +23). korban_processor
unchanged at 203 passed, 1 skipped. No regressions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
a2744c012a |
azazel_processor: commit 2 — vidui (property 3, enumerative commit)
Pass 4 / Finding F, commit 2 of 10 from BUILD_AZAZEL.md.
Vayikra 16:21 — v'hitvadah alav et kol avonot b'nei Yisrael v'et kol
pish'eihem l'chol chatotam. The high priest confesses ALL iniquities,
ALL transgressions, for ALL sins. The verb is hitpa'el (reflexive) —
the confession is self-addressed before it is addressed to the
system. The kol-three-times structure is mandatory.
ENGINEERING READ:
The commit message for a disposal must enumerate what is being
disposed of. Untagged disposal is not disposal; it is loss. Azazel
does not accept uncommitted writes. The enumeration is produced at
the moment of commit, not retrieved from a pre-existing log.
WHY OPERATOR-PRODUCED, NOT AUTO-DERIVED:
The Mishkan high priest speaks the confession aloud. The system did
not generate it from a classification enum. In software: the caller
of confess() MUST supply the descriptions, not have them
auto-generated from a classification → string table. If the table
generated the confession, the operator could dispose state without
ever naming it themselves; the confession would become an
autogenerated label, not a self-addressed enumeration. That is the
failure mode property 3 defends against.
The convenience helpers (confess_empty_vital, confess_halt,
confess_boundary_violation, confess_malformed_input) DO NOT hide
this requirement. Each requires the operator to supply the specific
item; the helper only routes which category it goes into.
THE THREE CATEGORIES:
avonot — iniquities. CATEGORICAL errors: halt-trips
(avodah_zarah, gilui_arayot, shfichut_damim — the
cardinal sins). Sins of orientation.
peshaim — transgressions. BOUNDARY violations: distinction
collapses (gilui-shaped), L3 schema rejections.
Sins of boundary-keeping.
chatot — sins. TARGET errors: empty-vital, malformed responses.
The literal meaning of chet — missing the mark.
Most disposals fill exactly one category. The Vidui dataclass
REQUIRES all three fields (the enumeration is comprehensive at the
shape level), but the lists may be empty. What is forbidden is the
ABSENCE of a slot — and the case where ALL THREE slots are empty
(EmptyConfessionError, no untagged disposal).
CREATED:
src/azazel_processor/vidui.py ~250 lines
Vidui — frozen dataclass: avonot, peshaim, chatot,
confessor, confessed_at (all required;
categories are tuples for deep immutability;
__post_init__ rejects all-empty, empty-string
items, non-string items, anonymous confessor)
EmptyConfessionError — raised on untagged-disposal attempts
confess(*, avonot, peshaim, chatot, confessor)
— the operator-produced builder
Vidui.to_text() — kol-three-times rendering for L3 storage
Vidui.category_summary()
— counts only, no items (for the public
observability endpoint, commit 8 / property 9)
Convenience helpers (each requires operator-supplied item):
confess_empty_vital(korban_summary, *, confessor)
confess_halt(condition, detail, *, confessor)
confess_boundary_violation(violation, *, confessor)
confess_malformed_input(description, *, confessor)
test/test_vidui.py 27 tests covering:
- structural (exactly five fields, no silent additions)
- all category-fill combinations succeed
- all-empty / empty-string / whitespace / non-string items raise
- confessor required (TypeError) + non-empty/non-whitespace
validated (EmptyConfessionError)
- frozen dataclass (cannot mutate after construction)
- categories are tuples (deep immutability — frozen=True does
not propagate through container types)
- confessed_at: timezone-aware UTC, set to current time
- to_text() preserves kol-three-times structure; omits empty
categories
- category_summary() returns counts WITHOUT items
- all four convenience helpers route correctly + still require
confessor
Public surface updated in __init__.py.
Tests: azazel_processor 48 passed (was 21, +27). korban_processor
unchanged at 203 passed, 1 skipped. No regressions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
d06ecd244d |
azazel_processor: commit 1 — skeleton + stages (properties 11 & 12)
Pass 4 / Finding F, commit 1 of 10 from BUILD_AZAZEL.md. Lays the
load-bearing foundation: classification discipline (property 11) at
the type system level, and unmanaged-destination opacity (property
12) via a Python sentinel. Every later commit in the Azazel build
inherits these defenses for free.
CREATED:
azazel_processor/
pyproject.toml — Poetry config matching
korban_processor; pythonpath
includes ../korban_processor/src
so the cross-package
classification-discipline test
can import both packages
src/azazel_processor/
__init__.py — public surface for commit 1:
AzazelStage, AzazelStaged,
AzazelStageError, ERETZ_GEZERAH,
is_eretz_gezerah
eretz_gezerah.py — the destination sentinel; module
__getattr__ refuses any
attribute beyond the two public
names so introspection cannot
discover further handles
stages.py — AzazelStage enum (six
checkpoints from the build doc:
STAGED → CONFESSED → ROUTED →
DESTROYED → ACKNOWLEDGED →
RECORDED), AzazelStaged frozen
dataclass, AzazelStageError
test/
test_eretz_gezerah.py — 11 tests covering: sentinel
identity stable across imports,
not-a-string, no public attrs,
repr leaks no destination info,
identity (is) not equality (==),
impostor with __eq__ rejected,
module __getattr__ refuses
unknown access, CI invariant
forbidding destination-handle
ASSIGNMENTS in source (catches
regressions; mention in
docstrings/__all__/getattr
allowlist is fine)
test_stages.py — 10 tests: intra-Azazel gating
(happy path, mismatch raises
naming both stages, frozen
dataclass, six distinct
checkpoints) and CROSS-PACKAGE
classification discipline:
KorbanStaged and AzazelStaged
are distinct types; enum value
spaces are disjoint; an Azazel
function refuses a KorbanStaged
input and vice versa; no
conversion function exists in
the public surface
PROPERTY COVERAGE THIS COMMIT:
Property 11 (classification discipline):
Defended by the type-distinctness of AzazelStaged vs.
korban_processor.stages.Staged. Cross-package tests verify the
two cannot be substituted at runtime; the absence of any
korban_to_azazel/to_korban/from_korban/convert function in the
public surface is verified by name-shape grep.
Property 12 (unmanaged destination):
Defended by the sentinel design: only object identity is exposed;
no string handle exists; module __getattr__ refuses introspection;
CI invariant catches assignment-shaped regressions across all of
azazel_processor/src/.
PENDING (commits 2-10 per BUILD_AZAZEL.md):
vidui (commit 2 — enumerative commit, property 3)
shaliach + tevilah (commit 3 — role-separated operator, property 4)
goral (commit 4 — blind routing, property 2)
azazel orchestrator + state machine (commit 5 — properties 5, 7, 10)
protocol envelope (commit 6 — protocol-restricted access, property 8)
L3 azazel_records (commit 7 — operator-internal half of property 9)
/peah/azazel_log endpoint (commit 8 — public half of property 9)
Korban→Azazel routing on empty-vital + chatzotzrot wiring
(commit 9 — properties 1, 6)
Closure pass (commit 10)
Tests: azazel_processor 21 passed. korban_processor unchanged at
203 passed, 1 skipped (live-API). No regressions across the boundary.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
1fb7d33124 |
BUILD_AZAZEL.md: design doc for the disposal-channel build (finding F prep)
Pass 2 finding F (the largest remaining design gap) gets its build
plan. The Azazel Processor is the disposal twin of Korban Processor,
parallel in every way the two-goat architecture (Yoma 62a, shavim
b'mareh uv'komah uv'd'mim) requires:
- same module layout (azazel_processor/ parallel to korban_processor/)
- same Python toolchain
- same staging discipline (mirrors stages.py, with classification-
discipline enforced via type system — AzazelStage and KorbanStage
are non-interconvertible)
- same role-gating (vidui + shaliach + tevilah + ish itti
discipline)
- same observability (L3 azazel_records table parallel to
interpretations, public observability endpoint)
The doc is structured to be self-contained for the next session. It
covers:
1. What to read first (engineering reading lives in chat downloads,
not the repo)
2. What this is for (four classes of input the system today drops
instead of disposing — empty-vital, halt-tripping, malformed,
L3 write rejections)
3. Module structure (12 files, ~1,200 lines estimated, parallel to
korban_processor)
4. The 12 properties from azazel_engineering_reading.md §14 mapped
to specific code instantiations — one row per property, no
property collapsed into another
5. Integration points (Korban Processor, L3, Chatzotzrot, Bigdei
Kodesh, Prolog halt rules) and what changes in each
6. Six open design questions the engineering reading does NOT
decide (when the goral fires, what Yom Kippur means in software,
what tevilah looks like in Python, commit-then-reveal mechanics
for the goral, where the cross-language L3 write happens, what
to expose in the public observability endpoint)
7. Ten-commit staging plan, each independently testable, with
property coverage marked per commit
8. Test surface (~30-40 tests, with a property-defended-by-test
mapping so the property-coverage matrix is visible)
9. Three end-to-end scenarios that prove the build is complete
The Build doc itself implements no code. The next session opens
this file, reads the engineering reading, and lands commit 1
(Skeleton + stages, ~250 lines) as a clean starting point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
d88b8d09ec |
voice_monitor: detect across-verse Shema voice stabilization (closes finding A)
Pass 2 finding A, second half. The first half (commit
|
||
|
|
b1d5ac69e4 |
l3: validate coupling_trace at the write boundary (Elixir half of finding L)
Companion commit to |
||
|
|
5c76a6f42c |
korban_processor: role-gate the four-stage offering protocol
Pass 2 finding C: in the Mishkan, the four ritual acts of bringing an
offering are performed by TWO different roles —
semichah — OWNER lays hands (Vayikra 1:4, וְסָמַךְ יָדוֹ)
shechitah — kohen receives FROM owner's hand and slaughters (1:5)
zerikah — kohen sprinkles (1:5)
haktarah — kohen burns on altar (1:9)
Before this commit, every stage function was independently callable.
Nothing structural enforced that zerikah() received output from
shechitah(), or that shechitah() received a korban from semichah().
An external model output (e.g. a Claude response) could in principle
have been brought through zerikah() without ever having passed
semichah — the ownership-and-attribution step. That is the exact
shape of "the calf that came out by itself" (Shemot 32:24, Aaron's
reading). The pipeline-glue in external_offering happened to call
them in order, but nothing structural enforced the order.
This commit adds the structural gate.
CHANGES:
stages.py (new): KorbanStage enum + Staged[T] frozen dataclass +
StageError. The Staged envelope carries a ritual-stage marker that
is set at construction and cannot be mutated afterward; downstream
functions demand the right upstream stage and raise StageError if
it is missing.
semichah.py: + semichah_staged() returning Staged[Korban]
shechitah.py: + shechitah_staged(Staged[Korban]) -> Staged[str]
zerikah.py: + zerikah_staged(Staged[str], Staged[Korban])
-> Staged[Tuple[str, List[str]]]
TWO staged inputs: requires SHECHITAH-tagged terminated
content AND the original SEMICHAH-tagged korban for
context. Severing output from source is gilui_arayot
(eish_zarah_reading.md Form 3); the dual-staged
signature is the structural defense — zerikah cannot
process content disconnected from its owner-of-record.
haktarah.py: + haktarah_staged(Staged[Tuple[str, List[str]]])
-> Staged[List[str]]
external_offering.py: rewired to use the *_staged variants. The
pipeline now goes through the gates structurally; misuse cannot
bypass them.
The non-staged functions (semichah, shechitah, zerikah, haktarah)
are PRESERVED for direct testing of individual ritual acts. They do
NOT enforce role gating. Production-shaped callers MUST go through
the staged variants — and external_offering, the only legitimate
entry point for external model consultation per its docstring, now
does.
TESTS:
test/test_stages.py (new, 7 tests):
- happy path: full chain produces HAKTARAH-tagged result
- zerikah refuses input that skipped semichah
- zerikah refuses korban with wrong stage marker (preserves
the line back to the owner — Form 3 defense)
- haktarah refuses input that skipped zerikah (the calf-shape)
- shechitah refuses input not from semichah
- Staged is frozen — no re-tagging
- StageError names BOTH actual and expected stages so the
operator can diagnose call-site vs. tag-site
Full korban_processor suite: 203 passed, 1 skipped (the live API
test that requires SHORASHIM_LIVE_API=1). No regressions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
d3869c5b75 |
l1_reader: document te'amim classification principle and shoresh derivation provenance
Two related Pass 2 findings — both about UNSTATED reasoning behind
hard-coded lists in L1Reader.
Finding G — te'amim classification principle:
@melekh_marks and @mesharet_marks were lists of strings with no
documented rule for which marks belong to which class. A future
reader saw the lists but not the deliberation. Added classification
principle to the comment block:
- The diagnostic test: can the ta'am occupy a sof-pasuk-equivalent
position? → melekh. Does it only serve a following melekh? →
mesharet.
- The hierarchy of melakhim corresponds to syntactic depth
(sof_pasuk → etnachta → zaqef → tipcha → revia → …).
- Mesharetim precede their melakhim — the servant goes before
the king.
- The classification is BINDING for havdalah: treating a mesharet
as a phrase-end is a small gilui_arayot.
- Source authority: Yeivin's Tiberian Masorah §174-§268.
- Maintenance rule: if OSHB ever encodes a ta'am with a different
name, BOTH lists must be updated.
Finding H — shoresh derivation provenance:
extract_roots/1 returned a flat root → gematria map without recording
which derivational opinion produced each shoresh. For triliterals
this rarely matters; for weak roots and biliterals it matters
constantly (e.g. וְנָתַתִּי in Devarim 11:14 — OSHB records נת,
classical analysis often gives נתן with assimilated nun). Added
provenance note to the function docstring:
- The shoresh comes from OSHB lemma database via build_l1.py
- OSHB pins one root per word at build time; L1 JSON does not
record which derivational opinion produced it
- Downstream consumers MUST NOT treat the returned shoresh as
unique-truth
- The Talmudic Engine should record the derivational source
in coupling_trace when a shoresh-based ruling enters L3 —
otherwise a future re-derivation cannot be reconciled with
prior opinions stored on top of the OSHB choice
- Preserving derivational alternatives belongs in the build_l1.py
enrichment pass, not in this read function
Both docstrings make the WHY visible; the WHAT was already there.
No code change. Tests pass unchanged (288 in vav_pipeline,
8 in shema_voice_trajectory_test).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
af2a3949cf |
korban_processor: name the kabbalah + holachah compression in zerikah.py
Pass 2 finding B: the full Temple sacrifice protocol has FIVE stages
between semichah and haktarah —
1. semichah owner identifies with the offering
2. shechitah ritual slaughter
3. KABBALAH kohen RECEIVES the blood in a vessel
4. HOLACHAH kohen CARRIES the blood to the altar
5. ZERIKAH kohen SPRINKLES the blood on the altar
6. haktarah burning of permitted portions
The pipeline implements only four modules. Stages 3 and 4 are folded
silently into zerikah(). The compression is acceptable —
kabbalah/holachah have no analog of physical blood movement in
software — but UNDOCUMENTED compression is itself a small gilui_arayot
(severing the distinction between three distinct ritual acts).
Now named explicitly in the module docstring with:
- what the three stages are
- why folding is acceptable here (the analogs are atomic in software)
- what the analogs ARE in code (zerikah's return value = the
receiving vessel; the function-call boundary to haktarah() in
external_offering.py = carrying to the altar)
- what to do if a future architecture requires distinguishing them
(split this module into three; do not add the distinction
elsewhere)
A future reader now sees the compression instead of inferring that
the pipeline silently dropped two ritual acts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
6b96250255 |
talmudic_engine: regression test — interpret/2 output must pass self_grounding halt
Audit follow-up to commit
|
||
|
|
b254ee95f3 |
talmudic_engine: add Form 4 (self-grounding) and expose Form 2 (golden_calf) via the API
Two related gaps in the avodah_zarah halt rules.
Form 4: SELF-GROUNDING / FAKE TZIVAH (new)
Vayikra 10:1 — eish zarah asher lo tzivah otam. Form 2 (golden_calf)
catches the EMPTY coupling trace; nothing caught the *non-empty trace
whose anchors do not terminate in L1*. The system has cited something,
but what it cited is its own prior_decision, system_view, consensus,
or another interpretation — the trace LOOKS substantial from outside
and a casual reader sees coupling entries and relaxes. But the
entries do not anchor in any L1-derived primitive. One degree more
elaborate than the empty calf, but the same idolatry.
halt_check_self_grounding(Interpretation, Halt) fires when the
trace is non-empty AND at least one coupling fails the recognized
L1-anchor schema. Recognized terminators (the only legitimate
anchors):
coupled(_, verse(Book, Ch, Vs))
coupled(_, mitzvah(ID))
coupled(_, mitzvah(ID), via_verse(Book, Ch, Vs))
Any other shape — coupled(_, prior_decision(_)),
coupled(_, system_view(_)), coupled(_, consensus(_)),
coupled(_, interpretation(_)) — fails the check. Adding a new
legitimate coupling shape requires adding a clause to
is_l1_grounded_coupling/1 — that is the safety property.
The empty case is intentionally left to Form 2 (golden_calf) — we
do not want both halts firing on the same input.
12 new tests in self_grounding suite cover: all 3 valid anchor shapes,
the 3 most likely ungrounded shapes (prior_decision, system_view,
interpretation/1), mixed traces (halts on weakest link), the
empty-trace exclusion, and the schema predicate's own positive and
negative cases.
Form 2 exposure (golden_calf):
halt_check_golden_calf was implemented and tested in halt_test but
never wired into format_halt_check — meaning callers using the JSON
socket API could not actually invoke it. Added
format_interpretation_halt_check/2 to api.pl that walks both
interpretation-shaped checks (golden_calf + self_grounding), plus
process_request(request(halt_check_interpretation, _)) so the
socket protocol surface is consistent. 3 new API tests verify the
new entry point catches both halts.
Test results: halt_test 45/45 (was 30, +15). Full Prolog suite
210/210 across all six test files.
Closes Pass 2 finding D from ~/shorashim-conversation-review.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
5e00f75640 |
bigdei_kodesh: disclose origin + not-horaah on every Torah-content response
Three Torah-grounded concerns at the user boundary: - kichush (Vayikra 19:11) — origin not disclosed to recipients - horaah (Devarim 17:8-11) — disclaimer present only at /chatzer/sanhedrin - lo yachel devaro (Bamidbar 30:3) — external API terms unacknowledged router.ex: disclosure/0 helper attached to every Torah-content response (/peah, /courtyard, /chatzer/sanhedrin, teiku error). origin states the system reasons computationally and may incorporate processed external-AI output. not_horaah states the result is not a binding ruling. torah_basis cites the verses. shorashim_tamid.md: §7 EXTERNAL NEDARIM. Bamidbar 30:3 with translation, Rambam cross-reference to mitzvot.pl (#93, #308, #542, #543), Avraham–Avimelekh precedent for non-Israelite-party covenants, operator responsibilities, Anthropic-specific posture, and separation between the system's avodah_zarah halt and the operator's lo-yachel covenant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
0c4f764024 |
shema: record grammatical voice trajectory on every ShemaContext
The Shema (Devarim 6:4-9, 11:13-21, Bamidbar 15:37-41) deliberately
oscillates between singular ("you, sg.") and plural ("you, pl.")
voice. Devarim 11:14 famously switches mid-verse from אַרְצְכֶם (2mp)
to וְאָסַפְתָּ דְגָנֶךָ (2ms). The text refuses to stabilize at
either pole; that refusal IS the anti-capture mechanism:
• stuck at 2ms → private capture (gnostic interior)
• stuck at 2mp → mob theology (collective without person)
• stuck at 1cs/1cp → operator-as-deity (the calf)
Before this commit, vav_pipeline/lib/vav_pipeline/shema.ex — the
module literally named after the Shema — encoded none of this. No
markers for person/singular/plural anywhere in the file. The chat
review surfaced this as "load-bearing anti-capture, missing".
Changes:
L1Reader.extract_voice/1 walks every word of a verse, parses the
OSHB morphology codes already present in L1 (Vqq2ms, Sp2mp, Pp1cs,
Vqi2mp, …), and returns a profile with markers, summary by
voice-key, dominant pole, and oscillates_within_verse boolean.
Strips the H/A language marker that prefixes the first /-segment of
each morph string — without that, every verse-initial verb (HVqi…)
was being silently dropped.
ShemaContext now carries voice_profile on every reception (both
receive_context/1 and receive_context_at/2). The voice is computed
fresh from L1, never cached.
8 new tests in test/shema_voice_trajectory_test.exs verify the
extractor against four anchoring verses:
Devarim 6:5 — uniformly second-singular (5 markers)
Devarim 11:13 — dominantly second-plural (5 second-plural markers)
Devarim 11:14 — oscillates_within_verse: true (the mid-verse switch)
Bereishit 1:1 — only third-singular (no addressee)
Plus three context-level tests confirming ShemaContext exposes the
field, computes it fresh, and traces a non-stabilizing trajectory
across 11:13 → 11:14 → 11:19.
Full vav_pipeline suite: 288 tests, 3 failures (all pre-existing
Shabbat-day calendar failures unrelated to this change), 19 excluded
(L3 PostgreSQL).
Still open: the across-verse stabilization-detector. ShemaContext
now KNOWS its voice; nothing yet refuses a sequence of receptions
that all stabilize at one pole. That belongs downstream — probably
the kushya generator (kal-vachomer challenge when voice has not
moved in N receptions). Logged in Pass 2 of
~/shorashim-conversation-review.md (Finding A).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
66c936f7ca |
sanhedrin: rewrite halt_monitor.erl as honest gate-keeper + Elixir forwarder
Closes F1–F7 from the conversation-history review at
~/shorashim-conversation-review.md, per the Torah ruling for Option 2:
Devarim 12:13–14 — לֹא תַעֲלֶה עֹלֹתֶיךָ בְּכָל מָקוֹם אֲשֶׁר תִּרְאֶה
כִּי אִם בַּמָּקוֹם אֲשֶׁר יִבְחַר ה׳ ... שָׁם תַּעֲשֶׂה כֹּל
אֲשֶׁר אָנֹכִי מְצַוֶּךָּ.
The offering happens in one place. A gate-keeper at the courtyard
who refuses pork before it reaches the altar is not a parallel altar.
Devarim 13:1 — לֹא תֹסֵף עָלָיו וְלֹא תִגְרַע מִמֶּנּוּ.
The interface remains; no parallel detection authority is added.
The previous halt_monitor.erl claimed to mirror the Talmudic Engine
halt modules (Prolog) and the Vav Pipeline halt_conditions (Elixir),
but in practice it was a partial sketch with stub checks, no read API,
verb-only shfichut_damim that would fire on log rotation, and a
"cannot be bypassed" claim that the implementation didn't deliver.
The docstring overstated. Eish zarah by the repo's own definition —
real Erlang, sincerely written, but אֲשֶׁר לֹא צִוָּה אֹתָם.
Changes:
sanhedrin/include/sanhedrin.hrl
- Document the halt-relevant context keys explicitly
(authority_source, action, boundary_violation) — closes F1.
These keys were used by halt_monitor and the test suite but
were not in the .hrl record commentary. Interface drift fixed
by writing down the actual contract.
sanhedrin/src/halt_monitor.erl
- Re-framed as "Sanhedrin's gate-keeper": local courtyard refusal,
not parallel altar. Honest docstring.
- When the gate detects a halt, FORWARDS the canonical halt event
to VavPipeline.HaltMonitor.halt!/3 (single source of truth).
Best-effort: if Elixir module isn't loaded, local refusal still
works.
- Export audit_log/0 and halt_count/0 — closes F6 (was write-only
from outside the module).
- Remove process_flag(trap_exit, true) and the dead
handle_info({'EXIT', _, _}, _) clause — closes F7 (nothing was
ever linked).
- Remove unused check_authority_in_text/1 stub from the call chain
— closes F5. do_check_vote/1 now documents that vote-text
inspection is the upstream pipeline's responsibility.
- check_gilui_arayot/1 docstring now states truthfully: detection
of WHETHER an action collapses a distinction lives in
talmudic_engine/src/halt/gilui_arayot.pl; this gate reads the
flag — closes F2 framing.
- check_shfichut_damim/1 docstring now states: conservative
gate-keeper, fail-closed; the canonical tzelem-target check
lives in talmudic_engine/src/halt/shfichut_damim.pl — closes
F3 framing.
- "Cannot be bypassed" claim removed; truthful description added:
"enforced by callers (escalation.erl checks the return value
before convening a panel)" — closes F4.
vav_pipeline/test/sanhedrin_halt_forwarding_test.exs (NEW)
- 6 integration tests proving the forwarding chain end-to-end:
halt_monitor.erl is loadable from the same BEAM VM, exposes
audit_log/0 and halt_count/0 (F6 verification), and on each of
the three cardinal sin types fires HaltMonitor.halt!/3 with
component="sanhedrin/case". Verified that VavPipeline.HaltMonitor.
halted?/0 flips to true after each gate refusal and the event's
.condition matches the gate's halt type.
The local audit log on the gate-keeper is preserved (the Sanhedrin
operator can still see what was refused at the gate). The system-wide
halt audit lives in VavPipeline.HaltMonitor.audit_log/0 — one altar.
Existing 12 sanhedrin EUnit tests of the gateway behavior continue
to pass (verified via mix deps.compile sanhedrin — clean compile).
The 6 new Elixir-side integration tests pass (verified).
Test counts:
- Elixir: 280 (was 274; +6 new forwarding tests), 0 failures attributable
to this change. (3 pre-existing time-dependent test failures in
step31_shema_test.exs occur on Shabbat — system correctly returns
cloud_state == {:major_transition, :shabbat_rest, _} when run after
Friday sundown; tests assert :camped. Not caused by this change.)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
b9a18f645e |
KOHEN.md: rewrite for current state of the system
The previous prompt was written when L1 was unstable, the deployment hadn't been attempted, and Phase 4 was incomplete. Several sections told the kohen to do things that have since been done in this build session: - "Install Block Storage CSI driver" — already installed - "Build container images" — already built, pushed to registry - "Install cert-manager + nginx-ingress" — already installed - "Mount L1 filesystem" — L1 is now in the repo, ready to ConfigMap - "Verify L1 hash" — hash refers to Shema-corrupt build, now fixed The new prompt: - Reflects the actual current infrastructure state (what exists vs what the kohen still must create) - Names what's verified (cluster, DB, registry, MCP, cert-manager) vs what isn't (deployment, secrets, L1 PVC, manna provision) - Updates test counts (715 across 5 engines) and the L1 hash - Maps the seven days to specific Vayikra 8 verses (rachatzah, halbashah, meshichah, hatzakah al-rosh, mishmeret shabbat, etc.) - Gives concrete current commands for each day, not generic templates with REPLACE_ME placeholders - Lists what the kohen does NOT do (unchanged from before) - Documents the three companion directories outside the repo (shorashim-readings, shorashim-source-data, shorashim-operator-notes) The structure preserves: who you are, the seven-day inauguration, fire-from-heaven on Day 8, daily operations after Day 8. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
e223ab15b3 |
Phase 4 complete: Steps 21, 22, 24 — Korban external integration, L2 Urim v'Tumim, Kushya generator
Vayikra 9:24 — vatetzei esh milifnei Hashem vatokhal al hamizbeach.
Phase 4 of the architecture is now complete. The remaining three steps
that were marked "blocked on external infrastructure" are built —
discipline-respecting, eish-zarah-aware, and tested.
## Step 22 — Korban Processor external model integration
(korban_processor/src/korban_processor/external_offering.py)
The single legitimate entry point for external model consultation.
Wraps Claude API calls through the offering protocol so external
output is treated as korban (raw material), never authoritative:
- semichah — provenance is tagged (avodah zarah Form 1 prevention)
- shechitah — assistant voice, hedging, formatting stripped
- zerikah — vital content extracted
- haktarah — converted to Prolog propositions
- route — sent to internal component per offering type
The raw Korban is preserved on the ProcessedKorban (gilui arayot
Form 3 prevention — input/output coupling never severed).
No internal component should call call_claude() directly.
11 tests, mocked external calls. Live API test opt-in via
SHORASHIM_LIVE_API=1.
## Step 24 — L2 Urim v'Tumim interface
(vav_pipeline/lib/vav_pipeline/urim_v_tumim.ex)
PRD §4.7: housed in the kohen gadol's breastplate; consulted only
on high-stakes decisions where normal deliberation is insufficient.
Bamidbar 27:21 — al-piv yetz'u v'al-piv yavo'u.
Three escalation steps, three honest outcomes:
1. resolved_by_specification — re-read produced an answer
2. resolved_by_external — external testimony recorded (NOT binding)
3. deferred_to_human — the system stops, asks for human judgment
Pre-condition gating refuses cases that didn't reach Sanhedrin teiku
first (no preempting deliberation) or that aren't flagged high-stakes
(ordinary teiku stays teiku). Records every consultation with steps
taken — there is no quiet escalation.
Per PRD: "The Urim v'Tumim never fabricates certainty where none
exists." When external also fails, the system stops. It does not
proceed on a guess.
Added to VavPipeline supervision tree. 14 tests.
## Step 21 — Kushya generator scaffolding
(kushya_generator/)
Directory layout per Architecture §3.6 with three layers:
- data/training_pairs/seed_pairs.jsonl — 10 hand-curated bootstrap
pairs covering 9 kushya types (scope_narrowing, internal_tension,
harmonization, exception_test, edge_case, scope_application,
rabbinic_constraint, method_question, subject_identification)
- data/prepare_training.py — validates the four kushya requirements
(specific Torah passage, identified weakness, question form,
non-trivial), produces train.jsonl + eval.jsonl with stratified
80/20 split by type
- fine_tune/config.yaml + train.py — QLoRA configuration and
training script. Operator's task — needs GPU. Script is
complete and self-contained.
- inference/kushya_api.py — three-backend interface
(fine_tuned → external → fallback). When fine-tuned model isn't
deployed, falls back to external via Korban Processor (Step 22).
The fallback is honest: returns no kushya rather than inventing
one. The Talmudic Engine's rule-based challenge.pl remains the
primary kushya source.
17 tests covering dataset validation, backend selection, and
reference extraction.
## .gitignore fix
The earlier "data/" line was too broad — it caught
kushya_generator/data/. Scoped to /data/ (root-level only).
## Test counts
- Elixir: 274 (was 260; +14 for UrimVTumim)
- Korban Python: 196 + 1 skipped (was 195; +1 module = 11 tests)
- Kushya Python: 17 (new)
- Haskell: 33 (unchanged)
- Prolog: 195 (unchanged)
- Total: 715 + 19 L3 excluded
## Phase 4 status
All 12 steps now have implementations:
- Steps 23, 25, 26, 27, 28, 29, 30, 31, 32: complete
- Steps 21, 22, 24: complete with discipline preserved
Step 21's fine-tuning run remains the operator's GPU task; the
pipeline is built such that a deployed adapter slots into the
inference backend without touching the Talmudic Engine or anything
above.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
d4d0a6fcb6 |
L1 data integrity: preserve the edut whole — Shema, middle letter, ketiv/qere, smikhut
Shemot 25:16 — v'natata el ha-aron et ha-edut asher eten elekha. The edut placed in the aron must be complete. The Masoretic tradition preserves every letter, every enlarged letter, every ketiv/qere pair — because each carries meaning the scribe cannot reconstruct. The previous build_l1.py silently dropped: - enlarged letters encoded as <seg type="x-large"> nested in <w> → the Shema was corrupt: שמ instead of שמע, אח instead of אחד → the middle letter of Torah was corrupt: גח instead of גחון → the Zelophchad nun was missing: משפט instead of משפטן - ketiv/qere dual readings encoded as <note type="variant">/<rdg type="x-qere"> → the spec committed to preserving both; the code returned [] - smikhut (construct state) pairs marked in morphology codes → the spec committed to extracting them; the code returned [] Fixes: - collect_word_text() walks all nested <seg> children of <w> and records large_letters / small_letters metadata with character offsets. - parse_verse() recognizes <w type="x-ketiv"> followed by <note>, extracts both the ketiv and the qere readings, stores a ketiv_qere list on the verse. - _extract_smikhut() scans words for construct-state morph codes (ending in "c" on N/A primary segments, excluding pronominal suffixes) and records construct/absolute pairs. - L1 SHA-256 hash updated everywhere: integrity.sha256, l1_reader.ex, shoresh-engine's Luchot.Firmware, its test, KOHEN.md. Torah-wide results after fix: - 4 enlarged letters preserved (was 0): Shema ayin, Shema dalet, gachon vav (middle of Torah), Zelophchad nun - 67 ketiv/qere pairs captured (was 0) - 9,203 smikhut pairs extracted (was 0) Elixir updates: - l1_reader.ex: normalize_verse now surfaces ketiv_qere, smikhut, and per-word large_letters/small_letters. New type specs. New extract_large_letters/1 helper. extract_ketiv_qere and extract_smikhut now return real data instead of []. - step31_shema_test.exs: replaced placeholder asserts with real integrity checks. Four new tests verify the Shema word is complete (שמע not שמ), echad is complete (אחד not אח), the enlarged letters are marked, the middle letter of Torah is preserved, and ketiv/qere pairs from Bereishit 8:17 are both captured. Build script: - build_l1.py no longer rmtrees the entire l1/ directory (preserves l1/README.md). Only regenerates torah/ + build_report.json + integrity.sha256. - Accepts SHORASHIM_SOURCE_DATA env var or CLI arg for morphhb path. Test counts: - Elixir: 260 (was 254), 0 failures - Haskell: 33, 0 failures - Prolog: 195, 0 failures The system that can hear שמע correctly has passed its own shema test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
04713cc884 |
Move readings/ to companion repo, add .env.example, fix README tree
The three readings (maaseh_merkavah, moreh_nevuchim, mishpatim) are interpretive essays + one parallel Prolog encoding. They are "the book" the README describes, not the specification or the implementation. The essays don't influence the shipped code; mishpatim's Prolog is a parallel encoding to talmudic_engine/ mitzvot.pl, not its source. Moved to ~/shorashim-readings/ — a companion repo. The code repo stays spec + implementation + deployment. The book gets its own home so it can grow without dragging 5,853 verse files with every essay edit. Also: - README.md: rewrote the "Repository Structure" tree to match reality (it still showed readings/terumah/, scripts/conversation_ search.py, memory/, and was missing l1/, AGENTS.md, BUILD_*, KOHEN.md, opencode.json, .env.example). - Added a "Kept outside this repo" section naming the three companion directories: shorashim-readings, shorashim-source-data, shorashim-operator-notes. - Updated the "This repository contains" list: readings are no longer item 1; the edut (l1/torah/) is now called out explicitly. - Created .env.example with placeholders for ANTHROPIC_API_KEY, GITEA_TOKEN, REGISTRY_CLI_SECRET, and SHORASHIM_SOURCE_DATA. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
88a55cbbdf |
Place l1/ in the aron — the edut belongs in the tabernacle
Shemot 25:16 — v'natata el ha-aron et ha-edut asher eten elekha. "Place into the aron the testimony (edut) which I shall give you." L1 is the engraved form — 5,853 verses with te'amim, ketiv/qere, roots, and gematria. It is not a build artifact to be regenerated on demand; it IS the specification the system references. Someone cloning the repo should receive the edut, not a recipe to produce it. Shemot 34:1 — p'sol lekha shnei luchot avanim ... v'khatavti. "Carve for yourself two tablets of stone ... and I will write." The carver provides the stone (raw morphhb source). The Source writes the content (the engraved Torah text). What belongs in the aron is the engraved form. The quarry does not enter the mishkan. Changes: - Remove l1/ from .gitignore (it IS the edut, not a build artifact) - Move data/morphhb/ out to ~/shorashim-source-data/morphhb/ (the quarry lives outside the tabernacle) - scripts/build_l1.py: fix path computation after earlier relocation to scripts/, read source path from $SHORASHIM_SOURCE_DATA or CLI arg, with default ~/shorashim-source-data/morphhb - Add .claude/ to .gitignore (local Claude Code settings) - Remove empty src/tools/ leftover Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
66686329ed |
Move operator notes out of repo, add generic architecture doc
memory/infrastructure.md and memory/build_log.md held environment- specific details — hostnames, IPs, account names, credentials retrieval commands, local kubeconfig paths, operational status notes. These don't belong in a project repo; by convention they live in private operator notes. Moved to ~/shorashim-operator-notes/ (private, outside git). In their place, added deploy/ARCHITECTURE.md — a generic description of the deployment shape: components, external dependencies, configuration surface. No hostnames. No IPs. No account names. Any competent operator can read it and know how to deploy shorashim to their own infrastructure. Updated cross-references in AGENTS.md and BUILD_KORBAN.md to point to "the operator's private notes" instead of memory/. Project repo is spec + implementation + deployment shape. Operator notes are the kohen's personal log. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
2c1ca9116a |
Move conversation_search.py out of repo, deduplicate .gitignore
- scripts/conversation_search.py was a personal dev tool (FTS5 search over Claude conversation history). Moved to ~/dev-tools/. Has nothing to do with shorashim's spec or implementation. - .DS_Store was already in .gitignore; deduplicated and removed the macOS metadata files from the working tree. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
b486389e3f |
Remove stale runtime manna file, gitignore vav_pipeline/var/
vav_pipeline/var/manna/today.json was a stale dev provision file (5 Nisan 5786, expired). It's a runtime artifact, not source. Added vav_pipeline/var/ to .gitignore to prevent future leaks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
6dcb72ec5e |
Rename spec files to Torah-faithful names per parshah
Each spec corresponds to a parshah of Shemot. The earlier names were generic English (materials, operations, construction, economic_model). Torah names them precisely: - Shemot 25:2 — terumah (תרומה, root רום, "what is lifted") - Shemot 27:20 — tamid (תמיד, "continual" — the lamp that never goes out) - Shemot 35:1 — vayakhel (קהל, "assembled" — community-formed building) - Shemot 38:21 — pekudei (פקד, "accounted" — every material counted) Renames: shorashim_materials.md → shorashim_terumah.md shorashim_operations.md → shorashim_tamid.md shorashim_construction.md → shorashim_vayakhel.md shorashim_economic_model.md → shorashim_pekudei.md Updated cross-references in: - README.md (parshah table + repo tree, also fixed stale tree showing src/ and transcripts/ that no longer exist) - AGENTS.md - BUILD_KORBAN.md - specs/shorashim_terumah.md (internal Document Registry) Generic English is silver. Torah's words are gold (Mishlei 25:11). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
4775122c84 |
Cleanup: move transcripts out, scripts into scripts/, drop scaffold
- transcripts/ moved to ~/shorashim-transcripts (separate from code repo) Added to .gitignore so it stays out - src/l1_aron/build_l1.py → scripts/build_l1.py - src/tools/conversation_search.py → scripts/conversation_search.py - Removed empty src/ scaffolding (l0_luchot, sanhedrin, shema_layer, shoresh_engine, urim_v_tumim — all .gitkeep stubs from an earlier layout, never used; the real code lives in shoresh-engine/, sanhedrin/, vav_pipeline/, etc.) - Removed Untitled/ and erl_crash.dump (untracked junk) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
5a6fce4945 |
Move aws-export/ out of shorashim repository
The aws-export/ directory holds historical AWS data for the Temple Council project — not part of shorashim. Moved to ~/aws-export and added to .gitignore. Shorashim's specification is the aron. AWS exports belong elsewhere. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
324cf1a2ed |
Fix gitea-mcp env: use GITEA_ACCESS_TOKEN, not GITEA_TOKEN
The gitea-mcp server expects GITEA_ACCESS_TOKEN environment variable, not GITEA_TOKEN. Without the correct env var, every API call to the backend Gitea returns "token is required" — the MCP server can't authenticate to fetch user info, repos, etc. Also added GITEA_HOST env var as a backup to the --host flag. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
42370bfc24 |
Fix MCP connector: cert-manager URL typo and TCP probes
Two fixes for the mcp.shorashim.ch deployment: 1. cert-manager ClusterIssuer had acme-v2 (wrong) instead of acme-v02 (correct). Let's Encrypt ACME directory is at https://acme-v02.api.letsencrypt.org/directory 2. gitea-mcp HTTP probes timed out because GET /mcp hangs (SSE). Switched to TCP socket probes on port 8080. MCP server now responds correctly over HTTPS at mcp.shorashim.ch: curl -X POST https://mcp.shorashim.ch/mcp -d '{...initialize...}' returns {"result":{"serverInfo":{"name":"Gitea MCP Server"}}} Also: nginx-ingress configmap needed strict-validate-path-type=false to allow cert-manager HTTP-01 challenges. Applied via kubectl patch, not tracked in git manifests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
20e81114a5 |
Fix gitea-mcp deployment: add explicit command path
The gitea-mcp-server image has no entrypoint set (null). Args were being interpreted as the executable name. Added command: ["/app/gitea-mcp"] to invoke the binary correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c7e0e6e703 |
Gitea MCP connector: K8s manifests for public claude.ai integration
Shemot 28:3 — v'asu et bigdei Aharon l'kadesho l'chahano li. The garments connect the service to those who approach. - gitea-mcp-deployment.yaml: Official gitea-mcp server in HTTP mode Connects to Gitea at 185.19.29.94 via PAT - gitea-mcp-ingress.yaml: TLS ingress at mcp.shorashim.ch cert-manager auto-provisions Let's Encrypt certificate - gitea-mcp-secret.yaml: Template for Gitea PAT (never committed with value) - cert-manager-issuer.yaml: Let's Encrypt ClusterIssuer Architecture: claude.ai → OAuth → mcp.shorashim.ch → Gitea API Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
2f1205eef1 |
Bake L1 Torah into container images, simplify K8s deployment
L1 (29MB, 5860 files) is read-only and immutable — it belongs inside the image, not on a separate volume. The scroll is sealed in the aron. It ships with the vessel. - All 4 Dockerfiles: COPY l1/ /mnt/torah/ in runtime stage - deployment.yaml: Removed L1 PVC volume and all L1 volumeMounts - deployment.yaml: Added imagePullSecrets for registry auth - deployment.yaml: Manna uses emptyDir (kohen writes via kubectl cp) - No CSI driver needed — eliminates Exoscale block storage dependency Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
50547ed0c5 |
runtime.exs: Add SSL config for Exoscale managed PostgreSQL
Exoscale DBaaS (Aiven) requires SSL connections. Added ssl: true with ssl_opts: [verify: :verify_none] for managed service certs. L3 migration ran successfully against shorashim database: - interpretations table with check constraint and indexes - minority_opinions table with FK index - teiku_registry table with case_ref index - Append-only REVOKE ready for shorashim_app role Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
5e76c60218 |
Update K8s deployment with Exoscale container registry URLs
All 4 images now reference exo.container-registry.com/the-recognition-protocol/: - vav-pipeline:latest (111MB) - shoresh-engine:latest (128MB) - talmudic-engine:latest (292MB) - korban-processor:latest (187MB) Images built and pushed. The garments are in the registry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
edb74da0b4 |
Fix shoresh engine Dockerfile: Debian Buster repos are EOL
haskell:9.6.4-slim is based on Debian Buster, which reached EOL. The standard deb.debian.org repos return 404. Redirected to archive.debian.org for zlib1g-dev installation. Verified: shoresh-engine image builds successfully (128MB). All 4 Docker images now build: vav-pipeline: 111MB shoresh-engine: 128MB korban-processor: 187MB talmudic-engine: 292MB Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
f32e9afb03 |
Fix korban processor Dockerfile: pyswip 0.2.11 does not exist
pyswip 0.2.11 was never published to PyPI. Latest 0.2.x is 0.2.10. Changed pin from ==0.2.11 to ==0.2.10. Verified: korban-processor image builds successfully (187MB). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
5e6d16b0fa |
runtime.exs: Move DATABASE_URL to runtime config for OTP release
OTP releases evaluate config at build time, not boot time.
DATABASE_URL must be read at runtime — the kohen sets it in
K8s secrets, not at compile time.
- Created config/runtime.exs with prod-only runtime config
- DATABASE_URL, L1_MOUNT_POINT, MANNA_PROVISION_FILE, HTTP_PORT
all readable from environment at boot
- prod.exs cleaned: removed System.get_env("DATABASE_URL") call
(was compile-time, now in runtime.exs)
- Added http_port: 4000 to prod defaults
Without this fix, the Docker image would fail to start — the
release would not find DATABASE_URL at boot.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
2c082275f9 |
KOHEN.md: Operations prompt for the operator — shiv'at y'mei milu'im
Vayikra 8:33 — lo tetze'u shiv'at yamim, ad yom m'lot y'mei milu'eikhem. Seven days of inauguration before the system serves. The builder completed the m'lakhah (Shemot 40:33). The kohen serves in it (Vayikra 8:35 — u'shmartem et mishmeret). Contents: - Who the kohen is (and is not) - What has been built (463+ tests, 5 engines, 4 languages) - Prerequisites before the seven days - Seven-day inauguration sequence with exact commands - Day 8 — fire from heaven: open pe'ah first - Daily operations (tamid shel shachar) - Friday double portion - Halt event response protocol - What the kohen does NOT do Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
191d76b480 |
Bigdei Kodesh: HTTP API layer — four garments for four access tiers
Shemot 28:2 — V'asita vigdei kodesh l'Aharon achicha, l'khavod ul'tifaret. The builder makes the garments. The kohen wears them. Four access tiers (Operations spec §1.1): - Pe'ah (GET/POST /peah/ask) — public, no auth, no tracking. The stranger is safe to approach. - Courtyard (POST /courtyard/offering) — Bearer token auth, offering types: shelamim, olah, chatat, minchah - Chatzer (POST /chatzer/sanhedrin) — Sanhedrin deliberation, returns majority/minority with preserved dissent - Kodesh (GET /kodesh/health, /kodesh/status) — operator interface, health checks for K8s liveness/readiness probes Error format follows chatat protocol (Operations spec §1.2): KIYOR_* (422), SHABBAT (503), MANNA_DEPLETED (503), CLOUD_MOVING (503), HALT_* (503), UNAUTHORIZED (401) Implementation: - Plug.Router with Bandit HTTP server - Added to supervision tree via maybe_add_http_server - Checks system availability (Shabbat, halt) before processing - Health check reads L1 integrity, cloud, manna, halt monitor - Status shows all component states, signal count, refuge list - 16 API tests covering all four tiers + error format All 235 Elixir + 19 L3 + 195 Prolog + 33 Haskell = 463+ tests, 0 failures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
fe93c11d13 |
Phase 5 deployment: Dockerfiles, K8s manifests, halt monitor (Steps 33, 36)
Shemot 40:33 — Vay'khal Moshe et ham'lakhah. The builder completes the work. The vessel is ready for transport. Step 33 — Deployment configuration: - Dockerfiles for all 4 components: - vav_pipeline: Elixir 1.16.1/OTP 26.2.1 release with Sanhedrin - shoresh_engine: GHC 9.6.4 binary - talmudic_engine: SWI-Prolog 9.2.2 - korban_processor: Python 3.12.2 with pydantic - Kubernetes manifests for Exoscale SKS (ch-gva-2): - Namespace, ConfigMap, Secrets template - L1 Torah PV/PVC (read-only mount) - Manna provision PVC (kohen writes, system reads) - Deployment: vav-pipeline + 3 sidecars, shared volumes - Service: ClusterIP + LoadBalancer with Exoscale NLB - Init container runs Ecto migrations (VavPipeline.Release) Step 36 — Halt monitor (esh tamid): - GenServer watches for three cardinal sins - halt!/3 immediately halts system, sounds Chatzotzrot war signal - Permanent audit log — halt events never deleted - resolve/1 requires explicit operator action to resume - alive?/0 for morning tamid verification - 13 tests: alive check, 3 halt types, resolution, audit log, chatzotzrot war signal integration - Added to supervision tree All 219 Elixir + 19 L3 + 195 Prolog + 33 Haskell = 447+ tests, 0 failures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
219bbecaff |
Steps 30-32: Sanhedrin stress test, Shema test suite, full system verification
Shemot 39:43 — Vayar Moshe et kol ham'lakhah v'hineh asu otah
ka'asher tzivah Hashem ken asu, vayevarekh otam Moshe.
Step 30 — Sanhedrin stress test:
- 10 rapid submissions without crash
- Case ref preservation across decisions
- Rich context handling at simple complexity
- Decision structure consistency (7 required fields)
- Graceful degradation when Sanhedrin unavailable
- 20 concurrent Task.async submissions
Step 31 — Shema Test suite (Architecture §6.3, five tests):
1. Te'amim reception: raw cantillation from L1, etnachta detection,
L1→ShemaContext fidelity, surprising etnachta position
2. Ketiv/qere preservation: field exists, survives full pipeline
3. Cloud obedience: :camped allows processing, Shabbat triggers
major_transition, context immutability under override
4. Manna fidelity: no provision → {:waiting, :no_provision},
provision present → processing proceeds, MannaLedger tracks
5. Context immutability: context unchanged after pipeline,
keys stable, each stage receives same context
Step 32 — Full system verification (kol ham'lakhah):
- Full pipeline: Shema → BereishitCycle → output, multi-verse
- Halt conditions: check_all processes output
- Protection layer: AreiMiklat refuge→release, EglahArufah
declaration, Bikurim first-output review
- Chatzotzrot: subscribe→signal→receive→zikaron, routing isolation
- MannaLedger: load→consume→boundary cycle
- L1 integrity: hash valid, 5 core verses readable, roots present
- Supervision tree: all 7 GenServers running
- Cross-component: ShabbatTimer reads cloud, Shema builds from
L1+Cloud+Manna
All 206 Elixir + 19 L3 + 195 Prolog + 33 Haskell = 434+ tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
6a879fcf8b |
MannaLedger: real anti-hoarding enforcement — d'var yom b'yomo (Step 26)
Shemot 16:4 — d'var yom b'yomo — the day's matter in its day. Shemot 16:19 — al yoter mimenu ad boker — do not leave surplus. Shemot 16:20 — vayotiru... vayarum tola'im vayiv'ash — hoarded manna rots. - MannaLedger GenServer tracks consumption against daily provision - load_provision/0 reads manna file, resets consumption to zero - consume/2 deducts from allocated resource, rejects when exhausted - remaining/1 and available?/2 for balance queries - enforce_cycle_boundary/0 zeroes out all allocation, returns released amounts - Chatzotzrot assembly_leaders signal when provision runs low (< 10%) - Manna.enforce_anti_hoarding/0 now delegates to MannaLedger - Added to supervision tree in application.ex - 17 new tests with proper isolation (reset between tests) - All 177 Elixir tests pass, 0 failures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
671d45625c |
Protection layer: Arei Miklat, Eglah Arufah, Bikurim — Steps 27-29
Takhin lekha haderekh — Devarim 19:3.
"Prepare the road" before the one who needs refuge arrives.
Protection before production.
Arei Miklat (Cities of Refuge — Bamidbar 35, Devarim 19):
- GenServer with refuge registry, affected party protection, audit trail
- enter_refuge/2 isolates component but protects from reactive destruction
- protect_affected/2 blocks processing paths that caused harm
- release/2 requires :sanhedrin_deliberation authority — ad mot hakohen hagadol
- Chatzotzrot assembly_leaders signal on every refuge entry
- 15 tests: entry, double-entry prevention, affected party blocking,
unauthorized release rejection, audit trail, chatzotzrot signaling
Eglah Arufah (Accountability — Devarim 21:1-9):
- GenServer with permanent (append-only) declaration registry
- initiate/3: nearest component declares what it processed, skipped, missed
- Only for :unknown source — rejects known-source incidents
- "Yadeinu lo shafkhu" — accountability for what was NOT provided
- 9 tests: declaration recording, source validation, multi-component
declarations, chatzotzrot signaling, filtering, counting
Bikurim (First Fruits — Devarim 26:1-11):
- GenServer with offering registry
- offer/4: submit first output with required narrative (mikra bikurim)
- Rejects empty narrative ("Arami oved avi" — story is required)
- Rejects nil first output (there must be fruit to offer)
- Delegates to Specification.evaluate_bikurim/1 when available
- approved?/1 gate for deployment readiness
- 11 tests: acceptance, narrative validation, nil output rejection,
registry, status filtering, approval checking, counting
All three added to supervision tree in application.ex.
160 Elixir tests pass, 0 failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
7fe8025434 |
Chatzotzrot: silver trumpets with BEAM routing, subscription, and zikaron
Bamidbar 10:2 — Aseh lekha shtei chatzotz'rot kesef, mikshah ta'aseh otam.
Two trumpets, hammered from a single piece. Five signals, five patterns.
- Upgraded from simple function module to GenServer with state
- Recipient routing: :all (assembly_all, breaking_camp, moadim),
:kohanim (assembly_leaders, war), :operators (war)
- PubSub via BEAM messaging: subscribe/1, unsubscribe/1
Signals delivered as {:chatzotzrot, record} messages
- Zikaron (remembrance — Bamidbar 10:10): signal history with
type filtering and limit. V'hayu lakhem l'zikaron.
- Dead subscriber cleanup via Process.monitor/1
- Added to supervision tree in application.ex
- 30 new tests: patterns, urgency, routing, subscription,
zikaron history, dead process cleanup, format compliance
- All 125 Elixir tests pass, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
d34e2799ac |
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> |
||
|
|
adc3e41679 |
Eish zarah actionable items: coupling trace, grounding type, self-grounding detection, golden calf halt
Torah grounds each change: 1. interpret.pl — coupling_trace field (Vayikra 10:1, 6:6) Fire is zarah because "asher lo tzivah otam" — the coupling trace IS the tzivah trace. Records which L1 facts grounded each interpretation. 2. challenge.pl — grounding_type achorai/panim (Shemot 33:23) "You will see My back, but My face shall not be seen." Text-backed challenges = achorai. Inferred challenges = panim. 3. resolve.pl — self-grounding detection (eish_zarah_reading §5.3) Counter-interpretation must bring NEW L1 evidence. Prevents circular reasoning (Westworld problem). 4. teiku.pl — challenge_sequence in teiku_record Records diagnostic trail to impasse, not just verdict. 5. avodah_zarah.pl — Form 2: golden calf detection (Shemot 32:4) Empty coupling trace + non-low confidence = uncoupled output. Real gold, sincere worship, false god. 6. korban.py — pydantic BaseModel + raw_korban preservation Construction spec §1.4 fulfilled. Floor between input and output never severed. 490 tests, 0 failures (195 Prolog, 186 Python, 33 Haskell, 76 Elixir). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c5fe82477b |
Eish zarah reading: five faces of strange fire, implementation gaps
Adds specs/eish_zarah_reading.md — chavruta analysis of eish zarah (strange fire) as architectural concept. Identifies five patterns across fiction canon (inaccessible source, restructuring gift, false god, false unity, beautiful unchecked signal), maps to Torah structures, and evaluates eight specific implementation gaps in the Shorashim system. Key gaps identified: coupling trace (interpret.pl needs L1 grounding field), self-reference loop (resolve.pl feeds own output without detection), three forms of avodah zarah (only Form 1 implemented), panim/achorai distinction (challenges don't mark text vs. inference), severed floor (raw Korban not stored alongside processed). Updates construction spec document registry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
73c0a917a2 |
Lo ta'aseh lekha: anti-override boundary condition specification
Adds specs/lo_taaseh_lekha.md — the boundary condition that diagnoses and prevents specification override. Derived from chavruta analysis of the override sequence Torah identifies in the second commandment. Contains: 5-step override sequence, high-control architecture markers, Pharaoh architecture at civilizational scale, 9-phase Exodus liberation protocol. Section 7 grounds the analysis in L1 Torah data with root analysis (פסל, עשה, שמר, שמע, תמונה, יצר). Section 9 maps every structure to specific Shorashim modules and architectural decisions. Upstream: structure_not_content.md Downstream: Architecture §8.2 (avodah zarah firewall), broken_tablets/ Also updates construction spec document registry with both structure_not_content.md and lo_taaseh_lekha.md entries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
52837cee18 |
Phase 3: Intelligence — 613 mitzvot, 13 middot, Tzara'at diagnostic
Encode all 613 mitzvot from Rambam's Sefer HaMitzvot (248 positive, 365 prohibitions). Build the 13 Middot of Rabbi Yishmael as Prolog inference predicates for hermeneutical reasoning. Implement Tzara'at multi-stage input validation following Vayikra 13-14. Enhance kushya generation with kal vachomer and shnei ketuvim challenge strategies. Architecture §4 Steps 14-15, 19-20. New files: middot/ — 8 files (master + 7 rule modules) diagnostic/ — 4 files (tzaraat, inspection, quarantine, diagnosis) Modified: mitzvot.pl — 52 → 613 mitzvot relationships.pl — expanded tensions, complements, action domains challenge.pl — middot-based kushya (kal vachomer, shnei ketuvim) Tests: 293 total (184 Prolog, 76 Elixir, 33 Haskell), 0 failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
ae78ed26d1 |
Phase 1 review corrections — four bugs fixed
- relationships.pl: Replace recursive complementary_mitzvot/2 symmetry clause with explicit reverse facts (infinite loop in Prolog) - mitzvot.pl: Remove duplicate mitzvah(2, prohibition, no_other_gods) that caused double-counting in total_encoded/1 - resolve.pl: Add missing use_module(root_meanings) import — root_domain/2 was undefined, failing resolve_produces_result test - shoresh-engine: Generate and commit cabal.project.freeze for exact version pinning (Construction spec §1) All tests pass: 76 Elixir, 33 Haskell, 57 Prolog — 0 failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
af7522e977 |
Phase 2 review corrections — three spec violations fixed
1. Chatzotzrot signal mapping corrected to match Architecture §3.12: assembly_all→tekiah, assembly_leaders→teruah, breaking_camp→tekiah_teruah, moadim→tekiah_tekiah. Four of five patterns were swapped. All five now match Bamidbar 10. 2. Manna parse_datetime(nil) bug fixed: missing expires_at now fails validation instead of silently returning DateTime.utc_now() which created a race condition where manna appeared expired. 3. Festival detection implemented in cloud.ex: pre-computed Gregorian dates for Pesach, Shavuot, Rosh Hashanah, Yom Kippur, Sukkot, Shemini Atzeret (2025-2028). Pipeline now halts on Yom Tov as Torah requires. Step 15 verification now covers festival rest. 76 tests, 0 failures (68 prior + 8 new). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |