Skip to content

Tutorial 5: Encoding Machloket

This tutorial teaches you how to encode halachic disputes (machloket) between authorities using Mistaber's multi-world system.

Learning Objectives

  • Understand what constitutes a machloket in Mistaber
  • Encode disputes using asserts and override
  • Use the machloket detection system
  • Compare rulings across worlds

Prerequisites

Estimated Time

20 minutes

Steps

Step 1: What is a Machloket?

A machloket (dispute) occurs when different halachic authorities reach different conclusions on the same question. In Mistaber:

  • Different worlds hold different values for the same predicate
  • The system can detect and report these differences
  • Each position is traceable to its source (makor)

Common types of machloket: - Forbidden vs Permitted: One authority forbids, another permits - D'oraita vs D'rabanan: Same prohibition, different normative level - Different conditions: Same conclusion but under different circumstances

Step 2: The Fish and Dairy Machloket

The classic example in YD 87 is fish with dairy:

Authority Position Reasoning
Mechaber Forbidden (sakana) Medical danger cited in Beit Yosef
Rema Permitted No Talmudic source for danger
Taz Permitted (strongly) The Mechaber's source is a scribal error
Shach Conditional Only forbidden if cooked together

Let's see how this is encoded.

Step 3: Mechaber's Position (Issur)

In mechaber.lp:

% YD 87:3 - Mechaber forbids fish+dairy due to sakana
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).

% Fish + dairy is forbidden due to health danger
asserts(mechaber, sakana(M)) :-
    is_dag_chalav_mixture(M).

asserts(mechaber, issur(achiila, M, sakana)) :-
    is_dag_chalav_mixture(M).

This creates: - holds(sakana(M), mechaber) - There is a health danger - holds(issur(achiila, M, sakana), mechaber) - Eating is forbidden

Step 4: Rema's Position (Heter with Override)

In rema.lp:

% Rema overrides Mechaber's sakana ruling
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")).
scope(r_rema_dag_chalav_mutar, rema).

% Override the sakana ruling
override(rema, sakana(M), no_sakana) :-
    is_dag_chalav_mixture(M).

override(rema, issur(achiila, M, sakana), permitted) :-
    is_dag_chalav_mixture(M).

% Explicitly permit fish + dairy
asserts(rema, heter(achiila, M)) :-
    is_dag_chalav_mixture(M).

asserts(rema, no_sakana(M)) :-
    is_dag_chalav_mixture(M).

% Mark this as a machloket
machloket(dag_chalav, mechaber, rema, M) :-
    is_dag_chalav_mixture(M).

Key mechanisms: - override/3 prevents inheritance of Mechaber's issur - asserts/2 establishes Rema's positive ruling - machloket/4 explicitly marks this as a dispute

Step 5: Detect Machloket Programmatically

Use the MachloketDetector to find disputes:

from pathlib import Path
from mistaber.engine import HsrsEngine
from mistaber.engine.machloket import MachloketDetector

engine = HsrsEngine(Path("mistaber/ontology"))

# Add a fish+dairy mixture
scenario = """
food(salmon).
food(cream).
food_type(salmon, dag).
food_type(cream, chalav).
mixture(lox_cream).
contains(lox_cream, salmon).
contains(lox_cream, cream).
"""

# Create engine with scenario facts
# (Using analyze to add facts)
engine_result = engine.analyze(scenario, world="base")

# Detect machloket
detector = MachloketDetector(engine)
machloket_list = detector.find_machloket_between(
    "holds(issur(achiila, M, sakana), W)",
    worlds=["mechaber", "rema"]
)

print("Machloket found:")
for m in machloket_list:
    print(f"  Topic: {m.topic}")
    print(f"  Opinions:")
    for authority, opinion in m.opinions.items():
        print(f"    {authority}: {opinion}")

Step 6: Using the CLI for Machloket

The CLI has a dedicated machloket command:

# Find all machloket on a pattern
python -m core.cli.main --ontology ./mistaber/ontology machloket "status(X, Y)"

# Find machloket between specific worlds
python -m core.cli.main --ontology ./mistaber/ontology machloket \
    "holds(issur(achiila, M, D), W)" \
    --worlds "mechaber,rema"

Step 7: Encoding a New Machloket

Let's encode a fictional machloket about a new issue.

Scenario: There's a dispute about whether item X requires blessing A or blessing B.

% === World 1: Authority A says blessing_aleph ===

% In authority_a.lp
world(authority_a).
accessible(authority_a, base).

rule(r_auth_a_bracha).
makor(r_auth_a_bracha, source_a("1:1")).
scope(r_auth_a_bracha, authority_a).

asserts(authority_a, bracha(item_x, blessing_aleph)) :-
    food(item_x),
    category(item_x, disputed_category).


% === World 2: Authority B says blessing_bet ===

% In authority_b.lp
world(authority_b).
accessible(authority_b, base).

rule(r_auth_b_bracha).
makor(r_auth_b_bracha, source_b("2:2")).
scope(r_auth_b_bracha, authority_b).

% Override authority A's ruling (if B inherits from A)
override(authority_b, bracha(item_x, blessing_aleph), different_bracha) :-
    food(item_x),
    category(item_x, disputed_category).

asserts(authority_b, bracha(item_x, blessing_bet)) :-
    food(item_x),
    category(item_x, disputed_category).

% Mark the dispute
machloket(bracha_on_item_x, authority_a, authority_b, item_x) :-
    food(item_x),
    category(item_x, disputed_category).

