Text to evidenceLesson 2openfrom namaq
Who owns a page
A reader asked whether /sources lets you read the book, and the answer turned out to be a question about ownership.
A reader asked whether /sources lets you read the book. The answer
turned out to be a question about ownership, and the ownership turned out to be wrong.
The symptom
Open volume 5 of the Siyar in /sources, read every entry the contents lists, and you
have read 128 printed pages of it — in five separate runs, with a 21-page hole after Aisha's
entry that nothing on screen mentions. Volumes 1 and 2 are complete. Volume 3 and volumes
6 through 28 are marked لم يُقرأ بعد, honestly. Only the holes
inside a volume that has been read are silent.
The interesting part is why the same UI is honest at one level and silent at the other. It isn't an oversight. The volume list can be honest because the edition declares its volumes, so something exists to be missing from. Inside a volume, nothing declares the pages — so there is no absence to detect.
Follow it down
Why does nothing declare the pages? Because until now a page belonged to the account that extracted it:
SourceAccountPage {
accountId // the entry this page was read for
sequence // its position within that entry
printedPage // nullable
}
A page's identity was (entry, ordinal). The book's own coordinates — volume and printed page — were along for the ride. So the reader could page through an entry and had no notion of paging through a volume, and a volume had no notion of which of its pages existed.
The evidence that settled it
Printed page 230 of volume 4 exists twice, at the same Shamela id 1656: 413 characters under Zaid ibn Harithah, 402 under Abdullah ibn Rawahah. Neither contains the other. The entry boundary falls inside the page, so each batch kept its own half.
Zaid's copy, tail: … قَالَتْ: أَنَا لِزَيْدِ بنِ حَارِثَةَ. إِسْنَادُهُ حَسَنٌ.
Rawahah's copy, head: ٣٧ - عَبْدُ اللهِ بنُ رَوَاحَةَ بنِ ثَعْلَبَةَ …
Forty-one pages are split this way, across 67 of the 88 batches, and three more are held twice in full where the work places a short entry inside a longer one. Which means a printed page's text depended on who read it — the same mistake ADR 0010 already rules out for values, arrived at from a different direction.
The decision
A printed page is a fact about the edition, so it belongs to the volume:
SourceVolume ──owns──> SourcePage (volumeId, printedPage) one page, one text
SourceAccount ─spans─> a range of them
Citation ──> Passage ──> Page ──> Volume (the account is not in this chain)
The entry survives — it is a real thing in the book, and notInSource needs a bearer
— but it stops owning text and becomes a span. Read
ADR 0018 for the argument and
the plan for the work.
What falls out for free
Once a page is keyed by volume and printed page, a passage anchor can be derived —
paragraph n of the page is <volume>/<printedPage>-p<n> —
instead of declared in a list and zipped to the text by position. That deletes the drift class
lesson 0001 is about: not by guarding it, but by
removing the second copy that could fall out of step. Stable anchors also stop
Citation.passageId churning on every re-import.
Retrieval
Why could the contents view mark an unread volume but not an unread stretch inside one?
Retrieval
Under the new model, what does Hamzah's account span?
The move worth stealing
The question you asked was about a reading experience. The answer was a key: a page was identified by who read it rather than by where it sits in the book. Every symptom — no continuous reading, no detectable gaps, split text, five misfilings nothing caught — followed from that one choice, and all of them resolve together when the key changes.
When a UI can't tell you something, it is often because the model was never asked to know it.
Read next
Primary source: ADR 0018, then the plan — read the acceptance criteria as a description of what "the book is readable" means concretely.
Card: Source pipeline card, which still describes the model as it stands today.