Skip to content

feat: add tafsir layer with retrieved, attributed ayah explanations (#54) - #64

Merged
zeemscript merged 3 commits into
Deen-Bridge:devfrom
Fury03:feat/issue-54-tafsir
Jul 24, 2026
Merged

feat: add tafsir layer with retrieved, attributed ayah explanations (#54)#64
zeemscript merged 3 commits into
Deen-Bridge:devfrom
Fury03:feat/issue-54-tafsir

Conversation

@Fury03

@Fury03 Fury03 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Closes #54

What

Asked "what does Surah al-'Asr mean?", the service produced a paraphrase from the model's own memory — no named tafsir, no way to see how Ibn Kathir or al-Sa'di actually explained the ayah. This PR replaces recall with retrieval.

POST /tafsir

Accepts a surah:ayah reference, a range, or a surah name, plus an optional list of tafsirs and a language. Returns the ayah text, a translation, and per-tafsir explanations — each one attributed to a named work, its author, and the language the text is actually in.

curl -X POST localhost:8000/tafsir -H 'Content-Type: application/json' \
  -d '{"reference": "103:1-3", "tafsirs": ["ibn-kathir", "tabari", "saadi"], "language": "en"}'

GET /tafsir/sources lists the retrievable works (Ibn Kathir, al-Tabari, al-Qurtubi, al-Sa'di, al-Baghawi, Ma'arif al-Qur'an, and others) and their available languages.

Reference validation

data/quran/surah_index.json (built by scripts/build_surah_index.py, provenance in data/quran/PROVENANCE.md) carries surah numbers, names, and ayah counts — no Quranic text. Bounds are checked offline before any network call, so 2:300 returns a 400 naming Al-Baqarah's 286 ayat instead of a hallucinated verse. The build script asserts 114 surahs / 6236 ayat, so a typo fails the build.

Chat integration

A verse-explanation question in /chat ("explain 2:255", "what does Surah al-'Asr mean?") is detected offline via regex + the surah index, then answered from retrieved passages. The system context requires attributing each claim to a named mufassir, forbids adding interpretation from memory, and requires surfacing — not flattening — points where the mufassirun differ. ChatResponse gains an additive optional tafsir block naming the works whose text actually backed the answer.

Intent detection is deliberately conservative: without the word "surah", both the definite article and an explicit ayah number are required, so "what does ar-Rahman mean?" or a question about the name Muhammad is never read as a surah reference.

Caching (coordinated with #27)

Tafsir text is immutable per ayah, so it is cached by exact ayah key through a new semantic_cache.KeyedCache — the keyed sibling of the semantic response cache, living in the same module and sharing its TTL and eviction configuration. It is not a second cache system; approximate embedding similarity is simply the wrong matching rule for ayah keys, where 2:255 and 2:256 are near-identical strings that must never match each other. Grounded tafsir answers skip the semantic response cache for the same reason — the expensive part is already cached at the retrieval layer.

Correctness notes worth reviewing

  • Attribution never comes from model memory. A passage's name comes from the source's own response for the resource that was fetched; the registry's display names are used only for "unavailable" messages.
  • Language labels come from the edition fetched, not from the payload. The API's translated_name.language_name describes the language the work's name was translated into — it reports "english" for al-Tabari on an English-locale request, even though the commentary is Arabic. Labelling it that way would be a misattribution, so the label follows the edition this service asked for. Covered by a test.
  • Graceful degradation. A work with no entry for an ayah, or one that returns empty text, lands in unavailable with a reason rather than an empty explanation dressed up as commentary. Retrieval failures in /chat are logged and the turn proceeds without tafsir rather than 500ing.

Tests

122 new offline tests in tests/test_tafsir.py against a FakeTafsirSource — reference validation and bounds, response assembly, attribution preservation, both readings surfaced when two tafsirs differ, unavailable/empty degradation, language fallback and labelling, cache hit and key isolation, intent detection (including the false-positive guards), and prompt-block assembly. No live API calls.

CI extends flake8 and compileall to the new modules and adds a tafsir pytest step. Full suite: 336 passed, 1 skipped.

Notes

  • httpx==0.28.1 pinned in requirements.txt (async client — no blocking calls in the async path).
  • No new secrets or keys; the Quran.com API needs none. QURAN_API_BASE, QURAN_API_TIMEOUT, TAFSIR_MAX_AYAT, and TAFSIR_CHAT_EXCERPT_CHARS are configurable and documented in .env.example and the README.
  • Tafsir text in the test fixtures is placeholder prose, not the real works — the payload shape mirrors the API exactly, and nothing copyrighted is bundled.
  • Jalalayn, mentioned in the issue, is not published by this source; the registry documents what is actually retrievable rather than promising a work that would silently 404.

Summary by CodeRabbit

  • New Features
    • Added tafsir-grounded Quran verse explanations with attribution from named classical works.
    • Introduced /tafsir and /tafsir/sources endpoints (language selection, verse ranges, offline bounds validation, and unavailable-work notices).
    • Enhanced chat to detect verse-explanation prompts and enrich responses with retrieved tafsir context.
  • Documentation
    • Updated README with endpoint details, request/response formats, chat grounding behavior, and new tafsir environment variables; added dataset provenance.
  • Quality
    • Expanded CI checks and added comprehensive tafsir tests.
    • Added TTL-based keyed caching for faster repeat lookups.

Verse interpretation was answered from model memory: no named mufassir, no
way to see how Ibn Kathir or al-Sa'di actually explained an ayah, and no
distinction between a classical reading and a modern gloss.

This adds a tafsir layer that replaces recall with retrieval:

- POST /tafsir returns per-tafsir explanations for an ayah or a short range,
  each attributed to a named work, author, and the language the text is in.
- GET /tafsir/sources lists the retrievable works and their languages.
- Ayah references are validated offline against a bundled surah index
  (data/quran/surah_index.json, built by scripts/build_surah_index.py), so
  an out-of-range reference is a 400 naming the real bound rather than an
  invented verse.
- /chat detects verse-explanation questions offline and answers from the
  same retrieved passages, instructing the model to attribute each claim to
  a named mufassir and to surface — not flatten — where they differ.
- A tafsir unavailable for an ayah degrades to a labelled "unavailable"
  entry; the rest of the response is unaffected.
- Tafsir text is immutable per ayah, so it is cached by exact ayah key
  through semantic_cache.KeyedCache — the keyed sibling of the semantic
  response cache, sharing its TTL and eviction config rather than adding a
  second cache system.

Language labels come from the edition that was fetched, not from the API's
translated_name.language_name, which describes the language of the work's
*name* and would label al-Tabari's Arabic commentary "english".

Tests run fully offline against a fake source; CI now lints and compiles the
new modules and runs the tafsir suite.

Closes Deen-Bridge#54
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds a tafsir layer with offline Quran reference validation, multi-work Quran.com retrieval, keyed caching, dedicated endpoints, chat grounding, attribution metadata, documentation, and CI tests.

Changes

Tafsir grounding feature

Layer / File(s) Summary
Reference data and keyed caching
scripts/build_surah_index.py, data/quran/surah_index.json, semantic_cache.py, .env.example, requirements.txt
Adds metadata for all 114 surahs and a process-local TTL/LRU keyed cache for tafsir and verse lookups.
Tafsir registry, validation, and retrieval
tafsir.py
Adds tafsir work metadata, reference parsing and bounds checks, intent detection, Quran.com retrieval, language fallback, attribution, caching, and structured responses.
API and chat wiring
main.py, tafsir.py
Adds tafsir routes, extends chat schemas, retrieves tafsir context for verse questions, and incorporates tafsir instructions into model prompts.
Validation, CI, and documentation
tests/test_tafsir.py, .github/workflows/ci.yml, README.md, data/quran/PROVENANCE.md
Adds offline tests and CI coverage, and documents endpoints, configuration, behavior, caching, and Quran data provenance.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ChatHandler
  participant tafsir_retriever
  participant QuranComTafsirSource
  participant Model
  Client->>ChatHandler: POST /chat with verse question
  ChatHandler->>tafsir_retriever: retrieve tafsir context
  tafsir_retriever->>QuranComTafsirSource: fetch verse and tafsir passages
  QuranComTafsirSource-->>tafsir_retriever: attributed tafsir data
  tafsir_retriever-->>ChatHandler: TafsirContext and TafsirInfo
  ChatHandler->>Model: generate with tafsir instructions
  Model-->>Client: grounded response with tafsir metadata
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.01% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: a new tafsir layer with attributed ayah explanations.
Linked Issues check ✅ Passed The PR implements the requested tafsir endpoint, chat integration, validation, caching, tests, and attribution.
Out of Scope Changes check ✅ Passed The added docs, config, cache, script, and test/workflow updates all support the tafsir feature set.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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: 3

🧹 Nitpick comments (4)
tafsir.py (4)

1099-1102: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Re-raise InvalidReference with exception chaining.

raise HTTPException(...) inside except InvalidReference as exc: drops the original traceback link. flake8-bugbear's B904 flags exactly this pattern, and per path instructions CI enforces flake8-based style checks — worth confirming this rule is active in CI, since it would otherwise fail the build.

🔧 Proposed fix
     except InvalidReference as exc:
-        raise HTTPException(status_code=400, detail=str(exc))
+        raise HTTPException(status_code=400, detail=str(exc)) from exc
As per path instructions, "CI enforces flake8, so style violations fail the build."
🤖 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 `@tafsir.py` around lines 1099 - 1102, Update the InvalidReference handler
around build_tafsir_response to explicitly chain the raised HTTPException from
the caught exception, using the existing exc binding and preserving the 400
status and message detail.

Sources: Path instructions, Linters/SAST tools


582-628: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

New httpx.AsyncClient per request — no connection reuse.

_get opens async with httpx.AsyncClient(timeout=self.timeout) as client: on every single call, and it's called once for the verse fetch and once per tafsir per ayah. A single /tafsir request for a 3-ayah range with the 4 default tafsirs can spin up over a dozen separate clients (each paying its own TCP/TLS handshake to api.quran.com) instead of reusing pooled connections.

Consider holding a module-level (or app-lifespan-scoped) httpx.AsyncClient and reusing it across calls; set_source/tests are unaffected since they substitute FakeTafsirSource entirely.

🤖 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 `@tafsir.py` around lines 582 - 628, The _get method creates a new
httpx.AsyncClient for every Quran API request, preventing connection reuse.
Update the containing client class to own and reuse a single AsyncClient across
_get calls, while preserving the existing timeout and response/error handling;
ensure the client is properly closed through the appropriate lifecycle cleanup.

813-825: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

No length bound on reference/tafsirs in TafsirRequest.

reference: str and tafsirs: Optional[List[str]] accept arbitrarily long input; resolve_requested_tafsirs truncates tafsirs only after the full list is parsed by Pydantic, and reference has no cap before it's regex-matched. Neither is catastrophic (both regexes here are linear, not exponential), but a Field(max_length=...) on each is cheap, self-documenting request-body validation that rejects oversized payloads before they're processed.

🛡️ Proposed fix
     reference: str = Field(
         ...,
         description="Ayah reference: '103:1', a range '103:1-3', or 'Al-Asr 1-3'",
         json_schema_extra={"examples": ["103:1-3", "2:255", "Al-Fatihah 1"]},
+        max_length=64,
     )
     tafsirs: Optional[List[str]] = Field(
         None,
         description=(
             "Tafsir keys to include (see GET /tafsir/sources). "
             f"Defaults to {list(DEFAULT_TAFSIR_KEYS)}."
         ),
+        max_length=MAX_TAFSIRS_PER_REQUEST,
     )
As per path instructions for FastAPI services: "missing Pydantic validation on request bodies" should be flagged.
🤖 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 `@tafsir.py` around lines 813 - 825, Add explicit Pydantic size constraints to
both fields in TafsirRequest: set a reasonable max_length for reference and
constrain tafsirs as a list before resolve_requested_tafsirs processes it, while
preserving the existing optional behavior and descriptions.

Source: Path instructions


714-789: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Sequential await in per-ayah/per-tafsir loops — avoidable latency on a user-facing path.

fetch_tafsirs_for_ayah's for key in keys: loop awaits each tafsir fetch one at a time, and both build_tafsir_response and build_chat_tafsir_context await fetch_verse_text then fetch_tafsirs_for_ayah sequentially for each ayah in the range. For a range request with MAX_AYAT_PER_REQUEST ayat and MAX_TAFSIRS_PER_REQUEST works, that's dozens of sequential round-trips to Quran.com on both the /tafsir endpoint and the /chat grounding path — directly adding to response latency users feel.

Since each fetch is independent (attribution is per-key, order can be preserved by list position), these are good candidates for asyncio.gather — cache hits stay cheap, only genuine upstream misses pay the round-trip, and they'd now run concurrently instead of serially.

Also applies to: 891-922, 996-1031

🤖 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 `@tafsir.py` around lines 714 - 789, Update fetch_tafsirs_for_ayah to schedule
independent per-key tafsir fetches with asyncio.gather while preserving keys
order and each unavailable result. In build_tafsir_response and
build_chat_tafsir_context, run per-ayah verse and tafsir work concurrently and
gather ayah tasks across the requested range, preserving response ordering and
existing cache, fallback, and error semantics.
🤖 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 `@main.py`:
- Around line 162-174: Update tafsir_retriever to wrap the awaitable
build_chat_tafsir_context call in an aggregate timeout, ensuring slow Quran.com
retrieval returns None within the configured limit while preserving the existing
warning and best-effort fallback behavior for timeout or other exceptions.

In `@README.md`:
- Around line 107-128: Update the README JSON example to include the top-level
disclaimer field alongside reference, language, and ayat, using the same shape
and representative value returned by TafsirResponse. Keep the existing tafsir
and unavailable examples unchanged.

In `@tafsir.py`:
- Around line 279-296: The prefix handling in _normalize_surah_name() must also
remove assimilated article forms so names such as “at-tawbah” and “ash-shams”
produce the same canonical key as their lookup counterparts. Update the
normalization logic to reuse the assimilated prefixes accepted by
NAMED_SURAH_PATTERNS[0], while preserving existing punctuation removal,
whitespace collapsing, and al/ul normalization behavior.

---

Nitpick comments:
In `@tafsir.py`:
- Around line 1099-1102: Update the InvalidReference handler around
build_tafsir_response to explicitly chain the raised HTTPException from the
caught exception, using the existing exc binding and preserving the 400 status
and message detail.
- Around line 582-628: The _get method creates a new httpx.AsyncClient for every
Quran API request, preventing connection reuse. Update the containing client
class to own and reuse a single AsyncClient across _get calls, while preserving
the existing timeout and response/error handling; ensure the client is properly
closed through the appropriate lifecycle cleanup.
- Around line 813-825: Add explicit Pydantic size constraints to both fields in
TafsirRequest: set a reasonable max_length for reference and constrain tafsirs
as a list before resolve_requested_tafsirs processes it, while preserving the
existing optional behavior and descriptions.
- Around line 714-789: Update fetch_tafsirs_for_ayah to schedule independent
per-key tafsir fetches with asyncio.gather while preserving keys order and each
unavailable result. In build_tafsir_response and build_chat_tafsir_context, run
per-ayah verse and tafsir work concurrently and gather ayah tasks across the
requested range, preserving response ordering and existing cache, fallback, and
error semantics.
🪄 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: 656582d8-9ce8-427f-bb20-b1a9e1a53212

📥 Commits

Reviewing files that changed from the base of the PR and between 67bf40a and e0ec7cd.

📒 Files selected for processing (11)
  • .env.example
  • .github/workflows/ci.yml
  • README.md
  • data/quran/PROVENANCE.md
  • data/quran/surah_index.json
  • main.py
  • requirements.txt
  • scripts/build_surah_index.py
  • semantic_cache.py
  • tafsir.py
  • tests/test_tafsir.py

Comment thread main.py
Comment thread README.md
Comment thread tafsir.py Outdated
Addresses review feedback on the tafsir layer.

Surah-name normalization only stripped "al"/"ul", so every surah whose
article assimilates into a sun letter normalized to a different key than
the lookup path produced: "surah at-tawbah 5", "as-sajdah", "ash-shams"
and "As-Saff 4" all failed to resolve, and even surah_by_name("Al-Sajdah")
returned None. The article is now stripped in whichever form it was
written, but only when the consonant it assimilated into actually doubles
— so Al-Anfal keeps its "an" rather than becoming "fal". Tests assert
every one of the 114 canonical names and every alias resolves to its own
surah, and that no two surahs share a normalized key.

Retrieval was fully sequential, so a verse-range prompt could stack one
upstream timeout per ayah per work before falling back to an ungrounded
answer. Ayat and the works within an ayah are now fetched concurrently,
and retrieval inside /chat is bounded by TAFSIR_CHAT_TIMEOUT (default
20s): past the budget the turn proceeds ungrounded rather than stalling.
Both paths share one assemble_ayah helper so /tafsir and /chat degrade
identically.

Also documents the disclaimer field in the README response example.

@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.

🧹 Nitpick comments (1)
tests/test_tafsir.py (1)

710-736: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Wall-clock threshold test carries some CI-flakiness risk.

assert elapsed < 0.4 against a documented 0.75s serial baseline gives roughly a 2x margin, which is reasonable but timing-based assertions like this can occasionally flake on busy/shared CI runners (contended CPU, GC pauses, etc.), especially as more tests run in parallel over time. Consider asserting concurrency via recorded call timestamps/overlap (or via source.tafsir_calls/verse_calls ordering) instead of a hard wall-clock bound, or widen the margin further if you want to keep it simple.

🤖 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_tafsir.py` around lines 710 - 736, Replace the wall-clock
assertion in test_retrieval_runs_concurrently_across_ayat with a deterministic
concurrency check using recorded tafsir_calls/verse_calls timestamps or
ordering, verifying independent ayat retrieval overlaps rather than relying on
elapsed < 0.4. If retaining timing-based validation, widen the threshold to
reduce CI flakiness while still distinguishing it from the documented serialized
baseline.
🤖 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.

Nitpick comments:
In `@tests/test_tafsir.py`:
- Around line 710-736: Replace the wall-clock assertion in
test_retrieval_runs_concurrently_across_ayat with a deterministic concurrency
check using recorded tafsir_calls/verse_calls timestamps or ordering, verifying
independent ayat retrieval overlaps rather than relying on elapsed < 0.4. If
retaining timing-based validation, widen the threshold to reduce CI flakiness
while still distinguishing it from the documented serialized baseline.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 61399c27-22b0-4420-8a90-a426b0e1612e

📥 Commits

Reviewing files that changed from the base of the PR and between e0ec7cd and 5e3a2a4.

📒 Files selected for processing (4)
  • .env.example
  • README.md
  • tafsir.py
  • tests/test_tafsir.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • .env.example
  • README.md

@zeemscript
zeemscript changed the base branch from dev to main July 23, 2026 20:40
@zeemscript
zeemscript changed the base branch from main to dev July 23, 2026 20:40
@zeemscript

Copy link
Copy Markdown
Contributor

@Fury03 please fix conflict

Resolves conflicts with the confidence and scholar-review layer (Deen-Bridge#56),
which landed on dev while this branch was in review. Both features touch
the same files; nothing was dropped from either side:

- main.py: both routers registered, both import blocks kept, and
  ChatResponse carries both the tafsir and confidence blocks. In the chat
  handler the two paths compose in order — tafsir retrieval runs first and
  marks a grounded turn non-cacheable, then confidence assessment,
  abstention and scholar queueing run on the generated answer, and the
  cache write is gated on both conditions.
- ci.yml: lint and compile cover both feature's modules; both test steps
  kept.
- requirements.txt, .env.example, README.md: both features' entries and
  sections merged.

Full suite green with both features present: 462 passed, 1 skipped.
@Fury03

Fury03 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

@zeemscript conflicts resolved in bf04442 — the branch is mergeable again and CI is green.

The conflicts came from #56 (confidence + scholar review) landing on dev while this was in review; both PRs touch the same five files. I merged dev in rather than rebasing, so the review history above stays attached to the commits it was written against. Nothing was dropped from either side:

  • main.py — both routers registered, both import blocks kept, and ChatResponse carries both the tafsir and confidence blocks. In the chat handler the two paths compose in order: tafsir retrieval runs first and marks a grounded turn non-cacheable, then confidence assessment / abstention / scholar queueing run on the generated answer, and the cache write is gated on both conditions.
  • ci.yml — lint and compile cover both features' modules; both test steps kept.
  • requirements.txt, .env.example, README.md — both features' entries and sections merged.

Verified beyond the test suite by driving the merged chat handler end-to-end against a stubbed model with live tafsir retrieval: "What does Surah al-Asr mean?" returns all three ayat grounded in four attributed works with the grounding block reaching the prompt, and a mixed tafsir+fiqh question ("Explain 2:275 and tell me the ruling on riba") correctly triggers both the tafsir path and the fiqh path in one turn. Full suite with both features present: 462 passed, 1 skipped.

One composition note, deliberately left out of the conflict fix since it changes behaviour rather than resolving a conflict: a tafsir-grounded answer currently scores in the uncertain band, because retrieval grounding isn't one of the confidence signals — so it gets the "please verify this" note. That's defensible as-is (real sources don't verify the model's synthesis of them), but if you'd like retrieval grounding to count as a corroboration signal in confidence.py, say the word and I'll open it as a small follow-up rather than folding it in here.

@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.

Caution

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

⚠️ Outside diff range comments (2)
README.md (2)

222-222: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the invalid-reference example wording.

2:300 is a 400 naming…” is malformed and does not clearly state that the API returns HTTP 400. This could confuse clients relying on the documented validation contract.

📝 Proposed fix
-  so `2:300` is a `400` naming Al-Baqarah's 286 ayat — never an invented verse.
+  so `2:300` returns a `400` naming Al-Baqarah's 286 ayat — never an invented verse.
🤖 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 `@README.md` at line 222, Update the invalid-reference example wording in the
README so it clearly states that requesting 2:300 returns HTTP 400 because
Al-Baqarah has only 286 ayat; remove the malformed “is a 400” phrasing while
preserving the validation-contract meaning.

125-125: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language identifier to the fenced code block.

This triggers MD040. Mark the block as text (or another appropriate language) so Markdown tooling can validate it consistently.

🛠️ Proposed fix
-```
+```text
🤖 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 `@README.md` at line 125, Update the fenced code block in README.md to include
an explicit language identifier, using text or another appropriate language, so
Markdown linting satisfies MD040.

Source: Linters/SAST tools

🤖 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.

Outside diff comments:
In `@README.md`:
- Line 222: Update the invalid-reference example wording in the README so it
clearly states that requesting 2:300 returns HTTP 400 because Al-Baqarah has
only 286 ayat; remove the malformed “is a 400” phrasing while preserving the
validation-contract meaning.
- Line 125: Update the fenced code block in README.md to include an explicit
language identifier, using text or another appropriate language, so Markdown
linting satisfies MD040.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 39eba816-ca9a-4363-b9d3-dd3c0466c2b7

📥 Commits

Reviewing files that changed from the base of the PR and between 5e3a2a4 and bf04442.

📒 Files selected for processing (5)
  • .env.example
  • .github/workflows/ci.yml
  • README.md
  • main.py
  • requirements.txt
🚧 Files skipped from review as they are similar to previous changes (4)
  • requirements.txt
  • .env.example
  • .github/workflows/ci.yml
  • main.py

@zeemscript
zeemscript merged commit 06981f2 into Deen-Bridge:dev Jul 24, 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.

[Enhancement] Multi-tafsir ayah explanation grounded in named classical tafsirs

2 participants