Use when checking a .bib or reference list for incorrect or fabricated entries — wrong titles, invented co-authors, wrong DOI/volume/pages, preprints cited instead of the published venue, or references that do not exist. Triggers on "verify this bibliography", "check my references", "are these citations real", a refs.bib alongside a paper, or suspicion that a bibliography was LLM-generated.
Scanned 9/5/2026
Install to Claude Code
npx -y skills add chgagne/claude-skills-research --skill verifying-bibliography --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Verifying Bibliography?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/chgagne-verifying-bibliography)More formats (shields.io, HTML) on the badges page.
---
name: verifying-bibliography
description: Use when checking a .bib or reference list for incorrect or fabricated entries — wrong titles, invented co-authors, wrong DOI/volume/pages, preprints cited instead of the published venue, or references that do not exist. Triggers on "verify this bibliography", "check my references", "are these citations real", a refs.bib alongside a paper, or suspicion that a bibliography was LLM-generated.
---
# Verifying Bibliography
## Overview
**Resolve a stable identifier first, then compare the record back against the `.bib`.**
Everything else in this skill follows from that one inversion.
The obvious design — take the title in the `.bib`, search for it, fuzzy-match the authors —
cannot detect the single most important defect. A fabricated title that accurately describes
the right paper retrieves that paper and passes. Searching *by* the thing you are trying to
verify is circular.
Two measured examples, both cleared by a title-search tool and both caught by reverse lookup:
- `moritz2019draco` carried the title *"Draco: An Approach to Generating Visualizations with
a Computational Design Process"*. Its DOI is correct and resolves to *"Formalizing
Visualization Design Knowledge as Constraints: Actionable and Extensible Models in Draco"*.
- `wongsuphasawat2016voyager` carried DOI `10.1109/TVCG.2015.2467251`, which resolves to
*"High-Quality Ultra-Compact Grid Layout of Grouped Networks"* — a different paper by six
different authors.
## Run it
```sh
python3 ~/.claude/skills/verifying-bibliography/assets/run-bibcheck.py \
refs.bib --bbl main.bbl --out review-assets/
```
Run it by absolute path from the paper directory. Stdlib only — no install, no venv,
no dependencies. (`python3 -m bibcheck` also works, but only from `<skill>/assets/`.)
- `--bbl main.bbl` restricts checking to entries actually cited (the `.bbl` is ground truth
for "cited", because it reflects the built document rather than every `\cite` including
commented-out ones)
- `--out DIR` writes `bibcheck-report.md` and `bibdiff.csv`
- Exit code `2` means source coverage was degraded — see *When a source is down*
Optional: `S2_API_KEY` in the environment, or `~/.config/scholarly/s2_key`. Semantic Scholar
sits last in the ladder; on two real bibliographies it changed nothing. Absent, it is
silently skipped and nothing breaks.
## The ladder
```
1. DOI -> Crossref /works/{doi} authoritative, reverse check
2. arXiv DOI -> arXiv API 10.48550/* is DataCite, absent from Crossref
3. arXiv id -> arXiv API
4. ACL DOI -> aclanthology.org via Crossref
5. no identifier -> DBLP by title the CS/ML spine
6. still nothing -> OpenAlex, then Semantic Scholar
```
Once a **non-preprint** identifier resolves, the weak rungs are skipped — they cost four
requests to four free APIs for no new information. A **preprint** record is the exception:
an arXiv hit says nothing about whether the work was later published, so the search
continues (see *Prefer the venue of record*).
DBLP is queried before OpenAlex because it is near-complete for NeurIPS/ICLR/ICML/ACL
proceedings, which frequently carry no Crossref DOI at all.
## Report author findings as set differences, never as a score
A scalar author-match score is worse than useless — it looks like evidence while hiding the
finding. The Voyager entry above scored **0.83** while carrying two invented co-authors and
one dropped real one. This tool prints:
```
+Bach, Bill, +Dragicevic, Pierre -Bill Howe
```
Unmissable. Apply the same discipline in prose: name the authors, do not summarise the
disagreement.
## Severity
| Level | Meaning |
|---|---|
| `CRITICAL` | Wrong title under a resolved identifier, an author in the `.bib` absent from the record, or a DOI that does not resolve. Treat as fabrication until shown otherwise. |
| `MAJOR` | Metadata disagrees with a record reached by a stable identifier — venue, year, volume, issue, pages. |
| `MINOR` | Name form, punctuation, a field the venue does not use, or a preprint cited where a published version exists. |
| `WEAK` | Matched by title search only. **Not verified.** A human must look. |
| `UNVERIFIED` | No source returned a record. **This is a finding, not a pass.** |
| `SKIP` | Non-paper entry (blog, web page, anonymous artifact). Check the URL by hand. |
**`WEAK` and `UNVERIFIED` are not clean bills of health.** They are the entries most likely
to be fabricated, because a fabricated reference is exactly the one no database can find.
## What is compared against what
Identity must be established before any field comparison means anything.
- **Volume, issue, pages, year** are compared *only* against a record reached by a stable
identifier. A title-search hit may be a different edition, a reprint, or a review of the
same work. DBLP matched a *SIAM Review* review of Strogatz's textbook against a *Physics
Today* review of it; comparing their page numbers is meaningless.
- **Year** additionally requires a non-preprint record. Indexes routinely pair the published
venue with the preprint year — Semantic Scholar reports *DMLR 2022* for a paper published
in DMLR in 2024 — so a weak year cannot distinguish an error from the normal preprint gap.
- **A title mismatch against a title-search record stops all further comparison.** If the
titles disagree, we never identified the work, and reporting its year or authors would
describe a different paper.
## Prefer the venue of record
When a paper has both an official publication and a preprint, the entry must cite the
**published version**. The preprint may stay in the entry as a supplementary `url`/`eprint`;
it never replaces the venue.
The check fires only when the entry cites the preprint *instead of* a venue —
`journal = {arXiv preprint arXiv:1808.04819}`, or no venue at all. An entry that names its
venue **and** carries `eprint`/`archivePrefix` is the desired state and is never flagged.
The claim "a published version exists" is only made on an identifier-resolved record or a
DBLP record. OpenAlex title matches invent confident-looking venues — it offered *"Int. J.
Neural Syst."* for the InfoNCE preprint and *"Open MIND"* for an unrelated 2026 paper.
## When a source is down
Sources fail, and a silent failure changes the findings. Two consecutive runs once produced
8 findings and then 7, purely because DBLP dropped connections.
- Per-host throttles (arXiv 3.0s, DBLP 2.5s, Semantic Scholar 1.1s, others 1.0s), 3 retries
with exponential backoff, `Retry-After` honoured on 429
- **A circuit breaker**: after 3 consecutive failures a host is dropped for the rest of the
run. Retrying is right for one flaky request and catastrophic for a host that is down —
when arXiv began answering 429, 3 attempts × 15s on *every* entry turned a 12-second run
into a **6.5-hour** one. A 404 does not trip the breaker: it means the host is healthy and
the record is simply absent.
- Hosts that fail after retries are counted, reported on stderr, and set **exit code 2**;
hosts dropped by the breaker are named separately
- Responses are cached in `~/.cache/scholarly/`; definitive 404/410 are cached too, so an
unresolvable DOI is not re-requested every run. Transient failures are never cached.
**If you see the degraded-coverage warning, re-run before trusting the report.** A warm
cache makes consecutive runs byte-identical.
## Limits
State these rather than implying completeness.
- **Bib-style defects are invisible.** A non-normalised `booktitle` ("The Fourteenth
International Conference on Learning Representations") is a real defect no record
comparison can see. It surfaces as `WEAK` at best.
- **Given-name checks compare first initials only.** `Hoggan, Eve` → `Hoggan, Eve E.` is
missed. Full given-name comparison was tried and reintroduced false alarms on the far more
common initials-only form (`Dupré, B.` for `Benoit Dupré`), which is a worse trade.
- **`SKIP` entries are unchecked**, not clean. Anonymous artifacts and blog posts need a
human to open the URL.
- The tool verifies that a reference *exists and is described correctly*. It says nothing
about whether the citation supports the claim attached to it — that is selective citation,
and it is handled in `reviewing-paper-sources`.
## Measured results
Against two bibliographies whose ground truth was established by prior manual review:
| | Entries | TP | FP | FN | Precision | Recall |
|---|---|---|---|---|---|---|
| VIS paper | 18 | 7 | **0** | 2 | 1.00 | 0.78 |
| ML paper | 57 | 3 | **0** | 2 | 1.00 | 0.60 |
| HaRC 0.2.0, same files | 75 | 4 | 9 | 9 | 0.31 | 0.31 |
Acceptance benchmark (`tests/test_acceptance.py`, 6 must-fire and 10 must-stay-silent keys):
**precision 1.00, recall 0.83**.
It also found a defect the manual pass had missed: `caetano2023trees`, an *uncited* entry
whose DOI resolves correctly but whose pages read 410–418 against the record's 411–419.
Uncited entries are unaudited — check the whole file.
HaRC missed both fabricated titles described above, and false-alarmed on correct ACL entries,
LaTeX-escaped names (`Dupr\'e`), DBLP disambiguation suffixes (`Elena Rossi 0001`),
preprint-vs-published years, and textbook editions. Every normalisation rule in
`normalize.py` exists because one of those false alarms was observed on real data. See
`reference/fabrication-signatures.md`.
Timing: a cold 57-entry run takes minutes; warm runs take **10–20 seconds**, and consecutive
warm runs are byte-identical.
## See also
- `reference/fabrication-signatures.md` — the LLM-generated bibliography signature, the
BibTeX comment traps, and the `.bbl` cross-check
- `reviewing-paper-sources` — consumes this report, writes the audit table and
`refs-corrected.bib`, and checks selective citation
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!