85155b23fb437aa78d54f5d8defb5ae2e7b1e076
1
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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>
|