Isnād–Rijāl Framework
Grade the narrators, not just log them. Claim-level provenance for multi-agent knowledge systems — adapted from classical hadith transmission science.
Paper: arXiv:2607.24117 | Paper DOI: 10.48550/arXiv.2607.24117 | Software DOI: 10.5281/zenodo.21216873
🌐 Full project home: https://alizahidraja.com/isnad
What & Why
In modern AI pipelines, a factual claim passes through many hands — a scraper extracts it, a model compiles it, another serves it. Each hand can drop, distort, or invent. Existing tools record what happened. ISNAD grades who transformed the claim, so it can tell you how much to trust the result.
The framework adapts hadith transmission science — one of history's most rigorous epistemologies, refined over twelve centuries — into a Python library for AI systems. Every claim carries its complete chain of transmitters (isnād); each transmitter is graded in a living registry (rijāl); chains are evaluated by their weakest link; content is criticized independently of transmission quality; and the two combine in a decision matrix that routes claims to serve, review, or quarantine.
60-Second Quickstart
pip install isnad
from isnad import Registry, Chain, ChainLinkSpec, grade_chain, decide
from isnad.types import NarratorGrade, ContentVerdict
from isnad.critics import EmbeddingCritic
# Build a chain: source → scraper → model
chain = Chain([
ChainLinkSpec("openstax-textbook", 0, domain="physics"),
ChainLinkSpec("pdf-scraper-v2", 1),
ChainLinkSpec("ingest-model-v3", 2),
])
# Seed-grade known narrators (required for coverage — see §8 experiment)
reg = Registry()
reg.register("openstax-textbook", "physics", grade=NarratorGrade.RELIABLE)
reg.register("pdf-scraper-v2", "physics", grade=NarratorGrade.RELIABLE)
reg.register("ingest-model-v3", "physics", grade=NarratorGrade.ACCEPTABLE)
# Grade the chain
grades = [reg.get_grade(l.narrator_id, l.domain) for l in chain.links]
transforms = [l.transform_type for l in chain.links]
chain_grade = grade_chain(grades, transforms, is_complete=True)
# Content criticism (now functional — embedding-based)
critic = EmbeddingCritic()
verdict = critic.evaluate("p = h/λ", "p = h/lambda", ["p = mv"])
action = decide(chain_grade, verdict)
print(f"Chain: {chain_grade.value.upper()} | Content: {verdict.value} | Action: {action.value}")
LangChain Integration (5 lines)
pip install isnad[langchain]
from isnad.integrations.langchain import IsnadCallbackHandler, seed_registry
reg = seed_registry({"source:docs": "reliable", "model:gpt-4o": "acceptable"})
handler = IsnadCallbackHandler(registry=reg, domain="physics")
chain.invoke("What is F=ma?", config={"callbacks": [handler]})
trace = handler.to_trace() # isnad_trace v0.1 JSON
Also available: IsnadTracer (older flat-list reporter with built-in report()) and
AsyncIsnadCallbackHandler for async pipelines.
Citation
If you use ISNAD in your research, please cite:
@article{raja2026grading,
author = {Ali Zahid Raja},
title = {Grading the Narrators: An Isnād–Rijāl Framework for
Claim-Level Provenance in Multi-Agent Knowledge Systems},
year = 2026,
doi = {10.48550/arXiv.2607.24117},
eprint = {2607.24117},
archivePrefix = {arXiv},
}
@software{raja2026isnad,
author = {Ali Zahid Raja},
title = {Isnād–Rijāl Framework: Reference Implementation},
year = 2026,
doi = {10.5281/zenodo.21216873},
orcid = {0009-0003-7875-4590},
}
GitHub users: click "Cite this repository" on the repo sidebar (powered by CITATION.cff).
What's Validated vs. What's Not
| Component | Status | Notes |
|---|---|---|
| Bayesian grading | ✅ Default | Beta-distribution replaces hardcoded thresholds; ISNAD_POLICY env override |
| Weakest-link quarantine | ✅ Validated | 100% of REJECTED narrator claims correctly blocked |
| jarḥ–taʿdīl discovery | ✅ Partial | Correctly identifies bad narrators; good ones need seed grades |
| Seed-grade bootstrapping | ✅ Validated | Pre-grading sources/models improves coverage from ~5% to ~10%; critical for non-zero serving; ISNAD_SEED_CONFIG env var |
| Corroboration (mutābaʿāt) | ✅ Empirically validated | 603/603 (100%) on Wikipedia; 104/104 (100%) on physics textbooks; 8/8 negative controls pass; madār detection blocks correlated chains |
| Content criticism | ✅ Functional | EmbeddingCritic (TF-IDF) catches contradictions offline; HybridCritic (NLI) + LLMCritic available |
| Semantic matching | ✅ Validated | Cross-source embedding matching (MiniLM) across Wikipedia and physics corpora |
| LangChain integration | ✅ Ready | IsnadTracer callback handler, seed_registry helper, 9 integration tests pass |
| Confidence-gating | ❌ Useless | Self-confidence scores uncorrelated with defects |
The honesty box is a feature. We tell you exactly what works, what's limited, and where you need to supply your own components.
Endpoint identity (model version drift)
For model narrators, grades are keyed by alias@version when a chain link
supplies a resolved version (e.g. ingest-model-v3@2.0). Deploying a new model
behind the same service name creates a new registry identity — track record
does not carry forward. That reset is intentional (paper §4.2).
- Register with
model_versionvia the API orRegistry.register_versioned() - Pass
versionon each chain link (or use@versionin seed keys for LangChain) - Legacy alias-only registrations still work when
versionis omitted,unknown, or a non-resolved tag (latest,dev,canary)
Demo: uv run python examples/endpoint_identity_drift_demo.py
Concept → Module Map
| Concept | What it does | Module |
|---|---|---|
| isnād (chain) | Ordered, gap-checked transmission chain per claim | isnad/core/chain.py |
| rijāl (registry) | Graded narrator store per (alias@version, domain) | isnad/core/registry.py |
| jarḥ–taʿdīl | Evidence-driven state machine for narrator grades | isnad/core/registry.py |
| Bayesian grading | Beta-distribution narrator grades (default) | isnad/core/registry.py |
| ittiṣāl | Completeness as epistemic property (gap → DAIF) | isnad/core/chain.py |
| Weakest-link grading | Chain grade = refined minimum over narrators | isnad/core/grading.py |
| mutābaʿāt (corroboration) | Independent-chain upgrade + madār detection | isnad/core/corroboration.py |
| matn criticism | Content evaluated independently of chain quality | isnad/critics/ |
| Decision matrix | 4×2 (chain × content) → action router | isnad/core/decision.py |
| Persistence | SQLAlchemy-backed registry (swap via protocol) | isnad/storage/ |
| API | FastAPI service with DI + Prometheus metrics | isnad/api/ |
| CLI | isnad serve |
isnad seed |
| ʿadālah / ḍabṭ | Integrity and precision as two distinct axes | isnad/types.py |
🗺️ Full architecture diagram:
docs/ARCHITECTURE.drawio— 3 tabs: System Architecture, Claim Lifecycle (data flow), and Validation Matrix (what's proven vs what's not). Open in draw.io or VS Code Draw.io extension.
The Decision Matrix
| Content CONSISTENT | Content CONTRADICTION | |
|---|---|---|
| Ṣaḥīḥ (sound chain) | SERVE — cache | REVIEW — ʿilal signal (highest-value case) |
| Ḥasan (good chain) | SERVE WITH CAVEAT | REVIEW — hold, do not serve |
| Ḍaʿīf (weak chain) | REVIEW — seek corroboration | QUARANTINE |
| Mawḍūʿ (fabricated) | REJECT + QUARANTINE NARRATOR | REJECT + QUARANTINE NARRATOR |
Pluggable Strategies — Extend It
The framework leaves key parameters open by design (paper §4.2/§4.3). Swap any:
| Strategy | Protocol | Default | What to provide |
|---|---|---|---|
GradingStrategy |
isnad/types.py |
RefinedWeakestLink |
How links combine into chain grade |
TransitionPolicy |
isnad/types.py |
BayesianTransitionPolicy |
Evidence → narrator grade transitions |
CorroborationPolicy |
isnad/types.py |
CappedCorroborationPolicy |
Independent chains → claim upgrade |
CorrelationDetector |
isnad/types.py |
SharedLineageDetector |
True independence between chains |
ContentCritic |
isnad/critics/base.py |
HybridCritic / EmbeddingCritic |
Content contradiction detection |
Swap a critic in one line:
from isnad.critics import EmbeddingCritic, LLMCritic
critic = EmbeddingCritic() # offline, fast
critic = LLMCritic(api_key="sk-...") # LLM-backed, higher quality
Good first issues:
- Implement an alternative critic (sentence-transformers embedding, CrewAI integration)
- Seed-grade bootstrapper from published benchmark data
- Extend semantic corroboration to multi-source corpora (ArXiv, textbooks, news)
Trace Capture & Chain Viewer
ISNAD now ships a capture → schema → viewer pipeline that makes transmission chains visible. The contract is isnad_trace v0.1 — a versioned JSON schema aligned with W3C PROV-DM and PROV-AGENT (arXiv 2508.02866).
Capture (LangChain callback)
from isnad.integrations.langchain import IsnadCallbackHandler, seed_registry
reg = seed_registry({"source:my-docs": "reliable", "model:gpt-4o": "acceptable"})
handler = IsnadCallbackHandler(registry=reg, domain="physics")
# Attach to any LangChain/LangGraph pipeline
chain.invoke("What is F=ma?", config={"callbacks": [handler]})
trace = handler.to_trace()
print(trace.model_dump_json(indent=2)) # isnad_trace v0.1
Key properties:
- Tree from run_id/parent_run_id — LangChain already provides the tree; the handler reconstructs it without timestamps or heuristics.
- Input provenance — every retrieved document is recorded with source, doc_id, and content hash. Full content is redacted by default.
- Model version captured — resolved model version (e.g.
gpt-4o-2024-08-06) from response metadata, not the endpoint alias. Recordsnullexplicitly when unavailable. - Shared ancestry detection — overlapping retrieval sets, shared model
families, or shared upstream sources are flagged as
shared_ancestry_detected. - Never breaks your pipeline — every callback is wrapped in try/except.
- Async support —
AsyncIsnadCallbackHandlerfor async LangChain runs.
Runnable demo (no API keys required):
python examples/isnad_langchain_demo.py
Viewer
Open viewer/index.html in a browser. Three hand-built fixtures demonstrate
the framework's key signals:
| Fixture | What it shows |
|---|---|
| 1. Clean chain | Ṣaḥīḥ-tier chain, verified independent corroboration (OpenStax + HyperPhysics). Baseline. |
| 2. Weak extraction | Ḍaʿīf chain (gpt-3.5-turbo at 18% error rate) but verified origin. Two axes kept separate. |
| 3. False corroboration | Five transmitters across three chains, all tracing to one NOAA source. Renders as a warning, not consensus. |
The viewer renders fixture 3 by default — it is the most important case.
What the viewer shows — and what it doesn't
Validated signals (mechanisms confirmed empirically or structurally):
| Signal | Status | Source |
|---|---|---|
| Weakest-link chain grading | ✅ Validated | §8 experiment: 100% of REJECTED narrator claims correctly blocked |
| jarḥ–taʿdīl narrator discovery | ✅ Partial | Correctly identifies injected weak narrators; requires seed grades |
| Corroboration negative controls | ✅ Validated | 8/8 correctly rejected; madār detection blocks correlated chains |
| Two-axis separation (chain ≠ origin) | ✅ Structural | Schema enforces separate enums; viewer renders them independently |
| Tree reconstruction from run_id/parent_run_id | ✅ Structural | Tested: linear chains, siblings, missing parents handled safely |
Indicative signals (displayed honestly, not validated):
| Signal | Status | Honest limit |
|---|---|---|
| Independence detection | ⚠ Indicative | Structural only (shared doc hashes, upstream sources, model families). Does not detect correlated training data or shared model blind spots. |
| Narrator grades | ⚠ Indicative | Only calibrated where seed-grade data exists. Cold-start coverage is ~10%. |
| Corroboration fire rate | ⚠ Corpus-dependent | 100% on Wikipedia; rarely fires on dense technical corpora (5/20K). |
| Origin strength | ⚠ Indicative | Derived from ʿadālah grade. No cryptographic attestation (complementary: Live Verify). |
| Content verdict | ⚠ Not captured | The trace schema has space for content_verdict but the callback handler does not populate it — the bundled critic is a stub on real text. |
What is NOT shown:
- No numeric confidence (never
87.3). Grades are ordinal bands: ṣaḥīḥ/ḥasan/ḍaʿīf/mawḍūʿ. - No colour-alone confidence encoding. The viewer respects
prefers-reduced-motionand visible keyboard focus. - No collapsed axes. Chain integrity and origin strength are always separate.
- No implicit corroboration.
unverifiedindependence is not rendered as agreement.
Experimental Validation — Semantic Corroboration (§8)
Status: Empirically validated on real data.
The framework's most distinctive contribution — independent-chain corroboration (mutābaʿāt) — has been validated on two corpora of increasing difficulty:
| Corpus | Sources | Matches | Fire Rate | Difficulty |
|---|---|---|---|---|
| Wikipedia (v2) | Regular + Simple English (30 topics) | 662 | 100% | Easy — natural paraphrasing |
| Physics Textbooks (v3) | OpenStax Vol.1 + Crowell (2 books) | 104 | 100% | Hard — formal prose, fewer overlaps |
Results
| Corpus | Sources | Matches | Fire Rate | Difficulty |
|---|---|---|---|---|
| Wikipedia (v2) | Regular + Simple English (30 topics) | 662 | 100% | Easy |
| Physics Textbooks (v3) | OpenStax Vol.1 + Crowell (2 books) | 104 | 100% | Hard |
Combined: 603 + 104 = 707 claim pairs tested across both corpora. 100% corroboration fire rate. 8/8 negative controls. Zero false positives. Source URLs for every claim.
Key Findings
- Corroboration fires on semantically-matched cross-source data — different text, same meaning, genuinely independent sources
- Independence detector correctly identifies madār — chains with shared model families or upstream sources are blocked from upgrade
- HASAN cap enforced — corroboration never falsely reaches SAHIH
- Information-theoretic weights calibrated — effective weight scales with chain quality (1.5 for single HASAN corroborator, 2.5+ for multiple)
Reproduce
cd experiments/corroboration_v2
pip install isnad sentence-transformers scikit-learn requests
python run.py # Fetch Wikipedia + semantic match + corroborate
python negative_controls.py # Verify all 8 gates
python analyze.py # Statistical analysis
Full methodology, results, negative controls, and paper gap analysis in:
Ecosystem
- 🌐 Site: https://alizahidraja.com/isnad
- 📄 Paper (arXiv): https://arxiv.org/abs/2607.24117
- 📄 Paper (DOI): https://doi.org/10.48550/arXiv.2607.24117
- 💾 Software (DOI): https://doi.org/10.5281/zenodo.21216873
- 📦 PyPI: https://pypi.org/project/isnad/
- 📝 Companion gist: https://gist.github.com/alizahidraja/56beaadf493976182f38aa602b8958e2
- 🧪 §8 Experiment & results:
experiments/s8_gated_vs_ungated/ - 🔬 Semantic Corroboration v2 (Wikipedia):
experiments/corroboration_v2/ - 📚 Corroboration v3 (Physics Textbooks):
experiments/corroboration_v3/— 104/104 on s8 corpus - 🗺️ Architecture Diagram:
docs/ARCHITECTURE.drawio— 3 tabs: System, Data Flow, Validation Matrix - 🔌 LangChain integration:
src/isnad/integrations/langchain/ - 🔗 Trace schema spec:
docs/trace-schema.md— v0.1 with PROV/PROV-AGENT mapping - 👁️ Chain viewer:
viewer/index.html— open in browser, renders all 3 fixtures - 🧪 Trace capture demo:
examples/isnad_langchain_demo.py— runnable without API keys - 📊 Critic evaluation:
src/isnad/critics/CRITIC_EVAL.md
Citation
@article{raja2026grading,
author = {Ali Zahid Raja},
title = {Grading the Narrators: An Isnād–Rijāl Framework for
Claim-Level Provenance in Multi-Agent Knowledge Systems},
year = 2026,
doi = {10.48550/arXiv.2607.24117},
eprint = {2607.24117},
archivePrefix = {arXiv},
}
@software{raja2026isnad,
author = {Ali Zahid Raja},
title = {Isnād–Rijāl Framework: Reference Implementation},
year = 2026,
doi = {10.5281/zenodo.21216873},
orcid = {0009-0003-7875-4590},
}
About
Built by Ali Zahid Raja · ORCID 0009-0003-7875-4590
The rigor belongs to twelve centuries of muḥaddithūn. The transfer to AI systems is the contribution claimed here. Built in public — collaborators welcome.
License: Code — Apache 2.0 · Paper & docs — CC BY 4.0
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file isnad-2.0.7.tar.gz.
File metadata
- Download URL: isnad-2.0.7.tar.gz
- Upload date:
- Size: 74.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9386b8320545464a11c650ed64b75e60b1130b529e3be0c29f0798caf697597b
|
|
| MD5 |
d663cd937ae15e64e0fcde6703a112a8
|
|
| BLAKE2b-256 |
824cba16df8dfcb9bfabf6623df4f43414dab562f21805fa0a914eba0df17f9d
|
File details
Details for the file isnad-2.0.7-py3-none-any.whl.
File metadata
- Download URL: isnad-2.0.7-py3-none-any.whl
- Upload date:
- Size: 92.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
06e6b803113e72389c5dac4f0b51dfe29116695e9e71d7dd66ed9fffeabd9261
|
|
| MD5 |
b3ce98ea9d2d817d740bb6b057f21766
|
|
| BLAKE2b-256 |
a6dfe52f0e1565d709e155fb965315b02f867bf1b19ef04a468666d6fcb33650
|