Bib-manage
SkillFiles & storageALWAYS use when a manuscript's BIBLIOGRAPHY is the thing being fixed, or when a cite's PDF turns out to be wrong, 'link my bib entries to the PDFs', 'which sources am I missing a PDF for', 'audit my sources.bib', 'my bib entry has no file field', 'fetch the Federal Register PDFs', 'why can't pincite find this source'. WRONG FILE: 'is this PDF the published version or the preprint', 'this PDF is a book review, not the book', 'the file field points at the wrong paper', 'the PDF doesn't match the citation'. METADATA: 'reconcile my bib against Paperpile', 'these entries were extracted by a model, check them', 'fix the metadata in sources.bib', 'my bib has no DOIs and the fields look wrong', 'pull the real author/journal/volume from my library', 'where did this bib entry's metadata come from'. PAGE RANGES: 'does my bib have page ranges', 'my bib only has start pages', 'backfill the page ranges', 'pull the pages from the DOI', 'add DOIs to my bibliography'. Use proactively before any pincite run, pincite resolves footnotes through `file = {...}` and silently skips entries that lack one. NEGATIVE ROUTING: the page number inside a footnote is pincite, but whether that footnote's PDF is the version of record or the wrong document is THIS skill, mid-pincite-run included; whether a cited source says what the footnote claims is source-verify; Bluebook FORM is bluebook or bluebook-audit; finding or downloading a paper is fetch-paper or paperpile.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Bib-manage skill
What this skill tells your AI
The instructions your AI receives, as published by edwinhu/workflows in skills/bib-manage/SKILL.md and read by ahel’s review.
What this skill carries — grep references/ for any subject the names below miss:
!d=${CLAUDE_SKILL_DIR}; command -v skill-toc >/dev/null 2>&1 && exec skill-toc "$d"; s=$HOME/.claude/skills/plugin-utils/bin/skill-toc; [ -x "$s" ] && exec "$s" "$d"; echo "(skill-toc unavailable: references and scripts are NOT listed here — install the plugin-utils plugin, or start a new session so its bin/ reaches PATH)"
Owns the bibliography as an index: every entry pointing at the right PDF, and that PDF being the version of record.
pincite CONSUMES this index — it resolves footnote → source through
file = {...} and through nothing else. So the two failures this skill exists to
prevent are both silent in pincite: an entry with no file is a pincite that is
never attempted, and an entry pointing at a preprint is a pincite that is
confidently wrong.
audit entries with/without file, files missing on disk, entries without a
DOI, PDFs no entry claims <- run this first
link citekey -> PDF, written back as file = {...}
version is each entry's PDF the published version, or a preprint/proof?
doi backfill missing DOIs from Crossref, verified against the bib's pages,
and the PAGE RANGE that same verified record already carries
reconcile
rewrite the entry's OWN FIELDS from a better source:
Paperpile > Crossref > the extracted value. Never `file`, never a key
fedreg fetch the Federal Register PDFs the entries cite
CLI
B="${CLAUDE_SKILL_DIR}/scripts/bibman.py"
python3 "$B" audit --root . # start here
python3 "$B" link --root . --dry-run # inspect the mapping first
python3 "$B" link --root . --backup # then write
python3 "$B" version --root .
python3 "$B" doi --root . --dry-run --only aggarwal2015
python3 "$B" reconcile --root . --dry-run # read the full set FIRST
python3 "$B" reconcile --root . --backup --fields doi,pages
python3 "$B" fedreg --root . --dry-run
Every path is a flag — --bib, --pdf-dir, --fedreg-dir — defaulting to the
layout pincite uses, so the tool works on any manuscript. --only scopes
version and doi to named citekeys; --dry-run reports without writing;
--backup leaves a .bak; --strict makes version exit 1 on any entry that
is not the version of record.
--fields scopes reconcile to a subset of author,title,journal,volume,pages, year,doi,publisher; --pp-index points at the Paperpile CLI's index cache.
audit, link and version never touch the network. doi and fedreg do.
reconcile reads Paperpile locally and reaches Crossref only for what Paperpile
does not cover.
Facts
-
The link threshold depends on the YEAR, and that is the whole of its accuracy. Two matchers run in order: PDF stem == citekey (48 of 79 on the test bibliography), then Paperpile-style
Author Year - Title.pdfon first-author surname plus title similarity (the other 31). Same year, the year corroborates and 0.55 is enough. DIFFERENT year — a working paper against its published version,Lund 2017→lund2018— demand ≥ 0.95, because only the title separates two papers by one author. At 0.55 with the year ignored,alexander2010("Interim News and the Role of Proxy Voting Advice") claims the PDF "The role of advisory services in proxy voting" at 0.52. That is a wrongfilein the bib, which is the one input pincite trusts absolutely — a mis-link there sends a footnote to another paper and can verify CONFIRMED while doing it. -
Never force a match; REPORT what stays unmapped. On the test bibliography four PDFs stay unclaimed and every one is correct:
Alexander et al. 2009(0.52),Cremers and Romano 2011whose filename is truncated mid-title by the filesystem (...proxy voting on co ... ion plans..., best match 0.89 against a 2007 entry), and two whose filenames are notAuthor Year - Titleat all. An unmapped PDF costs a minute of human attention. A forced one costs a wrong citation nobody looks at again. -
Paperpile writes SURNAMES ONLY, so the first-author surname is the FIRST token.
Barzuza et al.,Bebchuk and Hirst,Rock. Taking the last token — the correct habit for a bibtexauthorfield — reads "Barzuza et al." as an author named "al." and unmapped 14 of 83 PDFs here before it was caught. -
A 6-digit
pagesis an Elsevier ARTICLE NUMBER, not a page.J. Fin. Econ. 154 (2024) 103810is paginated 1–N in its own PDF and IS the version of record. Range-testing it reports every such entry as a preprint —shu2024andcabezon2025are both genuinely published and both false alarms without this exception. -
A publisher's own PDF can be an ADVANCE-ACCESS PROOF. OUP served
iliev2021as pages 1–49 instead of 5581+,v 00 n 0, internal nameRFS-OP-….tex— authenticated, current, and useless for a pincite. Flag when the range test fails AND page 1 carries no nonzero volume designation. A successful fetch never means the pagination problem is solved. -
Do NOT identify the version of record by matching the journal name against page 1. Bib journal names are Bluebook abbreviations ("Rev. Fin. Stud.") that never appear in a masthead ("The Review of Financial Studies"). That test flagged 46 of 61 entries and is worthless. The derived printed-page range against the bib's start page is the test that works: 5 of 79 flagged, all five real preprints.
-
The page derivation is pincite's, imported not copied.
pages_of,page_numbersandpage_offsetare pure functions and pincite does nothing at import time. Copying them lets the two skills' notion of "what page is this" drift, and aversionverdict that disagreed with pincite's own would be worse than no verdict. -
A bibliographic title query returns the SSRN PREPRINT DOI. Measured on
aggarwal2015: title alone returns10.2139/ssrn.2120797,10.2139/ssrn.1688993,10.2139/ssrn.2023480— the top three hits are all SSRN, none carries a page. Query with title + journal + volume + pages, then VERIFY the returned record's start page equals the bib'spagesbefore accepting: that yields10.1111/jofi.12284, page 2309–2346 against the bib's 2309. A DOI that fails the page check is reported, never written — it is usually the preprint of the very work the entry means. -
The verified Crossref record already carries the END page;
doiwrites it. The match test was alwaysrecord start page == bib start page, so the range that passes it is the same work's own span —10.1111/jofi.12284returns2309-2346against a bib holding2309. Written aspages = {2309--2346}. Four guards, and each blocks a different way of destroying a value that was already right:@articleonly (a@miscpress release'spages = {1}is not an article span); a bare start page only, so a Bluebook pincite someone recorded there survives — on OPVeasterbrook1991(66-67) andjackson2019(849, 851--52) are the only range-like values in 197 entries and BOTH are pincites, not spans; never anARTICLE_NUMBER; and the Crossref range must START at the bib's page, re-checked at write time so the DOI test and the range test can never disagree. Measured on OPV: 55 of the 64 bare start pages were eligible, 30 resolved, and the 25 that did not are law reviews, which Crossref indexes thinly. -
An end page turns
versionfrom one test into three. With only a start page the derived span can be asked one question,lo <= start <= hi. With the end page it answers whether the span's BOTH endpoints fall inside, and whether the PDF's page count can hold the printed span at all —n < spanis impossible for a version of record, andn > span + 2(beyond a cover sheet) is a different artifact.aggarwal2015is a 57-page PDF claiming a 38-page span,ertimur201864 pages for 21,hayne201959 for 43; each now fails on three counts instead of one, with no header parsing. Entries with no end page keep the start-only path unchanged — on OPV that is still most of them, and all 77 verdicts were byte-identical before and after the backfill. -
reconcile's precedence is Paperpile > Crossref > the extracted value, and it is implemented as source ORDER, not a merge. An entry confidently matched in Paperpile is never queried against Crossref, so the stronger source cannot be contradicted by the weaker one. On OPV — a bib whose header readsAuto-extracted from sources.md via Gemini Vertex Batch, where every field was written by a model reading a markdown list and no entry carried a DOI — Paperpile covered 33 of 197, Crossref 24, and 140 had neither. Every change prints ascitekey.field: old -> new [source];--dry-runwrites nothing and is byte-identical run to run. -
A CONFIDENT Paperpile match needs the surname, the year AND the title, and an entry with no author needs the year EXACTLY. Surname equal, |Δyear| ≤ 1, title ≥ 0.90 same-year / ≥ 0.95 across a year — the same asymmetry
linkuses. With NO author there is nothing to corroborate with, and 1.00-vs-0.97 is not enough:secproxyadvice2019(the SEC's own 2019 release, 84 Fed. Reg. 66,518) matched a 2020 COMMENT LETTER about it at 0.98, whose title is the release's title withRE:in front. Requiring Δyear == 0 there kills it; a document with no author has a fixed date and no working-paper drift to forgive. Two candidates within 0.02 of each other are AMBIGUOUS — reported, never picked, because a wrong library match rewrites author, title, journal and DOI at once. -
Crossref's start-page verification is
@articleONLY, because a pincite is shaped exactly like a page range. Nothing in the VALUE66-67says whether it is pages 66 to 67 of an article or a Bluebook pincite into a book. Readingeasterbrook1991's 66 as a start page made Crossref accept10.1111/j.1468-0319.1991.tb00155.x, titled "Discussion Papers", because it happens to begin on page 66 — a proposal to overwrite the title of The Economic Structure of Corporate Law. The entry TYPE is what separates them, andrange_blockedalready said so. This guard lives incrossref_pick, sodoigets it too. -
A Paperpile record can be the SSRN PREPRINT of a published article, and taking its fields REGRESSES the citation.
choi2009(S. Cal. L. Rev. 82:649) andbebchuk2019a(B.U. L. Rev. 99:721) are both published; both library records carryjournal = SSRN Electron. J.and a10.2139/ssrn.*DOI. Precedence orders sources by reliability — it does not authorise replacing a real journal name with a preprint server's placeholder. When the source record is SSRN-shaped and the entry ALREADY names a journal,journal,volume,pages,year,publisheranddoiare all refused and reported. A genuine working paper has no journal to protect, so it is unaffected. -
Precedence does not authorise a THINNER rendering of a name. Paperpile answered
J Fisch and A Hamdani and S D Solomonforfisch2019'sJill E. Fisch and Assaf Hamdani and Steven Davidoff Solomon, and Crossref answeredVICENTE CUÑAT and MIREIA GINE and MARIA GUADALUPEforVicente Cuñat and Mireia Giné and Maria Guadalupe— initials for given names, and full caps with the accents gone. Both are refused and reported. Note thatstr.isupper()on the whole author string is ALWAYS False: theandseparators are lowercase, so the test must run on the names alone. -
reconcilenever touchesfileand never touches a citekey.fileis not inRECONCILE_FIELDSand is refused by name infield_blocked— the PDF mapping belongs tolink, which owns the matcher and its thresholds. The keys are load-bearing for something outside this repo:paper/typst/opv-body.typreferences entries through#ref(<key>)and that project'sscripts/resolve_refs.pyresolves supra numbers through them, so a renamed key silently breaks the manuscript. After writing, the file is re-parsed and the key LIST compared to what it was; a mismatch exits non-zero pointing at the.bak. -
federalregister.gov bot-blocks every request and lands on unblock.federalregister.gov. Fetch from govinfo's link service,
https://www.govinfo.gov/link/fr/<vol>/<page>?link-type=pdf, into--fedreg-dirasfedreg-<vol>-<page>.pdf. Pre-2000 volumes return the WHOLE ISSUE, 200+ MB — trim withqpdf --empty --pages IN a-b -- OUT. The SEC's own release PDF of the same document has different pagination, so it is the wrong source for a Fed. Reg. pincite.
Red flags — STOP
| About to | Why wrong | Do instead |
|---|---|---|
| Run pincite on a bib you have not audited | Entries with no file are skipped silently; here that was 118 of 197 | audit first |
| Lower the 0.95 cross-year threshold to map "just one more" PDF | At 0.55 alexander2010 claims the wrong paper at 0.52 | Leave it unmapped and link it by hand |
Hand-add a file to make an unmapped PDF go away | The unmapped list is the output, not a failure | Read the reason it prints first |
Report a 6-digit pages entry as a preprint | It is an Elsevier article number; the PDF is the version of record | Trust the article-number skip |
| Check the version of record by finding the journal name on page 1 | Bluebook abbreviations never match mastheads — 46 of 61 flagged | Range-test the derived pages against the bib's start page |
| Accept a Crossref DOI because the title matched | Title-only returns the SSRN preprint; 6 of 7 test entries | Require the start page to equal the bib's pages |
Write a page range over a pages that already holds one | It is a Bluebook pincite, not a span — both such values on OPV were | Leave it; doi reports it as SKIPPED |
Give a @misc or @book entry an article span | pages = {1} on a press release is not page 1 of an article | The @article guard; read the SKIPPED list |
| Trust a freshly fetched publisher PDF | It can be an advance-access proof paginated 1–N | Re-run version on anything fetched |
Use the SEC release PDF for a Fed. Reg. cite | Different pagination from the one the cite uses | fedreg via govinfo link/fr/<vol>/<page> |
Run link without --dry-run on a bib you have not inspected | It writes into 197 entries | --dry-run first, then --backup |
Run reconcile without reading the full --dry-run set | It rewrote 163 fields across 47 entries on OPV | Read every line, then --backup; --fields to scope |
Take a Paperpile record's journal when the entry already names one and the record is SSRN | SSRN Electron. J. over B.U. L. Rev. is a regression, not a correction | The SSRN guard; read the CONFLICTS list |
| Accept a Paperpile match on a no-author entry a year apart | The SEC's 2019 release matched a 2020 comment letter at 0.98 | Δyear == 0 when there is no surname to corroborate |
Let Crossref verify a non-@article by its start page | 66-67 is a pincite, and "Discussion Papers" also starts on page 66 | @article only — the type is what separates them |
Add file to RECONCILE_FIELDS "since it is a field too" | The PDF mapping is link's, matcher and thresholds included | Leave it; field_blocked refuses it by name |
Signals
- GitHub stars
- 21
- Forks
- 4
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
bib-manage- Source
- github.com/edwinhu/workflows