Files
bikoret/azazel_processor
Scott KarpandClaude Opus 4.7 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>
2026-04-27 18:49:34 -04:00
..