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
Arithmetic, not a measurement: the birthday bound over a population of independently drawn keys, computed 8 September 2026 and recorded in ADR-0042. The two populations are one person's fleet over fifty years and the same figure with a factor of ten of headroom.

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.
Real output, 8 September 2026, commit 645bd0a, release build on Windows 11, over a copy of examples/demo. The command line and the line it printed are both longer than the plate and scroll.

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
Real output, 8 September 2026, commit 645bd0a, release build on Windows 11, over a copy of examples/demo with one wrong year in one bibliography line. The finding and the record's own line are longer than the plate and scroll. Exit code 1.

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.

$ 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
Real output, 8 September 2026, commit 645bd0a, release build on Windows 11, over a copy of examples/demo. Two commands in one plate, with the first command's Cite it as lines left out because the section above already shows them; the command line and the two findings are longer than the plate and scroll. The E06 is the new record waiting to be cited, and it goes when the note moves to the new key.

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:

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.

  1. Run kb source add once per source in the paragraph. retrieval_status is 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.
  2. Replace the prose mention in the body with [src:KEY].
  3. Replace the paragraph with one bibliography line per source, pasted from what kb source add printed.
  4. Add evidence_tier and valid_for to the front matter, which the check is now asking for.
  5. kb check goes clean on that note, and E06 stops 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