Skip to content

Tutorial: Handling Machloket

This tutorial teaches you to encode halachic disputes (machloket) between authorities using Mistaber's multi-world architecture and override mechanism. You will encode the famous dispute between the Mechaber and Rema regarding fish and dairy (YD 87:3).

Duration: 2 hours Difficulty: Intermediate Prerequisites: Tutorial 01: Your First Encoding Target: YD 87:3 (Fish and Dairy Dispute)

What You Will Encode

The dispute regarding fish cooked with dairy is one of the most interesting machloket in Yoreh Deah:

  • Mechaber (Sefardi): Fish with dairy is forbidden due to health danger (sakana)
  • Rema (Ashkenazi): Fish with dairy is permitted (no sakana concern)

This dispute is perfect for learning machloket encoding because:

  • Clear disagreement: Two explicit opposing positions
  • Different reasoning: Sakana vs. no halachic basis for concern
  • World inheritance: Child worlds inherit their parent's position
  • Override mechanics: Rema must override Mechaber's position

Understanding the Dispute

The Mechaber's Position

The Mechaber (R' Yosef Karo), based on medical authorities cited in the Beit Yosef, holds that cooking fish with milk poses a health danger (sakana):

"Fish with dairy is dangerous to one's health and should be avoided."

Key Points:

  • This is sakana (health danger), not issur (halachic prohibition)
  • Sakana is treated more stringently than regular issur in some respects
  • Based on medical understanding of his era (16th century)

Sources:

  • Shulchan Aruch YD 87:3 (by implication)
  • Beit Yosef on Tur YD 87 (citing medical texts)

The Rema's Position

The Rema (R' Moshe Isserles) does not gloss on the Mechaber's statement about fish and dairy. In halachic literature, significant silence often indicates disagreement. The Ashkenazi tradition permits fish with dairy.

Key Points:

  • No Talmudic source for fish + dairy sakana
  • Ashkenazi practice is to permit (lox and cream cheese, etc.)
  • Later poskim (Taz, Shach) provide explicit justification

Sources:

  • Rema's silence on SA YD 87:3
  • Taz YD 87:3 (scribal error argument)
  • Shach YD 87:5 (conditional interpretation)

The Taz's Revolutionary Interpretation

The Taz argues the entire sakana ruling is based on a scribal error:

  1. The Gemara (Pesachim 76b) discusses fish with meat being dangerous
  2. A copyist mistakenly wrote "milk" (chalav) instead of "meat" (basar)
  3. This error propagated through subsequent texts
  4. There is no authentic source for fish + dairy being dangerous

The Shach's Conditional Position

The Shach takes a middle ground: even according to the Mechaber, the sakana only applies to fish cooked together with dairy, not cold mixtures.

The Override Mechanism

Before encoding, understand how Mistaber handles disputes:

World Inheritance

graph TD
    subgraph "Without Override"
        M1[mechaber asserts sakana] --> S1[sefardi_yo inherits sakana]
    end

    subgraph "With Override"
        M2[mechaber asserts sakana] -.->|blocked| R2[rema: override → no_sakana]
        R2 --> MB2[ashk_mb inherits no_sakana]
        R2 --> AH2[ashk_ah inherits no_sakana]
    end

The override/3 Predicate

Syntax: override(World, OverriddenProp, DescriptiveValue)

The third argument must be a descriptive value indicating the new position:

% CORRECT - descriptive value
override(rema, sakana(M), no_sakana) :-
    is_dag_chalav_mixture(M).

% WRONG - never use 'invalid'
override(rema, sakana(M), invalid) :-    % DO NOT DO THIS
    is_dag_chalav_mixture(M).

Critical: Never Use 'invalid' in Override/3

The third argument describes what the child world holds instead. Use meaningful terms:

  • no_sakana (not invalid)
  • permitted (not false)
  • lenient_ruling (not null)

Step-by-Step Encoding

Step 1: Start the Session

User: "Prepare corpus for YD 87:3 - fish and dairy machloket"

The corpus-prep skill will identify this as a machloket and provide both positions in the report.

Step 2: Review the Corpus Report

Corpus Report: Fish & Dairy Machloket
# Corpus Report: YD 87:3 (Fish & Dairy)

## MACHLOKET IDENTIFIED

This seif contains a **documented machloket** between:
- Mechaber (Sefardi): Forbidden due to sakana
- Rema (Ashkenazi): Permitted

## Primary Source: Mechaber

**SA YD 87:3** (relevant excerpt):
> "Dagim v'chagavim ein bahem issur afilu miderabanan"
> (Fish and locusts have no [basar bechalav] prohibition even rabbinically)

**Beit Yosef on Tur YD 87**:
> Fish with milk is dangerous and should be avoided due to sakana.

## Rema's Position

**No explicit gloss** on the dairy aspect of this seif.

Later Ashkenazi poskim clarify this silence indicates permission.

## Commentary Classification

| Commentary | Position | Notes |
|------------|----------|-------|
| Taz | Permits | Scribal error argument |
| Shach | Conditional | Only if cooked together |
| Pri Megadim | Follows Rema | Standard Ashkenazi |

## Derivation Chain: Mechaber

```mermaid
graph TD
    Medical["Medical texts<br/>(cited by Beit Yosef)"] --> BY
    BY["Beit Yosef YD 87"] --> SA
    SA["SA YD 87:3 (implied)"]

Derivation Chain: Rema

graph TD
    NoSource["No Talmudic source<br/>for fish+dairy sakana"] --> Silence
    Silence["Rema's silence"] --> Practice
    Practice["Ashkenazi practice permits"]
    Taz["Taz: Scribal error"] --> Practice
    Shach["Shach: Conditional only"] --> Practice

Complexity Score: 7/10

  • Explicit machloket requiring both encodings
  • Override mechanism required
  • Child world inheritance verification needed
  • Multiple source layers

Questions for Human Review

  1. Should Mechaber's position be encoded as sakana (health) or issur (prohibition)?
  2. [Default: sakana - as this is the Mechaber's basis]

  3. Should the Taz's scribal error interpretation be encoded as an interpretation layer, or is Rema's override sufficient?

  4. [Default: Rema's override sufficient for core encoding; Taz as interpretation layer for future enrichment] ```

Step 3: Approve Corpus with Answers

User: "Approved. Answers:
1. Encode as sakana (health concern), which then implies issur achiila.
2. Rema's override is sufficient. Taz can be added later as interpretation."

Step 4: Encode the Mechaber's Position

After corpus approval, run encoding:

User: "Encode the rules"

The first encoded position is the Mechaber's:

% ============================================================================
% YD 87:3 - DAG (FISH) + CHALAV: SAKANA (MECHABER'S POSITION)
% ============================================================================
% The Mechaber considers fish with milk to be forbidden due to sakana
% (health danger), based on medical authorities cited in Beit Yosef.
%
% While SA YD 87:3 states fish has no basar bechalav issur, the Mechaber's
% position from Beit Yosef treats fish+milk as forbidden due to sakana.
%
% DISPUTANTS:
%   - Mechaber: sakana (forbidden)
%   - Rema: permitted (overrides)
%
% AFFECTED WORLDS:
%   - mechaber → sefardi_yo: inherit sakana
%   - rema → ashk_mb, ashk_ah: inherit permission

% === Mechaber's Position ===
rule(r_bb_dag_sakana).
makor(r_bb_dag_sakana, sa("yd:87:3")).
makor(r_bb_dag_sakana, beit_yosef("yd:87")).
scope(r_bb_dag_sakana, mechaber).

% Assert sakana (health danger) for fish + dairy
asserts(mechaber, sakana(M)) :-
    is_dag_chalav_mixture(M).

% Derive issur from sakana
% The action is forbidden because it poses health danger
asserts(mechaber, issur(achiila, M, sakana)) :-
    is_dag_chalav_mixture(M).

Key Points:

  • Rule ID is semantic: r_bb_dag_sakana
  • Multiple makor citations (SA and Beit Yosef)
  • Scope is explicitly mechaber
  • Two assertions: sakana(M) and issur(achiila, M, sakana)

Step 5: Encode the Rema's Override

The Rema's position requires the override mechanism:

% ============================================================================
% YD 87:3 - DAG (FISH) + CHALAV: PERMITTED (REMA'S OVERRIDE)
% ============================================================================
% The Rema and Ashkenazi poskim permit fish with dairy.
% Rema's silence indicates disagreement with Mechaber's sakana ruling.
%
% Later support:
% - Taz: The sakana ruling is a scribal error
% - Shach: At most, only applies if cooked together
%
% This OVERRIDES the Mechaber's sakana ruling in Ashkenazi worlds.

% === Rema's Override Position ===
rule(r_rema_dag_chalav_mutar).
makor(r_rema_dag_chalav_mutar, rema("yd:87:3")).
makor(r_rema_dag_chalav_mutar, taz("yd:87:3")).
makor(r_rema_dag_chalav_mutar, shach("yd:87:5")).
scope(r_rema_dag_chalav_mutar, rema).

% === OVERRIDE MECHANISM ===
% Block inheritance of sakana from mechaber
% Third argument is DESCRIPTIVE VALUE (never 'invalid')
override(rema, sakana(M), no_sakana) :-
    is_dag_chalav_mixture(M).

% Block inheritance of issur based on sakana
override(rema, issur(achiila, M, sakana), permitted) :-
    is_dag_chalav_mixture(M).

% === POSITIVE ASSERTIONS ===
% Explicitly assert the permissive position

% Eating fish with dairy is permitted
asserts(rema, heter(achiila, M)) :-
    is_dag_chalav_mixture(M).

% Cooking fish with dairy is permitted
asserts(rema, heter(bishul, M)) :-
    is_dag_chalav_mixture(M).

% Benefit from fish + dairy is permitted
asserts(rema, heter(hanaah, M)) :-
    is_dag_chalav_mixture(M).

% Explicitly assert no sakana
asserts(rema, no_sakana(M)) :-
    is_dag_chalav_mixture(M).

% === MACHLOKET MARKER ===
% Document this dispute for discoverability
machloket(dag_chalav_sakana, mechaber, rema, M) :-
    is_dag_chalav_mixture(M).

Key Points:

  1. Override with descriptive value: no_sakana, not invalid
  2. Both overrides needed: Block both sakana and issur(...sakana)
  3. Positive assertions: Don't just block; assert what IS true
  4. Machloket marker: Use machloket/4 for discoverability

Step 6: Verify World Inheritance

The encoding must correctly propagate through child worlds:

graph TD
    subgraph "Mechaber Branch"
        M[mechaber<br/>sakana, issur] --> SY[sefardi_yo<br/>inherits sakana, issur]
    end

    subgraph "Rema Branch"
        R[rema<br/>override: no_sakana, heter] --> MB[ashk_mb<br/>inherits no_sakana, heter]
        R --> AH[ashk_ah<br/>inherits no_sakana, heter]
    end

    style M fill:#ffcdd2
    style SY fill:#ffcdd2
    style R fill:#c8e6c9
    style MB fill:#c8e6c9
    style AH fill:#c8e6c9
World Inherits From Fish + Dairy Status
mechaber base sakana (forbidden)
sefardi_yo mechaber Inherits sakana
rema base (with override) no_sakana (permitted)
ashk_mb rema Inherits heter
ashk_ah rema + gra Inherits heter
gra base Likely follows Rema (verify)

Step 7: Write Multi-World Tests

Testing machloket requires verifying both positions and their inheritance:

"""Tests for fish+dairy machloket between Mechaber and Rema."""
import pytest
from mistaber import query


class TestFishDairyMachloket:
    """Comprehensive tests for the fish+dairy dispute."""

    @pytest.fixture
    def fish_dairy_mixture(self):
        """Standard fish + dairy mixture for testing."""
        return """
            mixture(m1).
            food(salmon). food_type(salmon, dag).
            food(cream). food_type(cream, chalav).
            contains(m1, salmon). contains(m1, cream).
        """

    # =========================================================
    # MECHABER POSITION TESTS
    # =========================================================

    def test_mechaber_asserts_sakana(self, fish_dairy_mixture):
        """Mechaber holds fish+dairy is sakana (health danger)."""
        result = query(fish_dairy_mixture, world="mechaber")
        assert result.holds("sakana(m1)")

    def test_mechaber_forbids_eating(self, fish_dairy_mixture):
        """Mechaber forbids eating fish+dairy due to sakana."""
        result = query(fish_dairy_mixture, world="mechaber")
        assert result.holds("issur(achiila, m1, sakana)")

    def test_mechaber_no_heter(self, fish_dairy_mixture):
        """Mechaber does NOT assert heter for fish+dairy."""
        result = query(fish_dairy_mixture, world="mechaber")
        assert not result.holds("heter(achiila, m1)")

    def test_sefardi_yo_inherits_sakana(self, fish_dairy_mixture):
        """Sefardi YO (child of mechaber) inherits sakana."""
        result = query(fish_dairy_mixture, world="sefardi_yo")
        assert result.holds("sakana(m1)")
        assert result.holds("issur(achiila, m1, sakana)")

    def test_sefardi_yo_no_heter(self, fish_dairy_mixture):
        """Sefardi YO does NOT permit fish+dairy."""
        result = query(fish_dairy_mixture, world="sefardi_yo")
        assert not result.holds("heter(achiila, m1)")

    # =========================================================
    # REMA POSITION TESTS
    # =========================================================

    def test_rema_no_sakana(self, fish_dairy_mixture):
        """Rema holds no sakana for fish+dairy."""
        result = query(fish_dairy_mixture, world="rema")
        assert result.holds("no_sakana(m1)")
        assert not result.holds("sakana(m1)")

    def test_rema_permits_eating(self, fish_dairy_mixture):
        """Rema permits eating fish+dairy."""
        result = query(fish_dairy_mixture, world="rema")
        assert result.holds("heter(achiila, m1)")

    def test_rema_permits_cooking(self, fish_dairy_mixture):
        """Rema permits cooking fish+dairy."""
        result = query(fish_dairy_mixture, world="rema")
        assert result.holds("heter(bishul, m1)")

    def test_rema_permits_benefit(self, fish_dairy_mixture):
        """Rema permits benefit from fish+dairy."""
        result = query(fish_dairy_mixture, world="rema")
        assert result.holds("heter(hanaah, m1)")

    def test_rema_no_issur(self, fish_dairy_mixture):
        """Rema does NOT assert issur for fish+dairy."""
        result = query(fish_dairy_mixture, world="rema")
        assert not result.holds("issur(achiila, m1, sakana)")

    # =========================================================
    # CHILD WORLD INHERITANCE TESTS
    # =========================================================

    def test_ashk_mb_inherits_heter(self, fish_dairy_mixture):
        """Mishnah Berurah (child of rema) inherits permission."""
        result = query(fish_dairy_mixture, world="ashk_mb")
        assert result.holds("heter(achiila, m1)")
        assert result.holds("no_sakana(m1)")

    def test_ashk_mb_no_sakana(self, fish_dairy_mixture):
        """Mishnah Berurah does NOT inherit sakana."""
        result = query(fish_dairy_mixture, world="ashk_mb")
        assert not result.holds("sakana(m1)")

    def test_ashk_ah_inherits_heter(self, fish_dairy_mixture):
        """Aruch HaShulchan (child of rema+gra) inherits permission."""
        result = query(fish_dairy_mixture, world="ashk_ah")
        assert result.holds("heter(achiila, m1)")

    def test_ashk_ah_no_sakana(self, fish_dairy_mixture):
        """Aruch HaShulchan does NOT inherit sakana."""
        result = query(fish_dairy_mixture, world="ashk_ah")
        assert not result.holds("sakana(m1)")

    # =========================================================
    # MACHLOKET DETECTION TESTS
    # =========================================================

    def test_machloket_marked(self, fish_dairy_mixture):
        """The machloket is properly documented."""
        result = query(fish_dairy_mixture, world="base")
        assert result.holds("machloket(dag_chalav_sakana, mechaber, rema, m1)")

    def test_cross_world_divergence(self, fish_dairy_mixture):
        """Mechaber and Rema diverge on this issue."""
        mechaber_result = query(fish_dairy_mixture, world="mechaber")
        rema_result = query(fish_dairy_mixture, world="rema")

        # Opposite positions
        assert mechaber_result.holds("sakana(m1)")
        assert not rema_result.holds("sakana(m1)")

        assert mechaber_result.holds("issur(achiila, m1, sakana)")
        assert rema_result.holds("heter(achiila, m1)")

    # =========================================================
    # EDGE CASES
    # =========================================================

    def test_only_fish_no_mixture(self, fish_dairy_mixture):
        """Fish alone (no dairy) has no sakana."""
        fish_only = """
            food(salmon). food_type(salmon, dag).
        """
        result = query(fish_only, world="mechaber")
        assert not result.holds("sakana(_)")

    def test_only_dairy_no_mixture(self, fish_dairy_mixture):
        """Dairy alone (no fish) has no sakana."""
        dairy_only = """
            food(cream). food_type(cream, chalav).
        """
        result = query(dairy_only, world="mechaber")
        assert not result.holds("sakana(_)")


@pytest.mark.parametrize("world,expect_sakana", [
    ("mechaber", True),
    ("sefardi_yo", True),
    ("rema", False),
    ("ashk_mb", False),
    ("ashk_ah", False),
])
def test_fish_dairy_sakana_by_world(fish_dairy_mixture, world, expect_sakana):
    """Parameterized test: sakana status varies by world."""
    result = query(fish_dairy_mixture, world=world)

    if expect_sakana:
        assert result.holds("sakana(m1)"), f"{world} should have sakana"
    else:
        has_no_sakana = (result.holds("no_sakana(m1)") or
                         not result.holds("sakana(m1)"))
        assert has_no_sakana, f"{world} should NOT have sakana"

Step 8: Run Validation

User: "Run validation"

Expected output:

=== Compilation ===
SUCCESS: All files compile without errors

=== Semantic Checks ===
[PASS] Override values are descriptive
[PASS] Both positions have makor citations
[PASS] Machloket is marked with machloket/4

=== Test Results ===
20 tests collected
20 passed

=== Multi-World Verification ===
[PASS] mechaber: sakana asserted
[PASS] sefardi_yo: sakana inherited
[PASS] rema: sakana overridden
[PASS] ashk_mb: heter inherited
[PASS] ashk_ah: heter inherited

NO REGRESSIONS DETECTED

Step 9: Complete Review and Commit

User: "Prepare review"

Complete both checklists, then:

User: "Checkpoint 4 approved. Machloket encoding is complete."
User: "Commit the encoding"

Common Machloket Encoding Mistakes

Mistake 1: Forgetting Positive Assertions

% WRONG - only overrides, no positive assertion
override(rema, sakana(M), no_sakana) :-
    is_dag_chalav_mixture(M).
% What does Rema actually hold? Unclear!

% CORRECT - override PLUS positive assertion
override(rema, sakana(M), no_sakana) :-
    is_dag_chalav_mixture(M).
asserts(rema, heter(achiila, M)) :-  % Explicitly assert permission
    is_dag_chalav_mixture(M).
asserts(rema, no_sakana(M)) :-       % Explicitly assert no sakana
    is_dag_chalav_mixture(M).

Mistake 2: Using 'invalid' in Override

% WRONG - invalid is not a descriptive value
override(rema, sakana(M), invalid) :-
    is_dag_chalav_mixture(M).

% CORRECT - use meaningful descriptive value
override(rema, sakana(M), no_sakana) :-
    is_dag_chalav_mixture(M).

Mistake 3: Missing Override for Derived Propositions

% PARTIALLY WRONG - overrides sakana but not the derived issur
override(rema, sakana(M), no_sakana) :-
    is_dag_chalav_mixture(M).
% The issur(achiila, M, sakana) might still be inherited!

% CORRECT - override both the source and derived proposition
override(rema, sakana(M), no_sakana) :-
    is_dag_chalav_mixture(M).
override(rema, issur(achiila, M, sakana), permitted) :-
    is_dag_chalav_mixture(M).

Mistake 4: Missing machloket/4 Marker

% INCOMPLETE - machloket not documented
asserts(mechaber, sakana(M)) :- ...
override(rema, sakana(M), no_sakana) :- ...
% How do we discover this machloket programmatically?

% CORRECT - include machloket marker
machloket(dag_chalav_sakana, mechaber, rema, M) :-
    is_dag_chalav_mixture(M).

Mistake 5: Not Testing Both Branches

# WRONG - only tests one position
def test_mechaber_sakana():
    result = query(scenario, world="mechaber")
    assert result.holds("sakana(m1)")
# What about Rema? What about child worlds?

# CORRECT - test both positions and inheritance
def test_mechaber_sakana(): ...
def test_rema_no_sakana(): ...
def test_sefardi_yo_inherits_sakana(): ...
def test_ashk_mb_inherits_heter(): ...

Summary: Machloket Encoding Checklist

Before completing a machloket encoding:

  • [ ] Both positions encoded with full metadata
  • [ ] Override uses descriptive value (never 'invalid')
  • [ ] Positive assertions accompany all overrides
  • [ ] All derived propositions also overridden if needed
  • [ ] machloket/4 marker documents the dispute
  • [ ] Tests verify both positions
  • [ ] Tests verify child world inheritance
  • [ ] Tests verify cross-world divergence
  • [ ] No regressions in existing tests

The Complete Picture

After encoding this machloket, your world structure includes:

base
  ├── mechaber
  │     └── sefardi_yo (inherits sakana)
  ├── rema (overrides: no_sakana)
  │     ├── ashk_mb (inherits heter)
  │     └── ashk_ah (inherits heter)
  └── gra
        └── ashk_ah (also inherits from gra)

Each world now produces correct fish+dairy rulings based on its tradition.

Next Steps

Now that you understand machloket encoding, continue with:

  1. Tutorial 03: Complex Conditions - Context-sensitive rules
  2. Tutorial 04: Testing Strategies - Comprehensive testing
  3. Tutorial 05: Review Process - Navigate reviews

Quick Reference: Machloket Patterns

% === Pattern 1: Override with positive assertion ===
override(child_world, parent_assertion(X), descriptive_value) :-
    condition(X).
asserts(child_world, alternative_assertion(X)) :-
    condition(X).

% === Pattern 2: Machloket marker ===
machloket(topic_name, authority1, authority2, Subject) :-
    condition(Subject).

% === Pattern 3: Multi-world test ===
def test_machloket():
    mechaber = query(scenario, world="mechaber")
    rema = query(scenario, world="rema")
    assert mechaber.holds("position_a(x)")
    assert rema.holds("position_b(x)")