citations
Check that the passages you quote actually appear in the sources you cite, and keep a library of the ones that do.
Install
pip install citations
Quick start
citations init
citations verify --claims claims/
2,940 quotes
found 2,940
not found 0
warnings
213 short — the source may qualify this in the next clause
155 normalized — matched after ignoring punctuation and spacing
all found.
Commands
| Command | What it does |
|---|---|
citations init |
Create a library here |
citations verify |
Do the quotations resolve in their sources? |
citations audit |
Does the stored metadata match the record the identifier resolves to? |
citations resolve |
Backfill missing DOIs and arXiv ids |
citations build |
Rebuild records from bibliographies |
citations lint |
BibTeX correctness, via papis |
citations link |
Point pdfs/ at the papers' artifacts |
Verify output
Three results, and they are exhaustive:
| Result | Meaning |
|---|---|
found |
The passage is in the source |
not found |
The source was read and the passage is not in it |
unchecked |
The source could not be read, so no measurement was made |
Warnings are separate, because a passage can be found and still worth a second look. A quote
can be short enough that the next clause changes its meaning — "We trained 50" appears
verbatim in a paper whose sentence continues "...and 5 refits each for 12 layered".
unchecked is neither a pass nor a failure. Only not found fails; --strict also fails on
unchecked, for CI.
not found means read the source. A mirror-reversed scan or a two-column extraction produces
the same signal as a passage that was never there.
Audit output
verify asks whether a quotation is in the source. audit asks a different question: does the
author list, year, volume and page range stored beside an identifier match the record that
identifier resolves to?
citations audit --bib paper/references.bib
75 entries
checked 62
agree 36
disagree 26
no id 13 nothing can check these until they have a DOI or PMID
26 disagree with the record their own identifier resolves to.
a wrong author list on a right DOI is invisible to every other check.
That run is real. Four of those entries carried an author list belonging to nobody on the
cited paper, one PMID resolved to an unrelated article in another field, and four author lists
stopped early with no and others marker. Every one of them resolved. A DOI checker, a link
checker and citations verify all pass them, because the DOI does point at the right paper —
it is the names beside it that belong to someone else.
Disagreements a registry causes rather than the bibliography are not reported: an online-first year against a print year, a deposited initial against a printed given name, PubMed's abbreviated end page, a BibTeX accent against the Unicode it encodes, and markup a publisher deposited inside a title. What survives is a disagreement about the work.
Fetched payloads are cached beside the file audited, so a re-run is offline and the report is reproducible from what was fetched rather than from the network.
Where the library lives
$CITATIONS_HOME if set
./.citations/ walking up this project's own, the way git finds .git
the shared library if you made one with citations init --user
none of those it tells you to run citations init
Project-local by default, so running the tool inside a paper works on that paper and there is no hidden global state.
What a claim file looks like
One file per source, in the paper's claims/ directory. citations verify --claims claims
reads all of them.
source:
citation: schiffman2026 # the bibkey
local: reference/schiffman2026.pdf # what gets read
sha256: 3f9a… # which bytes were read
extract_cmd: pdftotext
claims:
orthogonal-cores:
statement: 'Cores meeting equivalent causal criteria sit at principal angles of 75-90 degrees.'
quotes:
- exact: 'and principal angles ranged'
section: 'body'
statement is yours; exact is theirs. The tool checks the second only, so a statement that
overreaches its quote is for review to catch — the command cannot.
Records are YAML
So git diff shows what changed. A binary store cannot show you that a year moved from 2021 to
2022 — a real discrepancy this found between two of one author's own papers.
Claude Code
plugin/ is a Claude Code plugin that tells Claude when to reach for the CLI.
/plugin marketplace add elliottower/citations
/plugin install citations@citations
For all three reproducible-science tools in one plugin (citations + prereg + results):
/plugin marketplace add elliottower/reproducible-science
MIT licensed. docs/ has the working practices this came out of.
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 citations-0.2.0.tar.gz.
File metadata
- Download URL: citations-0.2.0.tar.gz
- Upload date:
- Size: 69.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
038c9261a2bb59542de67286ab06484e5a44aeb39d543b5c786cd8448fbe20ea
|
|
| MD5 |
176a2d043e858b8350e4bb5c4ca0b11b
|
|
| BLAKE2b-256 |
072e929e08e08b955df570200f710a84bbe0b50774edb40ac966669dfec0f4d2
|
Provenance
The following attestation bundles were made for citations-0.2.0.tar.gz:
Publisher:
publish-citations.yml on elliottower/reproducible-science
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
citations-0.2.0.tar.gz -
Subject digest:
038c9261a2bb59542de67286ab06484e5a44aeb39d543b5c786cd8448fbe20ea - Sigstore transparency entry: 2582482233
- Sigstore integration time:
-
Permalink:
elliottower/reproducible-science@e19b7e952529c76fe4f9d304c7dc93f806ad2a72 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/elliottower
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-citations.yml@e19b7e952529c76fe4f9d304c7dc93f806ad2a72 -
Trigger Event:
push
-
Statement type:
File details
Details for the file citations-0.2.0-py3-none-any.whl.
File metadata
- Download URL: citations-0.2.0-py3-none-any.whl
- Upload date:
- Size: 54.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb54470e59bcf6baa3fd7701f9d6d0bcdab74851fdb14e8849ab6a9278150e81
|
|
| MD5 |
169a368d0a01af5f69e397db2132dc17
|
|
| BLAKE2b-256 |
ee353af7ccbfb41e04cdb0cbfbf31e3531359fab666b3b23c02638fba2cb48f2
|
Provenance
The following attestation bundles were made for citations-0.2.0-py3-none-any.whl:
Publisher:
publish-citations.yml on elliottower/reproducible-science
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
citations-0.2.0-py3-none-any.whl -
Subject digest:
bb54470e59bcf6baa3fd7701f9d6d0bcdab74851fdb14e8849ab6a9278150e81 - Sigstore transparency entry: 2582482358
- Sigstore integration time:
-
Permalink:
elliottower/reproducible-science@e19b7e952529c76fe4f9d304c7dc93f806ad2a72 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/elliottower
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-citations.yml@e19b7e952529c76fe4f9d304c7dc93f806ad2a72 -
Trigger Event:
push
-
Statement type: