Adjudicating a difference¶
Sometimes the registry is wrong. You fetch the PDF and the volume Crossref deposited is not the one on the paper. Sometimes the difference is deliberate — a house style for publisher names, a container title your field writes differently. bibaudit can settle neither case: it has no authority to decide which side is right, and no way to know that you went and looked.
What it can do is let you record the decision in a file that travels with the
project. .bibaudit.toml holds the differences this bibliography has
adjudicated. Two properties keep that from becoming a way to hide problems: a
suppression must carry a reason, and a suppressed difference is moved into a
separate list rather than deleted, so the report can always state how much of
the bibliography is being taken on trust.
Where the file is found¶
The search starts at the first path given on the command line — the directory
itself, or the directory containing the file — and walks upward. The first
directory holding a .bibaudit.toml wins. The walk stops at a directory
containing .git, or at the filesystem root, so a suppression file in an
unrelated parent project cannot silence findings in this one. The .git check
happens after the config in the same directory, which is what lets the normal
case work: .bibaudit.toml beside references.bib at the repository root.
The form¶
[[ignore]]
key = "papantoniou2017colorectal"
field = "authors"
reason = "Crossref returns mojibake surnames; checked against the PDF 2026-07-30"
[[ignore]]
key = "*"
field = "publisher"
reason = "publisher names churn with imprint mergers; not tracked here"
Four keys are allowed inside an [[ignore]] table: key, field, kind and
reason. Anything else is refused by name, because a typo in this file removes
findings rather than adding them — write fields = "volume" and field falls
back to its default of "*", silencing every difference on that entry, with
nothing downstream to show that it had. Only the [[ignore]] array is read; a
table under some other name is not an error and silences nothing.
A reason is required¶
An unexplained suppression is a finding deleted by someone unknown. A rule
without a reason, or with one that is only whitespace, is refused — and one
bad entry voids the whole file rather than loading the rest, because a partially
loaded suppression file is a bibliography checked under rules nobody wrote down.
Every way this file can be wrong ends in a single sentence naming the file,
never a traceback: broken TOML, [ignore] written for [[ignore]], a scalar,
an entry that is not a table, a file that is not UTF-8. A fault inside one entry
names its position as well — .bibaudit.toml: [[ignore]] #2 has no 'reason';
the faults that make the file as a whole unreadable can only name the file. The
run exits 2 — the tool could not run — before anything is checked against a
registry. An empty file, or one with only comments, is valid and silences
nothing.
Put the evidence in the reason, and date it. It is the only thing a reader —
including you, two years on — has to judge the decision by.
key, field and kind¶
All three default to "*" and all three are shell-style globs:
| key | what it matches |
|---|---|
key |
the citekey. "epic*" matches epic2019diet; "*" applies the rule across the whole bibliography |
field |
the field the difference is in — title, authors, year, container, volume, issue, pages, publisher, kind, doi, pmid, isbn, identifier, status |
kind |
the sort of difference — mismatch, missing, drift, cosmetic, and the rest |
Both halves are printed in the report as field/kind on the line above the two
values, so the pair to write is the pair you can read off the finding — as it
reads before it is suppressed. A rule is matched against the original kind;
the suppressed: prefix in the listing below is stamped on afterwards, and is
never what a rule names.
kind is what narrows a rule to the difference you actually adjudicated:
field = "pages" alone also silences an entry that has no pages, and
"the registry's page range is wrong" is not the same claim as "this entry needs
no pages". Add kind = "mismatch" when that is what you meant.
Matching is case-sensitive on every platform. fnmatch folds case through
os.path.normcase, which is a no-op on POSIX and str.lower on Windows, so a
rule written for Smith2020 would have silenced smith2020 on one machine and
not the other — and two machines that disagree about whether a bibliography
passes is not a reproducible verdict. Rules are tried in file order, and the
first one that matches a difference is the one whose reason is attached to it.
What the report does with it¶
The silenced difference keeps both values and gains the reason. --show-suppressed
prints it under the entry, marked with ~:
$ bibaudit check references.bib --show-suppressed
bibaudit — 1 references checked
ADJUDICATED (1) a difference this project's .bibaudit.toml decided to accept
papantoniou2017colorectal references.bib:41
~ volume/suppressed:mismatch (Crossref has the wrong volume; checked against the PDF 2026-07-30)
~ stored 186
~ crossref 185
summary
ADJUDICATED 1
suppressed 1 (1 adjudicated here)
PASS — no reference in the failing set
bibaudit verifies that each reference exists and that its stored metadata
matches the publisher's record. It does not and cannot verify that a cited
work supports the statement it is attached to — that requires reading the paper.
Without that flag the entry is not printed — there is nothing to act on — but
the count stays, as
suppressed 1 (1 adjudicated here) (--show-suppressed to list). The
summary line always splits the total into registry defects and adjudications
made here, because those are different amounts of assurance and a single number
let the second hide inside the first. The JSON report carries the same split as
registry_artifacts and adjudicated, and every suppressed difference appears
in full under its entry's suppressed list.
ADJUDICATED, never REGISTRY-ARTIFACT¶
An entry silenced by .bibaudit.toml reports ADJUDICATED.
A difference explained by a documented registry defect
reports REGISTRY-ARTIFACT. These were one verdict, and merging them made two
incompatible claims indistinguishable: "the registry is known to be wrong here,
reproducibly, and here is the write-up" versus "somebody on this project wrote a
rule saying not to care". The first needs no reader. The second rests on a
person's say-so and can rot, so it ranks higher, prints first of the two, and is
coloured amber where the registry defect is green.
Neither verdict is in the default failing set. --fail-on can name
ADJUDICATED if you want a build to stop until the file is re-reviewed.
What a rule cannot silence¶
A retraction. The verdict is re-derived after suppression by the same rule that
produced it, and RETRACTED is returned before anything else is considered — so
even key = "*", field = "*" leaves a retracted citation failing. No
project-local decision makes a retracted paper safe to cite.
Findings on the same entry that the rule does not name also stand: silencing
volume leaves a title mismatch beside it a FIELD-MISMATCH, and adjudicating
one field never changes what the tool concludes about another.
A wildcard field is broader than it looks
A wildcard field matches every difference, and the identifier findings are
differences too — they are recorded against doi and identifier. A rule
with field = "*" on an entry whose DOI resolves in no registry moves that
finding out of the way, and the entry reports ADJUDICATED instead of
BAD-ID. Name the field you adjudicated.
A rule can go stale¶
Rename a citekey, re-export the bibliography from Zotero with a different key scheme, or wait for the registry to fix its record, and a rule stops matching anything. The report looks exactly the same as the day the difference was adjudicated — the difference is simply no longer being found.
bibaudit tracks which rules have silenced something and exposes the ones that
have not, but bibaudit check does not currently print them, and the report
does not name the .bibaudit.toml it loaded. So nothing in a run will tell you
that a rule went stale. Treat the file as something to re-read, keep the rules
narrow, and keep the date in every reason.
With --suggest¶
A suppressed difference has been moved out of the entry's findings before
--suggest ever sees it, so it is never proposed as a fix in the
suggested copy of a .bib. Adjudicating a missing field means --suggest
will stop offering to fill it.