Step 8: Compare Command for Machloket Analysis

Use the compare command to see all positions:

from pathlib import Path
from mistaber.engine import HsrsEngine

engine = HsrsEngine(Path("mistaber/ontology"))

# Compare a ruling across all worlds
comparison = engine.compare(
    "holds(issur(achiila, M, Level), W)"
)

print("Issur achiila across all worlds:")
for world, results in comparison.items():
    print(f"\n{world}:")
    if results:
        for r in results[:3]:  # First 3
            print(f"  M={r.get('M')}, Level={r.get('Level')}")
    else:
        print("  (no issur found)")

Step 9: Interpretations Layer for Nuanced Disputes

Some disputes are about interpreting the same rule differently. The Shach and Taz interpretations show this:

% In shach.lp - Shach's interpretation of Mechaber's fish+dairy rule
% Mechaber's sakana only applies when COOKED together

adds_condition(shach, r_bb_dag_sakana, cooked_together).
makor(interpretation(shach, r_bb_dag_sakana), shach("yd:87:5")).
% In taz.lp - Taz's interpretation
% The entire sakana ruling is a scribal error - no issur at all

removes_condition(taz, r_bb_dag_sakana, sakana).
makor(interpretation(taz, r_bb_dag_sakana), taz("yd:87:3")).

annotation(taz, r_bb_dag_sakana, scribal_error_argument).
annotation(taz, r_bb_dag_sakana, original_text_said_meat).

Step 10: Handling Safek in Machloket

When there's a machloket, some authorities use different safek policies:

from pathlib import Path
from mistaber.engine import HsrsEngine

engine = HsrsEngine(Path("mistaber/ontology"))

# Compare safek policies
ashk_mb_policy = engine.query("safek_policy(ashk_mb, d_rabanan, P)")
ashk_ah_policy = engine.query("safek_policy(ashk_ah, d_rabanan, P)")

print(f"Mishnah Berurah on safek d'rabanan: {ashk_mb_policy}")
print(f"Aruch Hashulchan on safek d'rabanan: {ashk_ah_policy}")

# MB: l_chumra (stringent)
# AH: l_kula (lenient)

Patterns for Encoding Machloket

Pattern 1: Direct Disagreement

Authority B explicitly disagrees with Authority A:

% A's position
asserts(authority_a, status(item, forbidden)).

% B's position (with override if B inherits from A)
override(authority_b, status(item, forbidden), disagreement).
asserts(authority_b, status(item, permitted)).

Pattern 2: Different Conditions

Same conclusion but under different circumstances:

% A: Forbidden in all cases
asserts(authority_a, issur(act, item)) :- relevant_item(item).

% B: Forbidden only with specific condition
asserts(authority_b, issur(act, item)) :-
    relevant_item(item),
    specific_condition(item).

Pattern 3: Different Normative Levels

Same prohibition but different madrega:

% A: Torah-level prohibition
madrega(r_rule_a, d_oraita).
asserts(authority_a, issur(act, item, d_oraita)) :- condition(item).

% B: Only rabbinic prohibition
madrega(r_rule_b, d_rabanan).
asserts(authority_b, issur(act, item, d_rabanan)) :- condition(item).

Exercises

Exercise 1: Detect Existing Machloket

Use the engine to find all existing machloket markers:

Solution
engine = HsrsEngine(Path("mistaber/ontology"))
machloket = engine.query("machloket(Topic, Auth1, Auth2, Item)")
for m in machloket:
    print(f"Dispute on {m['Topic']} between {m['Auth1']} and {m['Auth2']}")

Exercise 2: Encode Waiting Time Machloket

Encode the dispute about waiting time between meat and dairy (6 hours vs 3 hours vs 1 hour).

Solution
% Standard Ashkenazi: 6 hours
asserts(ashk_standard, waiting_time(meat_to_dairy, 6)).

% German tradition: 3 hours
asserts(ashk_german, waiting_time(meat_to_dairy, 3)).

% Dutch tradition: 1 hour
asserts(ashk_dutch, waiting_time(meat_to_dairy, 1)).

% Mark disputes
machloket(waiting_time, ashk_standard, ashk_german, meat_to_dairy).
machloket(waiting_time, ashk_standard, ashk_dutch, meat_to_dairy).

What You've Learned

  • Machloket occurs when different authorities derive different conclusions
  • Use asserts/2 for positive rulings and override/3 to block inheritance
  • The machloket/4 predicate explicitly marks disputes
  • MachloketDetector finds disputes automatically
  • Interpretations can add/remove conditions rather than replace rules

Next Steps

Continue to Tutorial 6: Extending Ontology to learn how to add new foods and rules to Mistaber.


Production Encoding Workflow

For systematic encoding of machloket with human review checkpoints, see the Handling Machloket Tutorial in the Encoding Tutorials section, which uses the mistaber-skills Claude Code plugin.

Quick Reference

% Mark a machloket
machloket(topic, authority_1, authority_2, item).

% Override inherited ruling
override(world, inherited_prop, reason).

% Interpretation modifiers
adds_condition(commentator, rule, condition).
removes_condition(commentator, rule, condition).
# Detect machloket
from mistaber.engine.machloket import MachloketDetector
detector = MachloketDetector(engine)
disputes = detector.find_machloket("pattern")
disputes = detector.find_machloket_between("pattern", worlds=["a", "b"])