Citing What You Read
Last updated: 8 September 2026
A book on these shelves is a note, and a source is the outside work
that note read. The two words are close enough to trip on, so this
page keeps them apart: a book is ours and lives in
knowledge/; a source belongs to somebody else and lives
as a record in sources/. What follows is how a book says
which source it read, and why saying it twice is the thing the design
removes.
Two strings that drifted
A document written in this fleet last week cited
Maslow (1965) twice in its body, for a paper published in
1943, and its reference list carried no Maslow entry at all. Three
more defects of the same shape sat in the same document: a reference
entry dated 1996 against the document's own in-text 1986, a body year
of 1972 against six catalogue records reading 1969, and ten authors
cited in the body against a list of fourteen entries holding none of
them.
None of those is an error of knowledge. Nobody was confused about when Maslow published. They are divergences between restatements: the same source was written down by hand in more than one place, the copies were never joined, and nothing could compare them. "The body and the reference list agree" was a property maintained by attention, and attention is the thing that runs out at two in the morning.
That is the class of failure this page is about, and it is worth naming precisely, because the fix follows from the naming. The bibliography line is printed by the tool that mints the source record and compared against it byte for byte, so a citation that disagrees with its source is a state the data cannot enter rather than a mistake somebody has to notice.
A source is an object
One source becomes one file, with an opaque key on it. Every mention of that source anywhere in the base is a pointer at the file rather than a fresh retelling of what the file says. The reference list at the foot of a note is then a projection of the pointers rather than another copy of the facts, and a restatement can drift with nothing noticing, while a projection cannot drift without failing a check.
The shelf mark
The key is ten characters drawn from
23456789ABCDEFGHIJKLMNPQRSTUVWXYZ, thirty-three
characters with 0, 1 and O
taken out. That alphabet is Zotero's, and it is taken for the reason
it exists: a key gets read aloud, written on paper and retyped off a
screen, and each of those three has a partner it will be confused
with. Nothing else about the string carries meaning. It is a shelf
mark, and a shelf mark tells you where the book is, not what is in it.
It is never derived from the content, the title, the author,
the year or the path, and it is never regenerated. The
popular alternative is a formula key such as
maslow_motivation_1943, which reads beautifully until the
day somebody fixes a misspelled author name and every pointer in the
base is now aimed at a key that no longer exists. A key that changes
when you correct a field is the identity bug wearing a better face.
Why ten characters
Zotero uses eight and can afford to, because it enforces a uniqueness constraint at insert against a library one process reads whole. Here there is no such process. Bases do not see one another, a private base is not readable from outside it, and the whole fleet is meant to restore onto a machine that has never met the others, so uniqueness has to come from the draw and not from a constraint. The number that decides the length is then the birthday bound over the population a key will ever share a namespace with.
| Length | Keys available | Collision at 100,000 | Collision at 1,000,000 |
|---|---|---|---|
| 8 | 1.41e12 | 1 in 280 | 1 in 2.8 |
| 10 | 1.53e15 | 1 in 306,000 | 1 in 3,060 |
| 12 | 1.67e18 | 1 in 3.3e8 | 1 in 3.3e6 |
The scale in the last two columns is the honest part. The busiest week this fleet has had read sixty-three sources; sustained at three times that rate forever, one person reaches roughly a hundred thousand records in fifty years. Ten characters put that at one chance in 306,000, and survive being wrong about the scale by a factor of ten. Twelve buys three more orders of magnitude and spends them on a token nobody can hold in one glance, which undoes the reason the alphabet was chosen. Ten reads as two groups of five.
The draw comes from the operating system's generator, with modulo bias rejected rather than tolerated. The clock was refused for a measurable reason: Windows' default timer granularity is milliseconds at best and often about fifteen, so two sessions minting inside one tick would collide.
The record
A record is sources/<KEY>.txt, in the same
key = value shape as agent.txt and
kb-aliases.txt. It is minted by kb source add
from arguments, so there is no template to copy a placeholder out of,
and that command prints the bibliography line the note will carry.
$ kb source add zed --type article --title "A Theory of Human Motivation" --author "Maslow, A. H." --year 1943 --container "Psychological Review 50" --pages "370-396" --url "https://psychclassics.yorku.ca/Maslow/motivation.htm" --retrieved-on 2026-09-06 --retrieval-status full
wrote zed\sources\YYUFQ78X4U.txt
Cite it as [src:YYUFQ78X4U], and carry one bibliography line:
- [src:YYUFQ78X4U] Maslow, A. H. "A Theory of Human Motivation". Psychological Review 50, 370-396. 1943. https://psychclassics.yorku.ca/Maslow/motivation.htm. read in full, 2026-09-06.
The file it wrote holds the fields and nothing else:
type = article
title = A Theory of Human Motivation
author = Maslow, A. H.
year = 1943
container = Psychological Review 50
pages = 370-396
url = https://psychclassics.yorku.ca/Maslow/motivation.htm
retrieved_on = 2026-09-06
retrieval_status = full
type is one of six, book,
chapter, article, report,
webpage, recording, and not forty: forty
types exist to feed style processors, and there is no style processor
here. retrieval_status is one of full,
partial, metadata,
unreachable, and it is required, because
metadata is the word for "I saw the catalogue
entry and never opened the thing". That sentence was needed
the first week it existed. The honest boundary, stated rather than
glossed: a required field removes the case where nobody was ever
asked, and it does not remove lying.
Note. One file per source, never one shared ledger. More than one session writes these repositories at the same time, and two sessions appending to one ledger conflict on every busy day. The path is derived from the key and never the reverse.
What a note looks like
The pointer sits in the body where the claim is, and one line under
## Sources carries the copy. Both are shown here rather
than described, because the whole point is that the second one is a
paste and not an invention. The bibliography line is one line however
long it gets, so it scrolls inside the block below.
The paper proposes that human needs are ordered and that a satisfied
need stops motivating [src:YYUFQ78X4U]. It does not claim the order is
fixed across cultures, it does not present data, and the pyramid
drawing that carries its name appears nowhere in it.
## Sources
- [src:YYUFQ78X4U] Maslow, A. H. "A Theory of Human Motivation". Psychological Review 50, 370-396. 1943. https://psychclassics.yorku.ca/Maslow/motivation.htm. read in full, 2026-09-06.
Single brackets, and that is not cosmetic. Double brackets are this
base's wikilink and carry exactly one meaning, a pointer to a note in
this base resolved by file stem; overloading them would make one
syntax mean two things and force the linter, the router, an editor and
a person to each learn the exception. [1] is already what
a citation marker looks like, and [text](url) cannot
collide with a token that has to open with src:. Like a
wikilink, a citation resolves inside its own base and nowhere else,
for the privacy reason and not for a tidiness one.
The rendered line is a canonical form and not a citation style. Every title is quoted, including a book's, which no style would do, because equality has to be decidable by a machine and readable by a person at the same time. Style is what the export below is for.
What the checks refuse
kb check gained four codes for citations and one for the
records themselves. Each is stated here as the situation it catches
rather than as the condition it tests; the
reference carries the codes
in a table with the rest.
E05- You cited a key and there is no record behind it. A citation is a pointer, so a key pointing at nothing is a claim with no source, and the base cannot tell whether you meant a record you never made or one you deleted.
E06- You made a record and no note cites it. The bibliography is a projection of the pointers, so a record nothing points at appears in no bibliography and answers no question. It is an error rather than a warning because a warning accumulates into a list nobody reconciles, which is what the hand written ledgers it replaced had already become.
E08- You cited a key and the bibliography line is missing, or it says something the record does not. This is the load-bearing one, and the next section is about it.
E09-
There is a file under
sources/that is not a record. Without it, a dropped file blames the notes for a defect that is in the folder. W09- You cited a source that another record supersedes. A warning and not an error, because citing what you actually read is legitimate.
The one that catches Maslow
The note below is correct in every other way. Its front matter is complete, its keyword line is long, its claim is sound, and its bibliography line says 1965 where the record says 1943. That is the defect that shipped, reproduced.
$ kb check zed
zed (5 public files, map: none)
error E08 knowledge/motivation.md:21 the bibliography line for [src:YYUFQ78X4U] has drifted from the record. The record says:
- [src:YYUFQ78X4U] Maslow, A. H. "A Theory of Human Motivation". Psychological Review 50, 370-396. 1943. https://psychclassics.yorku.ca/Maslow/motivation.htm. read in full, 2026-09-06.
1 errors, 0 warnings
Nobody had to notice. The comparison is byte for byte against a string
the tool itself printed, so the only way to satisfy the check is to
paste what the record renders, and the only way to change what it
renders is to change the record, which every pointer already reads.
Without E08 the embedded copy would be exactly
the free prose paragraph it replaces, in a new place.
A correction supersedes
The common case is not a source that was wrong. It is a source that
was right and has been improved on: a preprint that became a paper, a
page you read again with a DOI in hand, an edition that replaced the
one you had. A record can declare replaces = <OLDKEY>,
and three consequences follow, which together are the whole feature.
- The old key keeps resolving. The old record is never deleted, so no note that cites it breaks.
- A superseded record is exempt from
E06. Without that, correcting a source would start failing the moment the last note moved to the new key, which is the opposite of what a correction should cost. - A note still citing the old key gets
W09, naming the replacement. A warning, because an error would force somebody to rewrite the history of their own reading.
$ kb source add zed --type article --title "A Theory of Human Motivation" --author "Maslow, A. H." --year 1943 --container "Psychological Review 50" --pages "370-396" --doi "10.1037/h0054346" --retrieved-on 2026-09-08 --retrieval-status full --replaces YYUFQ78X4U
wrote zed\sources\LUDPV5YVC5.txt
$ kb check zed
zed (5 public files, map: none)
warn W09 knowledge/motivation.md:15 [src:YYUFQ78X4U] is superseded by [src:LUDPV5YVC5], "A Theory of Human Motivation". The old key still resolves, so this is a correction to consider and not a broken link
error E06 sources/LUDPV5YVC5.txt no note cites [src:LUDPV5YVC5], "A Theory of Human Motivation". The bibliography is a projection of the pointers, so a record nothing points at is in no bibliography and answers no question. Cite it or delete it
1 errors, 1 warnings
The pointer goes one direction only. A back pointer on the old record would be a second fact free to disagree with the first, which is the failure this whole design removes. This is what "Maslow 1965 is wrong, 1943 is right" should have produced, instead of two notes and a sentence joining them.
Leaving: CSL JSON
kb sources --json emits a CSL JSON array, the shape
Zotero, Pandoc and every reference processor already read. It is there
from the first commit for one reason: it costs nothing today and it
means the records can leave this system without a converter written
under pressure. One record from the run above, indented here in the
key order the tool emits; the tool prints the whole array on one line.
{
"URL": "https://psychclassics.yorku.ca/Maslow/motivation.htm",
"accessed": { "date-parts": [ [ 2026, 9, 6 ] ] },
"author": [ { "family": "Maslow", "given": "A. H." } ],
"container-title": "Psychological Review 50",
"custom": { "retrieval_status": "full" },
"id": "YYUFQ78X4U",
"issued": { "date-parts": [ [ 1943 ] ] },
"page": "370-396",
"title": "A Theory of Human Motivation",
"type": "article-journal"
}
accessed carries retrieved_on, which is the
field CSL has for exactly the thing the old prose paragraphs were
retyping by hand. retrieval_status and
replaces travel under custom, which is where
CSL passes fields through untouched. It is an export and not
an import. Nothing here reads CSL back, because an importer
is a translator and a translator is a second thing to maintain.
What was taken from Zotero, and what was not
The identity model is Zotero's, and saying so costs nothing and gives a reader a reference point they may already have. Three things were taken, and they are the three that matter:
- The opaque key, and its alphabet, for the transcription reason above.
- A pointer instead of a restatement, which is the mechanism the whole page is about: an item is an object, and every mention of it points at the object.
- Supersession, which Zotero carries as a
dc:replacesrelation between items.
Everything else was left where it was, and the list of what was left is the more useful half, because it says what this is not trying to be. No style engine and no CSL processor, so nothing here will format a bibliography for a journal. No browser connector and no translators, so nothing here will scrape a publisher's page into a record for you; a record is typed out once. No sync and no blob storage, so there is no snapshot of the PDF and no account.
Using Zotero itself as the store was the other real option, and it was
refused for a stated reason rather than by reflex. Everything needed
exists: a local HTTP API, a CSL JSON format parameter, a translator
that already emits item links into markdown. The cost is that a
graphical application would have to be running for kb check
to pass, and an application runtime would become a dependency of a
binary that has one dependency on purpose. The memory would
stop being readable without a vendor's runtime, which is the
one thing this product is not allowed to do.
Careful. Zotero was read from its published source on 8 September 2026 and nothing was run, so every claim here about how Zotero works is a reading and not a measurement. The claims about kb on this page are runs, and the plates carry their date and commit.
Nothing is converted yet
This is a writing convention that starts today and enforces nothing on what was written before it. Notes in this fleet still cite in a hand written prose paragraph at their foot, and they still work. What changed for them is that the check demanding an evidence grade stopped being opt in: it used to ask whether the front matter declared a source field, which is a key a writer adds or does not, so a base that declared none was never graded. It now asks a question about the body, namely whether the note cites anything at all, by pointer or in prose.
Measured on the private fleet the authors run, which is a corpus a reader cannot reproduce: that condition took the ungraded-note warning from zero findings to fifty-eight, on exactly the twenty-nine notes that cite in prose, two findings each. Every other check on that fleet printed an identical count before and after. The stronger rule, every note in the knowledge folder, was considered and refused: it fires on 188 of 204 notes on the day it lands, which turns the linter into a wall people learn to scroll past.
The migration is one note at a time, and it is reversible at
every step. A half converted note fails E08 or
E06 loudly, which is the property that makes doing them
one at a time safe rather than reckless.
- Run
kb source addonce per source in the paragraph.retrieval_statusis the field to think about, because several of those paragraphs already say "metadata only, paywalled, not read" in prose, and that sentence has a value now. - Replace the prose mention in the body with
[src:KEY]. - Replace the paragraph with one bibliography line per source, pasted from what
kb source addprinted. - Add
evidence_tierandvalid_forto the front matter, which the check is now asking for. kb checkgoes clean on that note, andE06stops firing for those records.
The reasoning behind every decision on this page, including the two options that were refused and the fact that would make us reopen the key length, is ADR-0042. The verbs and the check codes are on the Reference.
Previous: Concepts · Next: How the Catalogue Ranks · All of the docs · Back to the library