The consistency model#
If a consistency check failed on your own repository, diagnostics and the FAQ are the faster route; this page explains the machinery behind them.
A repository usually describes itself several times over: codemeta.json for
registries, CITATION.cff for citation, pyproject.toml for installation.
The files drift. A release bumps the version in one and not the others; an
author joins the project and is added to one list.
rs-metadata’s job is to notice that — and, just as importantly, to stay quiet when two files differ for a good reason.
codemeta.json is the anchor#
Every other metadata file is compared against codemeta.json, and never
against another companion file.
flowchart LR
CFF[CITATION.cff] --> N
PY[pyproject.toml] --> N
NPM[package.json] --> N
R[DESCRIPTION] --> N
N([normalized CodeMeta concepts])
CM[codemeta.json<br/><i>anchor</i>] --> N
Comparing every file against every other would leave findings with no obvious
owner: if CITATION.cff and package.json disagree, which one is wrong?
Anchoring on codemeta.json makes every finding actionable, since the
companion is normally the file to fix.
Four layers#
A crosswalk says which concepts correspond. It does not say when two values mean the same thing, and it does not say how strictly they must agree. Keeping those apart is what lets one comparison engine serve every format.
mapping which concepts correspond, and how strictly (declarative YAML)
↓
extraction reading one file format (adapter)
↓
normalization when two representations are equivalent (shared strategies)
↓
comparison producing diagnostics (one engine)
Adding a format means writing an adapter and a mapping file. No existing adapter changes, and the engine does not know the new format exists. See adapters.md.
Normalization#
Comparison is semantic. Each concept is compared with a named strategy, and the strategy is recorded on the finding so a reader can tell what counted as equivalent.
Strategy |
Treats as equal |
|---|---|
|
Case, surrounding whitespace, trailing punctuation |
|
|
|
|
|
|
|
|
|
|
|
Mixed identifiers: DOIs by DOI rules, everything else as a URI |
|
An ISO date with or without a time part |
|
|
|
Authors, matched by ORCID first, then by family name with initials accepted |
|
Requirements, matched by package name across notations |
Collections are compared as sets: order never matters.
Matching people#
The hardest case, and the one most likely to generate false alarms.
If both entries carry an ORCID, the ORCIDs decide — in both directions. Two different ORCIDs are two different people even if the names match exactly.
Otherwise, family names must match and given names must be compatible, where a single initial is accepted as an abbreviation (
J.matchesJosiah, butJohndoes not matchJane).Otherwise, full names are compared after normalization.
Matching is greedy and one-to-one, so two entries can never both claim the same counterpart.
Names given as a single string are split with Dutch and German particles kept
on the family side: Jan van der Berg is Jan + van der Berg, not Jan van
der + Berg. Family, Given is understood.
Comparison rules#
The mapping declares how strictly each concept must agree.
Rule |
Violated when |
Typical use |
|---|---|---|
|
The sides are not equivalent in either direction |
|
|
The source holds a value the anchor lacks |
Identifiers, authors from packaging files |
|
The sides share nothing at all |
|
|
Never — differences are reported without judgement |
Dependencies, Trove classifiers |
subset is asymmetric on purpose: codemeta.json is allowed to be richer than
any companion. What it may not do is contradict one.
Three outcomes#
Outcome |
Code |
Meaning |
|---|---|---|
match |
(nothing reported) |
The two files agree |
mismatch |
|
Both describe the concept, and they disagree |
not comparable |
|
A mapping exists but this value cannot be meaningfully compared |
Plus consistency.incomplete (informational): one file records something the
other does not. Not a contradiction, but occasionally it reveals metadata that
was never copied across.
A concept the source simply does not have is not reported at all. CITATION.cff
has no field for a programming language and never will; saying so on every run
would be noise.
What is deliberately not reported#
This list matters as much as the rules. Every entry is a case where a naive comparator would fire on correct metadata.
Package name versus display name.
MyToolin a citation andmy-toolon PyPI are one piece of software. Compared with separators removed.Summary versus abstract. PEP 621’s
descriptionis the one-line summary that corresponds to core metadata’sSummary; CodeMeta’sdescriptionis the full abstract. They are different fields, so they are marked not comparable rather than compared.Partial dependency lists.
codemeta.jsonis not obliged to enumerate every dependency. Only a package pinned to genuinely incompatible constraints in both files is reported, and then as a warning.Dynamic versions. A
pyproject.tomldeclaringdynamic = ["version"]does not contain a version, so there is nothing to compare.{file = "LICENSE"}. Names a file, not a license. Nothing identifies which license it is, so nothing is mapped.Partially overlapping keywords. Keyword lists legitimately differ in detail. Only a completely disjoint pair is reported, because that almost always means one file was copied from another project.
Prose divergence. A reworded abstract is a documentation lapse, not contradictory metadata — a warning, never an error.
Trove classifiers versus repostatus.
Development Status :: 5 - Production/Stableandactivedo not correspond one to one, so the difference is shown and not judged.Author lists from packaging files. Packaging metadata commonly names only the current maintainer.
subset, notequal— and compared againstauthorandmaintainertaken together, because ecosystems draw that line differently.
Per-format tables#
The exact concept-by-concept rules, with provenance, are generated from the mapping files: crosswalk.md.
Source validation comes first#
A companion file is validated against its own specification before any
comparison. CITATION.cff is checked against the official CFF 1.2.0 JSON
Schema, vendored into the package.
If that fails, comparison is skipped entirely. Comparing against a file that is not valid CFF would produce a page of findings about the wrong problem.
CI must give the same answer on a build machine, on a laptop on a train, and in five years’ time.