Distillate

Text to evidenceLesson 1openfrom namaq

Where text becomes citable

A source reaches Namaq as five nested things, and only one of them is a design decision you could still change cheaply.

A source reaches Namaq as five nested things, and only one of them is a design decision you could still change cheaply. This lesson finds that one.

The shape, in one pass

Open data/history/batches/aisha-bint-abi-bakr/ and you have the whole model in front of you: a batch.json, a summary.md, and accounts/aisha-bint-abi-bakr/ holding 67 printed pages as 001.md … 067.md, each with a .notes.md beside it for that page's editorial footnotes.

Nesting, outermost first:

source   one edition of a work (al-Risalah's Siyar, ed. al-Asad) + its 28 volumes
account  that work's entry for one subject  ← unique per (source, subjectKind, subjectSlug)
page     one printed page of that entry     ← bodyMarkdown + notesMarkdown
passage  one paragraph of that page         ← addressed by an anchor: 135-p2
claim    one value the model holds          ← field: "fullName"
citation the pinpoint link between them     ← passageAnchor + excerptArabic

The reader shows the top three. A profile shows the bottom two. Nothing in the app renders a claim without the passage underneath it, which is the point of ADR 0009: evidence attaches to a subject slug, not to a profile page, so a graph-only person can carry citations before anyone writes their profile.

Four of these five are barely decisions

Source, account, page and claim are each pinned by something outside Namaq's control. A page is a page because the book printed it that way — ADR 0016 says so outright, and a citation's pageReference depends on it. An account is unique per subject because a work has one entry per person. A claim must name a field because history:validate rejects one that names nothing.

The passage is the exception. Nothing outside the repo says a paragraph is the citable unit, or that 135-p2 is what addresses it. That was chosen — and the way it was chosen is the interesting part.

The choice: an anchor is bound to its paragraph by position

The page file is plain markdown. It contains no anchors, no ids, no markers:

١٩ - عَائِشَةُ بِنْتُ الصِّدِّيْقِ أَبِي بَكْرٍ التَّيْمِيَّةُ …

بِنْتُ الإِمَامِ الصِّدِّيْقِ الأَكْبَرِ … القُرَشِيَّةُ، التَّيْمِيَّةُ …

وَأُمُّهَا: هِيَ أُمُّ رُوْمَانَ بِنْتُ عَامِرٍ …

The anchors live in batch.json, as a bare list:

"passages": [{ "anchor": "135-p1" }, { "anchor": "135-p2" }, { "anchor": "135-p3" }, …]

And they are joined to the text by index, nothing else (src/lib/history/batchSchema.ts):

export function passageExcerpts(page, files) {
  const paragraphs = (files[page.bodyFile] ?? '').split(/\n{2,}/)…;
  return new Map((page.passages ?? []).map((p, index) => [p.anchor, paragraphs[index] ?? '']));
}

So 135-p2 means "the second paragraph on this page" and means nothing else. The name looks like an identifier; it is an ordinal wearing a costume.

What that buys

The page file stays exactly the book's text. No markup for an extractor to get wrong, no ids to keep in sync by hand, and a re-extraction that reproduces the same paragraphs reproduces the same anchors for free. Compare the alternative — anchors written into the markdown — and you have made the source text a Namaq format, which cuts against the source text is the book.

What it costs, and how the cost is contained

Insert one paragraph in the middle of a page and every anchor after it now excerpts the following paragraph. Citations keep resolving. Nothing errors. They just quietly point one paragraph off.

The validator's answer is a count, not a checksum:

if (declared > 0 && declared !== paragraphs) {
  issues.push({ …, message: `declares ${declared} passage(s) but "${page.bodyFile}" has ${paragraphs} paragraph(s)` });
}

That is the design lesson worth carrying: excerptArabic on the citation is not a convenience copy of the passage. It is the check digit on a positional binding.

Retrieval

A page file gains a paragraph mid-page, and an anchor is appended to the list. What catches it?

Retrieval

Why does the page markdown carry no anchors of its own?

Bring it back to your question

The group headings — شهداء بدر, السابقون الأولون — now have a precise answer rather than a rule of thumb. A heading is an index line with no paragraph, so it yields no passage; with no passage there is no passageAnchor, and a citation without one cites a page and no text. The martyrdom is a real model value, but the evidence for it has to come from a paragraph inside that person's own account, which is where al-Dhahabi actually says it.

The heading is not excluded because it is a heading. It is excluded because the citable unit is a paragraph, and that is the decision you could revisit — PassageKind already distinguishes NOTE from BODY, so a third kind is a small change. Whether it should exist is a design question for a later lesson.

Primary source: docs/data-pipelines.md — the authored-layer map this whole model is described by; read the three hand-authored paths and which of them are one-way. Then ADR 0010 for why files outrank rows at all.

Card: Source pipeline card — the full artifact-to-screen table, the commands, and the rules that decide what may exist.