Tutorial 6: Extending the Ontology¶
This tutorial teaches you how to extend Mistaber's ontology by adding new foods, new rules, and even new halachic domains.
Learning Objectives¶
- Add new foods to the knowledge base
- Define food types and relationships
- Create new halachic rules
- Add a new commentator world
- Test your extensions
Prerequisites¶
- Tutorial 5: Encoding Machloket completed
- Understanding of ASP syntax
Estimated Time¶
25 minutes
Steps¶
Step 1: Understanding the Ontology Structure¶
The ontology is organized as:
mistaber/ontology/
├── schema/ # Type definitions and constraints
│ ├── sorts.lp # Basic sorts (food, vessel, world, etc.)
│ ├── disjointness.lp
│ └── constraints.lp
├── base/ # Foundational concepts
│ ├── substance.lp # Physical properties
│ ├── status.lp # Halachic statuses
│ └── madrega.lp # Normative levels
├── worlds/ # Authority perspectives
│ ├── base.lp
│ ├── mechaber.lp
│ └── rema.lp
├── corpus/ # Encoded halacha by topic
│ └── yd_87/ # Yoreh Deah 87 (Basar Bechalav)
│ └── base.lp
└── interpretations/ # Commentator modifications
├── shach.lp
└── taz.lp
Step 2: Add a New Food Item¶
To add a new food, create facts using the existing predicates.
Create a file my_foods.lp:
% my_foods.lp
% Adding new food items to the ontology
% Declare foods
food(quinoa).
food(tempeh).
food(tofu).
food(almond_milk).
% Classify by type
food_type(quinoa, grain). % Assuming grain type exists
food_type(tempeh, parve). % Parve (neutral)
food_type(tofu, parve).
food_type(almond_milk, parve). % Plant-based, not real chalav
% Note: almond_milk is NOT classified as chalav because it's plant-based
% This reflects the halacha that plant-based "milks" are parve
Step 3: Add a New Food Category¶
If you need a new category, add it to the schema:
% In schema/sorts.lp or a custom file
% New food category
food_category(kitniyot). % Legumes (relevant for Pesach)
% Add to hierarchy
subcategory(kitniyot, maakhal).
% Specific items
food_type(rice, kitniyot).
food_type(corn, kitniyot).
food_type(beans, kitniyot).
Step 4: Create Rules for New Foods¶
Now create rules that apply to your new foods:
% kitniyot_rules.lp
% Rules about kitniyot for Pesach
% === Ashkenazi Position ===
% Kitniyot are forbidden on Pesach for Ashkenazim
rule(r_kitniyot_pesach_ashk).
makor(r_kitniyot_pesach_ashk, rema("oc:453:1")).
madrega(r_kitniyot_pesach_ashk, minhag).
scope(r_kitniyot_pesach_ashk, rema).
asserts(rema, issur(achiila, F, minhag)) :-
food(F),
food_type(F, kitniyot),
context(pesach).
% === Sefardi Position ===
% Kitniyot are permitted for Sefardim
rule(r_kitniyot_pesach_seph).
makor(r_kitniyot_pesach_seph, sa("oc:453")).
scope(r_kitniyot_pesach_seph, mechaber).
asserts(mechaber, heter(achiila, F)) :-
food(F),
food_type(F, kitniyot),
context(pesach).
Step 5: Add a New World (Commentator)¶
Let's add the Pri Megadim as a new authority:
% worlds/pri_megadim.lp
% Pri Megadim - R' Yosef Teomim (1727-1792)
% Commentary on Shulchan Aruch (Mishbetzot Zahav on Taz, Eshel Avraham on Magen Avraham)
% === World Declaration ===
world(pri_megadim).
% Pri Megadim comments on both Taz and Shach
% For our purposes, inherit from rema (Ashkenazi baseline)
accessible(pri_megadim, rema).
% === Include Corpus ===
#include "../corpus/yd_87/base.lp".
% === Pri Megadim's Specific Rulings ===
% Example: PM on fish + dairy
% The PM generally agrees with the lenient Ashkenazi position
rule(r_pm_dag_chalav).
makor(r_pm_dag_chalav, pri_megadim("yd:87")).
scope(r_pm_dag_chalav, pri_megadim).
asserts(pri_megadim, heter(achiila, M)) :-
is_dag_chalav_mixture(M).
% === Safek Policy ===
safek_policy(pri_megadim, d_oraita, l_chumra).
safek_policy(pri_megadim, d_rabanan, l_kula).
% === Minhag Region ===
minhag_region(pri_megadim, ashkenaz).
% === Show Directives ===
#show world/1.
#show accessible/2.
#show asserts/2.
Step 6: Add New Interpretations¶
Create an interpretation layer for a commentator:
% interpretations/pri_chadash.lp
% Pri Chadash - R' Chizkiya da Silva (1659-1698)
% Known for lenient interpretations in some areas
% Pri Chadash's interpretation of bitul rules
% PC allows bitul even for davar sheyesh lo matirin in some cases
expands_scope(pri_chadash, r_bitul_shishim, allows_davar_sheyesh_lo_matirin).
makor(interpretation(pri_chadash, r_bitul_shishim), pri_chadash("yd:102")).
% This means: when querying with Pri Chadash's interpretation,
% the davar sheyesh lo matirin exception to bitul may not apply
Step 7: Test Your Extensions¶
Create a test file:
# test_my_extensions.py
"""Tests for my ontology extensions."""
import pytest
from pathlib import Path
try:
import clingo
CLINGO_AVAILABLE = True
except ImportError:
CLINGO_AVAILABLE = False
PROJECT_ROOT = Path(__file__).parent.parent
ONTOLOGY_DIR = PROJECT_ROOT / "core" / "ontology"
def run_with_extensions(extra_files: list[str], scenario: str) -> list[str]:
"""Run clingo with base ontology plus custom extensions."""
ctl = clingo.Control(["--warn=none"])
# Load base ontology
for subdir in ["schema", "base", "worlds"]:
dir_path = ONTOLOGY_DIR / subdir
if dir_path.exists():
for lp_file in dir_path.glob("*.lp"):
ctl.load(str(lp_file))
# Load extensions
for ext_file in extra_files:
ctl.load(ext_file)
# Add scenario
ctl.add("test", [], scenario)
# Ground and solve
ctl.ground([("base", []), ("test", [])])
atoms = []
with ctl.solve(yield_=True) as handle:
for model in handle:
atoms = [str(a) for a in model.symbols(shown=True)]
break
return atoms
@pytest.mark.skipif(not CLINGO_AVAILABLE, reason="clingo not installed")
def test_new_food_classification():
"""Test that new foods are properly classified."""
scenario = """
food(quinoa).
food_type(quinoa, parve).
"""
atoms = run_with_extensions([], scenario)
# Verify the food is recognized
assert "food(quinoa)" in atoms
@pytest.mark.skipif(not CLINGO_AVAILABLE, reason="clingo not installed")
def test_new_world_exists():
"""Test that our new Pri Megadim world is recognized."""
scenario = """
world(pri_megadim).
accessible(pri_megadim, rema).
"""
atoms = run_with_extensions([], scenario)
assert "world(pri_megadim)" in atoms
assert "accessible(pri_megadim,rema)" in atoms
Step 8: Integrate with Existing Ontology¶
To make your extensions permanent, decide where they belong:
| Extension Type | Location |
|---|---|
| New food categories | schema/sorts.lp |
| New food items | base/substance.lp or new file |
| New halachic rules | corpus/[topic]/ |
| New world | worlds/ |
| New interpretations | interpretations/ |
Step 9: Add a Complete New Siman¶
Let's outline adding Yoreh Deah 89 (waiting between meat and dairy):
% corpus/yd_89/base.lp
% Yoreh Deah Siman 89: Waiting Between Meat and Dairy
% === Waiting Time Rules ===
% Base requirement: must wait after eating meat before dairy
rule(r_yd89_waiting_base).
makor(r_yd89_waiting_base, sa("yd:89:1")).
madrega(r_yd89_waiting_base, d_rabanan).
requires_waiting(meat_to_dairy).
% Time periods (in hours)
waiting_period_type(six_hours).
waiting_period_type(three_hours).
waiting_period_type(one_hour).
% === World-Specific Waiting Times ===
% Mechaber: 6 hours
% "tzarich l'hamtin shesh shaot" (one must wait six hours)
rule(r_yd89_mechaber_six).
makor(r_yd89_mechaber_six, sa("yd:89:1")).
scope(r_yd89_mechaber_six, mechaber).
asserts(mechaber, waiting_time(meat_to_dairy, six_hours)).
% Rema: 6 hours for standard Ashkenazi practice
% But notes that some have custom of 3 hours (German) or 1 hour (Dutch)
rule(r_yd89_rema_six).
makor(r_yd89_rema_six, rema("yd:89:1")).
scope(r_yd89_rema_six, rema).
asserts(rema, waiting_time(meat_to_dairy, six_hours)).
Step 10: Document Your Extensions¶
Create documentation for your additions:
# YD 89: Waiting Between Meat and Dairy
## Overview
This module implements the laws of waiting between eating meat and dairy.
## Sources
- SA YD 89:1 - Base requirement
- Rema YD 89:1 - Ashkenazi variations
## Predicates Added
- `requires_waiting/1` - Marks a transition requiring wait
- `waiting_time/2` - (direction, period) pairs
- `waiting_period_type/1` - Valid waiting periods
## Worlds Affected
- mechaber: 6 hours
- rema: 6 hours (with noted variations)
Best Practices for Extensions¶
1. Follow Naming Conventions¶
% Rules: r_[topic]_[detail]
rule(r_yd89_waiting_base).
% Sources: authority("reference")
makor(r_rule, sa("yd:89:1")).
makor(r_rule, rema("yd:89:1")).
2. Always Include Sources¶
Every normative rule needs makor citations:
rule(r_my_rule).
makor(r_my_rule, source("reference")). % Required!
madrega(r_my_rule, d_rabanan).
scope(r_my_rule, world_name).
3. Respect the Hierarchy¶
New worlds should properly inherit:
4. Test Incrementally¶
Test each addition before moving on:
# Test new foods
assert "food(my_food)" in atoms
# Test new rules fire
assert "issur(...)" in atoms or "heter(...)" in atoms
# Test world inheritance
assert "accessible(new_world, parent)" in atoms
Exercises¶
Exercise 1: Add a New Food¶
Add "soy_milk" as a parve food and verify it's not treated as dairy.
Solution
food(soy_milk).
food_type(soy_milk, parve). % NOT chalav
% Test: mixture with soy_milk and meat should not trigger basar bechalav
mixture(meat_soy).
contains(meat_soy, beef).
contains(meat_soy, soy_milk).
food_type(beef, beheima).
% Result: no is_beheima_chalav_mixture(meat_soy) because soy_milk is not chalav
Exercise 2: Add Kitniyot Rules¶
Implement the full kitniyot rules with Ashkenazi/Sefardi distinction.
Solution
% New category
food_category(kitniyot).
food_type(rice, kitniyot).
food_type(beans, kitniyot).
% Ashkenazi: forbidden on Pesach
asserts(rema, issur(achiila, F, minhag)) :-
food(F),
food_type(F, kitniyot),
context(pesach).
% Sefardi: permitted
asserts(mechaber, heter(achiila, F)) :-
food(F),
food_type(F, kitniyot),
context(pesach).
Exercise 3: Add a New World¶
Create a world for the Kaf HaChaim (Sefardi posek).
Solution
What You've Learned¶
- How to add new foods with proper classification
- How to create new food categories
- How to write rules that apply to new foods
- How to add new commentator worlds
- How to create interpretation layers
- How to test your extensions
Next Steps¶
You've completed the tutorial series! Consider:
- Reading the Case Studies for real-world examples
- Exploring the Architecture Documentation for technical details
- Contributing your extensions back to the project
- Using the Encoding Workflow for AI-assisted production encoding
Production Encoding Workflow
Ready to contribute to the Mistaber ontology? The Encoding Workflow Guide provides a systematic, AI-assisted pipeline with human review checkpoints using the mistaber-skills Claude Code plugin.
Quick Reference¶
% Add food
food(item_name).
food_type(item_name, category).
% Add category
food_category(new_category).
subcategory(new_category, parent_category).
% Add world
world(new_world).
accessible(new_world, parent_world).
% Add rule
rule(r_rule_id).
makor(r_rule_id, source("ref")).
madrega(r_rule_id, level).
scope(r_rule_id, world).
asserts(world, conclusion) :- conditions.