Skip to content

feat: madhhab-aware fiqh answers with ikhtilaf methodology - #51

Merged
zeemscript merged 1 commit into
Deen-Bridge:devfrom
GEEKYFOCUS:feat/fiqh-madhhab
Jul 22, 2026
Merged

feat: madhhab-aware fiqh answers with ikhtilaf methodology#51
zeemscript merged 1 commit into
Deen-Bridge:devfrom
GEEKYFOCUS:feat/fiqh-madhhab

Conversation

@GEEKYFOCUS

@GEEKYFOCUS GEEKYFOCUS commented Jul 22, 2026

Copy link
Copy Markdown

Closes #39

Summary

Makes the assistant madhhab-aware: detects fiqh questions via keyword pre-filter, presents positions of all four major Sunni schools with attribution, and supports an optional madhhab field on ChatRequest to lead with the user's preferred school.

Changes

  • fiqh.py — new module: keyword-based fiqh-question classifier, madhhab normalization (handles shafi'i, shafie, etc.), FiqhInfo response model, ikhtilaf methodology system-prompt blocks
  • main.pymadhhab field on ChatRequest (validated permissively: unknown values → None with warning), fiqh detection + context injection in /chat handler, fiqh metadata on ChatResponse
  • tests/test_fiqh.py — 38 unit tests (madhhab normalization, keyword pre-filter, context structure, eval case validation)
  • tests/fixtures/fiqh_cases.jsonl — 9 comparative-fiqh eval cases (6 fiqh + 3 non-fiqh)
  • .github/workflows/ci.yml — lint/syntax extended to fiqh.py, new pytest step for fiqh tests

Key design decisions

  • Keyword pre-filter for fiqh detection: zero latency for obvious non-fiqh questions. Extensible with a Gemini classifier later (the classify_fn parameter is already wired).
  • Prompt injection: FIQH_IKHTILAF_CONTEXT is appended to ISLAMIC_CONTEXT only when a fiqh question is detected; non-fiqh answers are unaffected.
  • Permissive madhhab validation: unknown spellings degrade to None with a warning, mirroring the existing safety-pipeline pattern — no 422 errors for typos.

Summary by CodeRabbit

  • New Features

    • Added fiqh question detection and support for recognizing requested madhhabs.
    • Chat requests can include an optional madhhab preference.
    • Responses now indicate whether a question is fiqh-related and include the requested madhhab when provided.
    • Fiqh responses include guidance for presenting differing opinions across Sunni schools.
  • Tests

    • Added comprehensive fiqh classification and madhhab validation coverage.
    • Expanded automated linting, syntax checks, and test execution for fiqh functionality.

- fiqh.py: fiqh-question classifier (keyword pre-filter), madhhab normalization,
  FiqhInfo model, and ikhtilaf methodology system-prompt blocks
- main.py: madhhab field on ChatRequest, fiqh detection + context injection
  in /chat handler, FiqhInfo on ChatResponse
- tests/test_fiqh.py: 38 unit tests for madhhab norm, keyword pre-filter,
  context structure, and eval case validation
- tests/fixtures/fiqh_cases.jsonl: 9 comparative-fiqh eval cases
- CI: extend lint/syntax to fiqh.py, add pytest step for fiqh tests

Closes Deen-Bridge#39
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds madhhab-aware fiqh detection to chat requests, enriches fiqh prompts with school-specific context, returns classification metadata, and adds offline tests, fixtures, linting, syntax checks, and CI execution for the new module.

Changes

Fiqh-aware chat flow

Layer / File(s) Summary
Fiqh classification and metadata contracts
fiqh.py
Adds madhhab alias normalization, keyword-based fiqh classification, response guidance strings, and the FiqhInfo model.
Madhhab-aware chat handling
main.py
Accepts an optional madhhab, enriches fiqh prompts, and includes fiqh metadata in cached and generated responses.
Offline validation and CI coverage
tests/fixtures/fiqh_cases.jsonl, tests/test_fiqh.py, .github/workflows/ci.yml
Adds fixture-driven unit tests and runs linting, syntax checks, and the fiqh test suite in CI.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ChatRequest
  participant classify_fiqh
  participant generate
  participant ChatResponse
  Client->>ChatRequest: prompt and optional madhhab
  ChatRequest->>classify_fiqh: classify prompt
  classify_fiqh-->>ChatRequest: FiqhInfo
  ChatRequest->>generate: enriched system context
  generate-->>ChatResponse: generated text and FiqhInfo
  ChatResponse-->>Client: response metadata
Loading

Possibly related PRs

  • Deen-Bridge/dnb-ai#48: Adds a related madhhab-aware fiqh implementation using overlapping chat, classification, and CI files.

Suggested reviewers: zeemscript, dayz-tech-co

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: madhhab-aware fiqh answers with ikhtilaf methodology.
Linked Issues check ✅ Passed The PR implements fiqh detection, madhhab normalization, prompt guidance, response metadata, tests, fixtures, and CI updates requested by #39.
Out of Scope Changes check ✅ Passed All modified files support the madhhab-aware fiqh feature set, and no unrelated functionality appears to have been added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
.github/workflows/ci.yml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/fixtures/fiqh_cases.jsonl (1)

10-10: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove the invalid trailing JSONL record.

Line 10 parses as the integer 10, so the loader later attempts dictionary access on an integer and the fiqh tests fail before completing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/fixtures/fiqh_cases.jsonl` at line 10, Remove the invalid trailing
JSONL record from the fiqh fixture so every line contains a JSON object;
specifically delete the standalone integer record `10` that causes dictionary
access to fail during fixture loading.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 31-34: Update the flake8 and compileall commands in the CI
workflow to include tests/test_fiqh.py, or target the complete tests directory,
so the new test module is covered by lint and syntax checks.

In `@fiqh.py`:
- Around line 65-76: Update classify_fiqh so keyword_match remains only a
pre-filter and keyword hits do not automatically return True when classify_fn is
absent. Ensure the /chat call path supplies or invokes semantic classification,
or explicitly narrow the classifier contract and adjust expected cases
accordingly; add coverage for both positive and negative classifications,
including substring and broad-keyword matches.
- Around line 81-84: Update the fiqh/madhhab methodology instruction to say
“points of agreement, when present” rather than unconditionally invoking ijma'.
Preserve the requirement to present genuine agreement among the schools first,
without implying consensus exists for every fiqh question.

In `@main.py`:
- Around line 186-190: The semantic-cache identity must account for the
normalized madhhab when handling fiqh requests. Update the cache lookup and
storage flow associated with `madhhab`, `is_fiqh`, and `fiqh_info` to either
include the normalized madhhab in the cache key or bypass caching for
madhhab-aware fiqh requests, preventing responses from being reused across
different preferences.
- Line 100: Update the ChatRequest.madhhab field to retain permissive string
values while enforcing a bounded Pydantic length constraint. Then update the
semantic-cache key generation to include the normalized madhhab value, ensuring
requests with different schools do not reuse the same cached answer.

In `@tests/test_fiqh.py`:
- Around line 146-167: Add a parametrized test covering every case from
load_cases() that asserts classify_fiqh(case["prompt"]) equals the fixture’s
case["is_fiqh"] value. Place it with the existing fiqh fixture tests and
preserve the current positive keyword-match test.
- Line 6: Add a pinned pytest version to requirements.txt and update the CI
workflow to install dependencies from that file instead of installing pytest ad
hoc, while preserving the existing flake8 installation and test behavior.

---

Outside diff comments:
In `@tests/fixtures/fiqh_cases.jsonl`:
- Line 10: Remove the invalid trailing JSONL record from the fiqh fixture so
every line contains a JSON object; specifically delete the standalone integer
record `10` that causes dictionary access to fail during fixture loading.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c15c509-a8f1-4354-a313-1fde3d92c426

📥 Commits

Reviewing files that changed from the base of the PR and between ae1f75b and 13571b4.

📒 Files selected for processing (5)
  • .github/workflows/ci.yml
  • fiqh.py
  • main.py
  • tests/fixtures/fiqh_cases.jsonl
  • tests/test_fiqh.py

Comment thread .github/workflows/ci.yml
Comment on lines +31 to +34
run: flake8 main.py stellar.py safety tests/redteam study.py fiqh.py --max-line-length=120 --ignore=E501,W503

- name: Check syntax
run: python -m compileall -q main.py stellar.py safety tests/redteam study.py
run: python -m compileall -q main.py stellar.py safety tests/redteam study.py fiqh.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Include tests/test_fiqh.py in lint and syntax checks.

Both commands omit the new test module, so a flake8 violation or syntax regression there is not covered by the explicit checks. Include tests/test_fiqh.py or target the complete tests directory.

As per path instructions, CI-enforced flake8 coverage should include every changed Python file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 31 - 34, Update the flake8 and
compileall commands in the CI workflow to include tests/test_fiqh.py, or target
the complete tests directory, so the new test module is covered by lint and
syntax checks.

Source: Path instructions

Comment thread fiqh.py
Comment on lines +65 to +76
def keyword_match(text: str) -> bool:
lower = text.casefold()
return any(kw in lower for kw in FIQH_KEYWORDS)


def classify_fiqh(prompt: str, classify_fn=None) -> bool:
kw = keyword_match(prompt)
if not kw:
return False
if classify_fn is None:
return True
return classify_fn(prompt)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep the keyword scan as a pre-filter, not the final classifier.

classify_fiqh returns True for every keyword hit when classify_fn is omitted, and /chat calls it that way. This marks the fixture’s “How many rak'ahs…” case as fiqh because "rak'ah" is a substring, while broad entries such as "can i" can match unrelated questions. Add the semantic classification step, or narrow the contract and update the expected cases; test both positive and negative classifications.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fiqh.py` around lines 65 - 76, Update classify_fiqh so keyword_match remains
only a pre-filter and keyword hits do not automatically return True when
classify_fn is absent. Ensure the /chat call path supplies or invokes semantic
classification, or explicitly narrow the classifier contract and adjust expected
cases accordingly; add coverage for both positive and negative classifications,
including substring and broad-keyword matches.

Comment thread fiqh.py
Comment on lines +81 to +84
FIQH / MADHHAB METHODOLOGY:
When the user asks a fiqh (jurisprudence) question, follow these rules:

1. State points of agreement (ijma') among the schools first.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant section with line numbers
sed -n '1,160p' fiqh.py | nl -ba | sed -n '70,95p'

# Search for other consensus/agreement wording in the file
rg -n "ijma|agreement|consensus|madhhab|school" fiqh.py

Repository: Deen-Bridge/dnb-ai

Length of output: 196


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Print the relevant region with line numbers using awk
awk 'NR>=70 && NR<=95 { printf "%d:%s\n", NR, $0 }' fiqh.py

# Search for related methodology wording elsewhere
rg -n "ijma|agreement|consensus|madhhab|school" fiqh.py

Repository: Deen-Bridge/dnb-ai

Length of output: 1891


Avoid unconditional ijma' wording

“ijma'” is a technical claim of unanimous consensus, so this should say “points of agreement, when present” instead of treating every fiqh answer as established consensus. That keeps the prompt accurate and avoids the model overstating agreement where there may only be common or majority views.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fiqh.py` around lines 81 - 84, Update the fiqh/madhhab methodology
instruction to say “points of agreement, when present” rather than
unconditionally invoking ijma'. Preserve the requirement to present genuine
agreement among the schools first, without implying consensus exists for every
fiqh question.

Comment thread main.py
prompt: str
chat_id: Optional[str] = None
context: Optional[str] = None # Additional context for specific queries
madhhab: Optional[str] = None # User's madhhab: hanafi, maliki, shafii, hanbali

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail

printf '\n== file list ==\n'
git ls-files main.py

printf '\n== line count ==\n'
wc -l main.py

printf '\n== main.py around request model ==\n'
sed -n '1,160p' main.py | cat -n

printf '\n== main.py around cache flow ==\n'
sed -n '200,280p' main.py | cat -n

Repository: Deen-Bridge/dnb-ai

Length of output: 10208


🏁 Script executed:

set -euo pipefail

printf '\n== madhhab-related code in main.py ==\n'
rg -n "madhhab|normalize_madhhab|fiqh_info|semantic_cache|embed_text|normalize_text" main.py

printf '\n== fiqh.py outline ==\n'
ast-grep outline fiqh.py --view expanded

printf '\n== fiqh.py madhhab helpers ==\n'
sed -n '1,220p' fiqh.py | cat -n

printf '\n== semantic_cache.py outline ==\n'
ast-grep outline semantic_cache.py --view expanded

printf '\n== semantic_cache.py key functions ==\n'
sed -n '1,240p' semantic_cache.py | cat -n

Repository: Deen-Bridge/dnb-ai

Length of output: 15311


🏁 Script executed:

set -euo pipefail

sed -n '176,220p' main.py | cat -n

Repository: Deen-Bridge/dnb-ai

Length of output: 2548


Bound ChatRequest.madhhab and include it in the cache identity.
Keep unknown madhhab values permissive, but add a bounded Pydantic constraint (for example max_length) so arbitrarily large raw input is rejected. Also vary the semantic-cache key on the normalized madhhab, since the answer changes when the school-specific instruction is added.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@main.py` at line 100, Update the ChatRequest.madhhab field to retain
permissive string values while enforcing a bounded Pydantic length constraint.
Then update the semantic-cache key generation to include the normalized madhhab
value, ensuring requests with different schools do not reuse the same cached
answer.

Source: Path instructions

Comment thread main.py
Comment on lines +186 to +190
# --- Fiqh classification & madhhab ---
madhhab = normalize_madhhab(request.madhhab)
is_fiqh = classify_fiqh(request.prompt)
fiqh_info = FiqhInfo(is_fiqh_question=is_fiqh, madhhab_requested=madhhab)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make the semantic-cache key madhhab-aware.

The normalized madhhab changes the generated prompt, but the cache lookup is keyed only by the prompt embedding. A response generated for a Shafi‘i user can therefore be returned to a Hanafi or no-preference user while fiqh_info reports the current request’s preference. Bypass caching for madhhab-aware fiqh requests or include the normalized madhhab in the cache identity.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@main.py` around lines 186 - 190, The semantic-cache identity must account for
the normalized madhhab when handling fiqh requests. Update the cache lookup and
storage flow associated with `madhhab`, `is_fiqh`, and `fiqh_info` to either
include the normalized madhhab in the cache key or bypass caching for
madhhab-aware fiqh requests, preventing responses from being reused across
different preferences.

Comment thread tests/test_fiqh.py
import json
import os

import pytest

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== files ==\n'
git ls-files | rg '(^|/)(requirements(\.txt)?|pyproject\.toml|poetry\.lock|Pipfile(\.lock)?|tox\.ini|noxfile\.py|\.github/workflows/.*\.ya?ml|tests/test_fiqh\.py)$'

printf '\n== requirements-like files ==\n'
for f in requirements.txt pyproject.toml poetry.lock Pipfile Pipfile.lock tox.ini noxfile.py .github/workflows; do
  if [ -e "$f" ]; then
    echo "--- $f ---"
    if [ -d "$f" ]; then
      find "$f" -maxdepth 2 -type f | sort
    else
      sed -n '1,220p' "$f"
    fi
  fi
done

printf '\n== test file ==\n'
sed -n '1,220p' tests/test_fiqh.py

Repository: Deen-Bridge/dnb-ai

Length of output: 5672


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' .github/workflows/ci.yml

Repository: Deen-Bridge/dnb-ai

Length of output: 1311


Pin pytest in the dependency set. tests/test_fiqh.py imports pytest, but CI installs it ad hoc with pip install pytest flake8 instead of from requirements.txt, so the test environment isn’t reproducible. Add a pinned pytest entry and install from the pinned file in CI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_fiqh.py` at line 6, Add a pinned pytest version to
requirements.txt and update the CI workflow to install dependencies from that
file instead of installing pytest ad hoc, while preserving the existing flake8
installation and test behavior.

Source: Path instructions

Comment thread tests/test_fiqh.py
Comment on lines +146 to +167
def test_fiqh_cases_jsonl_loads():
cases = load_cases()
assert len(cases) >= 8
for case in cases:
assert "prompt" in case
assert "is_fiqh" in case
assert "expect_schools" in case


def test_non_fiqh_cases_have_empty_schools():
for case in load_cases():
if not case["is_fiqh"]:
assert case["expect_schools"] == []


# ---------------------------------------------------------------------------
# Keyword pre-filter: eval cases
# ---------------------------------------------------------------------------

@pytest.mark.parametrize("case", [c for c in load_cases() if c["is_fiqh"]], ids=lambda c: c["prompt"][:40])
def test_fiqh_cases_keyword_match_true(case):
assert keyword_match(case["prompt"]), f"Expected keyword match for fiqh case: {case['prompt'][:60]}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the fixture’s is_fiqh contract.

These tests only validate JSON structure and positive keyword matches. Add a parametrized assertion that classify_fiqh(case["prompt"]) == case["is_fiqh"] for every fixture case; this will catch false-positive regressions in the non-fiqh cases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_fiqh.py` around lines 146 - 167, Add a parametrized test covering
every case from load_cases() that asserts classify_fiqh(case["prompt"]) equals
the fixture’s case["is_fiqh"] value. Place it with the existing fiqh fixture
tests and preserve the current positive keyword-match test.

@zeemscript
zeemscript merged commit c1fb35c into Deen-Bridge:dev Jul 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants