| name | verify-citation |
| description | Verify that academic citations exist and match the metadata claimed for them, and optionally write verification results into a project summary table. Looks up DOIs against CrossRef, fetches canonical authors/title/year/journal and the abstract via OpenAlex, and reports any mismatch against a BibTeX entry or a free-form DOI. Use to gate every citation `literature-curator` adds, to audit an existing BibTeX file for fabricated or wrong entries, or to retrieve an abstract so the calling agent can judge whether the paper actually supports a given claim. |
Provide a deterministic, network-based existence + metadata check on academic citations so that no fabricated or mis-cited reference makes it into the manuscript. Returns canonical metadata and the abstract so the calling agent (typically `literature-curator`) can assess claim-fit semantically. Optionally writes the verification verdict into the project's literature summary table so the audit trail lives next to the bibliography.
<Use_When>
literature-curator is about to add a new entry to the project BibTeX file.
- A reviewer / supervisor asks "is this citation real?" or "does this paper actually say X?".
- Auditing an existing BibTeX file (e.g. after a co-author imports references from another project).
- Resolving a
[CITE: ...] placeholder — verify the candidate before committing.
- Refreshing the
verified_on / verify_status columns of the project summary table.
</Use_When>
<Do_Not_Use_When>
- Only the citation format needs fixing (use a BibTeX prettifier, not this).
- The user wants to find a citation from scratch with no candidate yet — that is
literature-curator's WebSearch task; this skill verifies once a candidate exists.
- Offline-only environment with no network — skill requires CrossRef/OpenAlex.
</Do_Not_Use_When>
This skill reads:
| Variable | Default | Purpose |
|---|
BIB_FILE | references.bib | Path to project BibTeX. Used when verifying by citekey. |
SUMMARY_FILE | references.csv | Project literature summary table. When --summary-csv is set (or this is configured in CLAUDE.md), verified_on / verify_status are written back per citekey. |
CITATION_VERIFY_EMAIL | (unset) | Polite-pool email for CrossRef/OpenAlex. Strongly recommended — gives a higher rate limit and prioritizes your requests. |
CITATION_VERIFY_TIMEOUT | 15 | Request timeout in seconds. |
Resolution order on each invocation:
- Command-line flags (highest priority).
- Environment variables.
- Project CLAUDE.md "Research stack" block — fields
BibTeX file, Summary file, CrossRef email.
- Defaults above.
If BIB_FILE is not configured on first use, ask the user once and offer to persist it.
Step 1 — Pick the input mode
Four modes:
python3 verify_citation.py --doi 10.1038/s41586-023-XXXXX-X
python3 verify_citation.py --bib references.bib --key smith2023connectome
python3 verify_citation.py --bib references.bib
python3 verify_citation.py --bib references.bib --summary-csv references.csv
Optional flags:
--claim "<one-line claim>" attaches the claim to the report so downstream logs show what was being supported. The skill does NOT decide whether the abstract supports it — that is the calling agent's call.
--json emits structured JSON instead of the human report.
Step 2 — Run the verifier
python3 "$CLAUDE_PLUGIN_ROOT"/skills/verify-citation/verify_citation.py \
--bib "$BIB_FILE" \
--summary-csv "$SUMMARY_FILE"
The script:
- Parses the requested entry(ies) from the BibTeX file.
- Hits CrossRef (
api.crossref.org/works/<DOI>) for canonical metadata.
- Falls back to a CrossRef title+author search if no DOI is present in the BibTeX.
- Hits OpenAlex (
api.openalex.org/works/doi:<DOI>) for the abstract.
- Compares author / title / year between BibTeX and CrossRef; flags mismatches.
- If
--summary-csv is set, writes verified_on (today, YYYY-MM-DD) and verify_status to the matching citekey row. Other columns (our_use, paper_says, bucket, cited_sections) are never overwritten.
Step 3 — Read the report
Human report (per entry):
[PASS|MISMATCH|NOT_FOUND] <citekey or DOI>
DOI: <doi> → CrossRef: <found / not found>
Title: crossref="..." match=<true/false>
Author: crossref="..." (+N more) match=<true/false>
Year: crossref=YYYY match=<true/false>
Abstract (first 500 chars):
"..."
Claim: <if --claim was provided>
→ Read the abstract above and confirm whether it supports the claim.
Note: <any flags>
Statuses:
PASS — DOI resolves AND title/authors/year all match (case-insensitive, normalized).
MISMATCH — found at CrossRef but at least one metadata field disagrees with the BibTeX entry.
NOT_FOUND — DOI does not resolve and title+author search returns no plausible match.
NOT_VERIFIED — network error, surfaced separately; not treated as a pass.
Step 4 — Caller assesses claim-fit
The script does NOT decide whether the abstract supports the claim — that requires reading. The calling agent reads the abstract from the report and answers the claim-fit question. The skill provides the evidence; the agent provides the judgment.
<Output_Contract>
stdout: human-readable report (one block per entry) — or JSON if --json was passed.
Exit codes:
0 — all entries PASS.
1 — at least one entry MISMATCH or NOT_FOUND.
2 — script error (network down on every retry, malformed BibTeX, missing file, etc.).
When --summary-csv is set:
- Columns updated:
verified_on, verify_status (per-citekey).
- Columns added if the citekey row does not exist: a new row with
citekey, verified_on, verify_status filled and human-curated columns left blank for literature-curator to fill.
- Columns the skill never touches:
our_use, paper_says, bucket, cited_sections, authors, year, title, venue, doi. These belong to the curator.
</Output_Contract>
<Failure_Modes>
| Symptom | Cause | Action |
|---|
urllib.error.URLError / timeout | Network unreachable, CrossRef down | Retry once; if still failing, report affected entries as NOT_VERIFIED (network) and do NOT mark them PASS |
Title matches but DOI does not resolve | BibTeX has a typo in DOI | Report as MISMATCH; the curator agent fixes the DOI |
Author count differs | BibTeX truncates with "et al." | Soft MISMATCH — flagged in notes but does not necessarily fail (curator decides) |
Abstract empty in OpenAlex | Publisher does not deposit abstracts | Skill still PASSes on metadata; caller must verify claim-fit via manuscript download |
Summary file does not exist | First run, no summary table yet | Skill creates the file with the canonical header row |
</Failure_Modes>
- `SKILL.md` — this file.
- `verify_citation.py` — the verifier. Stdlib-only (`urllib`, `json`, `csv`, `re`). Runnable as a script or importable.