This is the public record of changes to Scholar Sidekick’s deterministic pipelines. Two version tags let a client pin behaviour over time and detect drift:
x-scholar-transform-version - the normalisation, formatting, and resolver chain. Mirrored in /.well-known/sources.json.x-scholar-verify-version - the citation-verifier pipeline (claimed-vs-resolved comparison, thresholds, similarity primitives). Emitted on /api/verify routes only.A version tag is bumped only when a change could alter byte-identical output (transform) or a verdict (verify) for the same input. Each entry below is tagged:
For agents: record x-scholar-transform-version (and x-scholar-verify-version for /api/verify calls) alongside any cached output. If either value changes, re-run cached comparisons or re-baseline your expected outputs.
transform_version 2026-08-18The builtin AMA formatter shared its author rule with Vancouver: list the first six authors, then et al. The AMA Manual of Style(11th edition) prescribes a different cutoff — list all authors when there are six or fewer, and when there are seven or more, list the first three followed by et al.
AMA output now follows the manual: a seven-author paper renders as Polack FP, Thomas SJ, Kitchin N, et al. instead of listing six names. Papers with six or fewer authors now list every author. Vancouver output is unchanged — it keeps the six-then-et-al. convention. Affects the author slot of AMA output on every formatting surface (/api/format, streaming, export, format-items). transform_version is bumped; re-baseline pinned AMA output.
transform_version 2026-08-11The Vancouver and AMA formatters title-cased the venue, which lowercased every letter after the first and quietly broke journal names that capitalise on purpose: eLife rendered as “Elife”, mBio as “Mbio”, PLoS ONE as “Plos ONE”. This had been wrong for as long as the helper has existed; yesterday’s preprint change made it obvious by rendering medRxiv as “Medrxiv”.
A token carrying an uppercase letter after its first character is now left exactly as the source wrote it — a rule rather than a name list, so it also covers bioRxiv, ChemRxiv and publisher names like McGraw Hill. Venues with no internal capitals are still title-cased, so nature physics continues to render as “Nature Physics”. One known limit, documented rather than special-cased: an all-lowercase name such as npj carries no signal to detect and is still title-cased.
Affects the venue slot in Vancouver and AMA output only — CSL styles were never routed through this helper. Verdicts are unaffected, because container comparison lowercases before matching. transform_version is bumped; re-baseline pinned output for any of the affected venues.
transform_version 2026-08-10A preprint from medRxiv, bioRxiv, ChemRxiv, Research Square, SSRN or Preprints.org resolved with Crossref’s raw posted-content type and an empty container, so it formatted exactly like a peer-reviewed journal article and the hosting repository was dropped entirely. For a tool whose claim is citation trustworthiness, presenting an unrefereed preprint as though it were peer-reviewed is a gap worth closing.
Such records now carry type: "preprint" and the hosting repository as the container. Concretely: RIS emits TY - UNPB instead of JOUR, BibTeX emits @misc instead of @article, and the repository appears in the venue slot of every formatter. CSL-JSON output is unchanged — preprint is an internal type and is collapsed to article-journal before citeproc sees it.
The repository is a fallback: a preprint later published in a journal keeps the journal, and a server that deposits no repository name keeps an empty container rather than a guess. Formatted output and export files change for preprints, so transform_version is bumped — re-baseline any pinned preprint output. verify_version is unchanged, and verdicts are not affected: a resolved container is a soft signal that cannot, on its own, turn a correct citation into a mismatch.
The per-plan burst multiplier was being applied twice. The base allowance is scaled by the plan multiplier, and the environment-override step then scaled the already-scaled value again, so the effective factor was the multiplier squared wherever no explicit limit was configured for that plan and route.
In practice that was the /api/audit bucket on every tier above the free one (plus streaming on BASIC). Anonymous and free callers were unaffected — their multiplier is 1, so squaring it changed nothing, which is why this went unnoticed.
| Plan | Was | Now |
|---|---|---|
| anonymous / free | 4 | 4 (unchanged) |
| free key (ssk_) | 100 | 20 |
| BASIC | 25 | 10 |
| PRO | 100 | 20 |
| ULTRA | 400 | 40 |
| MEGA | 1600 | 80 |
/api/audit allowances, per 30s. This bucket is deliberately the tightest one we run: a single call fans out to as many as 25 upstream verifications, so the pre-fix MEGA allowance permitted 40,000 upstream lookups per 30 seconds from one key. The corrected figures are the ones our published documentation already described — a free ssk_ key is now exactly the “~5×” of anonymous that /docs and /help claim, where it had been 25×.
Tagged non-breaking: this enforces the allowances that were always intended and already documented, rather than changing a contract. If you were pacing against the higher ceiling you may now see 429 RL_BLOCKED — pace from the RateLimit-Limit and RateLimit-Reset response headers rather than a hard-coded rate; the Python client already does this for you in audit_bibliography(). Citation output is untouched, so neither transform_version nor verify_version changes.
/api/health now reports the rate limits it actually enforcesThe limits object on GET /api/health was built from a set of RL_* environment variables that were disconnected from the rate limiter. Those are the legacy plan-agnostic pins, which the limiter ranks below plan-scoped ones - so the endpoint could report one number while enforcing another. In production it advertised a /api/format ceiling of 40/min while actually enforcing 10/min.
It now derives every value from the same policy pipeline the middleware enforces, so the two cannot drift again. The shape changed from flat integers to a per-family object carrying its own window, because families genuinely have different windows - e.g. { "format": { "limit": 10, "window_s": 60 } }. Keys are now the real route families: format, format_stream, export, format_items, audit, fallback. This renames stream and default, and adds audit, which was missing entirely despite being the tightest bucket. The reported figures are the anonymous tier; an API key raises them.
Treated as non-breaking: a diagnostic field with no known parser in any first-party client. No transform_version or verify_version bump - citation output is untouched. If you read this field, the live RateLimit-Limit response header remains the authoritative per-request value.
The verifier now cross-checks every claimed author against the resolved record, not just the first. A citation that previously returned matched can now return ambiguous (review) when a claimed co-author does not appear on the resolved record, with per-author detail as coauthor rows in mismatches[]. The check can only downgrade to review - it never produces a mismatch verdict on its own.
It is deliberately conservative: it flags substituted co-authors, never merely extra ones, and it skips entirely whenever the comparison is unsafe - the resolved record lists fewer authors than the claim, any group/corporate author is present, or the first author already failed. Measured against 299 real multi-author citations, it produced zero false accusations. Only requests that supply more than the first claimed author are affected.
verify_version is unchanged (2026-08-02): the comparison pipeline itself is not modified - this switches on a dormant, flag-gated check. If you baseline multi-author /api/verify responses, re-run them. If a citation you believe is correct now returns ambiguous with coauthor rows, tell us - reports directly tune this check.
Our 1,402-citation blind holdout found the ISBN path carrying 9 of 14 false accusations - 21.4% of that arm, against 0.33% for DOIs. Every one was the same book, flagged over cataloguing noise, because books are cited by work and catalogued by edition. Title comparison now additionally tries three normalised forms: with a trailing edition/format statement removed (“…, Based on the Oxford Edition, Second Edition”), with a leading article in any of the catalogued languages removed (La Monadologie vs Monadologie), and with whitespace collapsed, which repairs catalogue records that have stripped an accent into a space (Mediterráneo stored as Mediterra neo). Seven of the nine now match.
Each form is applied additively - the best score across all forms wins - so a comparison can only ever score higher, never lower. Re-checked against every fabrication in the sealed holdout: none became a match. Volume numbers are deliberately not stripped, because the numeral is the part that distinguishes Capital, Volume I from Volume III.
verify_version 2026-08-02 — a citation previously returned as mismatch or ambiguous on one of these book forms may now return matched. Re-run cached book comparisons if you baseline against them.
The entry below closes by noting that “de Jong” and “de Jonge” are still accepted as the same author, and that it needed separate work. That work is done. It is not one edge case — it is the largest open gap on the author axis, and no amount of better string matching will close it. Nothing changed in how citations are verified; what changed is that the limit is now documented with the numbers behind it.
The gap. A single wrong letter in a surname becomes invisible from five characters up. The similarity score for a name differing in its last letter is 0.88 at four characters, 0.92 at five, 0.93 at six, and keeps climbing — while the match threshold sits at 0.92. Past that length, one wrong letter simply cannot push a pair below the bar. Across 49 pairs of real, distinct surnames that differ by one letter, none of the nine short ones were wrongly matched and all nine of the eight-to-nine-character ones were.
Who it affects, and who it does not. Almost entirely long Scandinavian, Germanic and Slavic surnames where an -sen/-son ending separates two common real names: Hansen/Hanson, Jensen/Jenson, Peterson/Petersen, Christensen/Christenson. Short Chinese, Korean and Vietnamese surnames are not affected — Li/Lu, Kim/Kam, Chen/Chan and Wang/Wong are all correctly flagged. That is the opposite direction to the Unicode-hyphen bug fixed on this same date, which fell hardest on exactly those short names.
Why we stopped looking for a better algorithm. A genuine typo and a different person sit the same distance apart — “Hernadez” for “Hernandez” is one edit, and so is Hansen versus Hanson. We tested twelve string and phonetic metrics against 25 different-person pairs and 17 typo pairs, and none separated the two groups. The phonetic algorithms usually recommended for name matching are worse than useless here: their purpose is to cluster spelling variants, so they merge 19 to 24 of those 25 different-person pairs into a single key. We also built a second-stage typo-detector and rejected it — it would have started accusing real citations whose names lost their accents in transit.
What this means for you. It is a recall limit, not a false-accusation risk: it makes the verifier miss some wrong authors, and cannot cause it to wrongly flag your correct citation. If a first author’s surname is long and could plausibly be a near neighbour of another real name, read a first-author match as weak evidence and check the resolved record. The full measurements, including the rejected fix, are in known-failures.md.
verify_version 2026-07-31“van der Berg” and “van der Meer” are different people, but the verifier accepted them as a match. The similarity measure gives extra weight to a shared opening — sensible for catching typos, wrong when the shared opening is a particle that hundreds of unrelated surnames carry. Here “van der ” was most of the string, so the real surnames barely counted.
When both names begin with the same particle, that opening is now discounted and the surnames proper are compared. The pair above drops from 0.93 to 0.67 and is correctly flagged, as are “van Dijk”/“van Dijkstra” and “de la Cruz”/“de la Torre”. Names that genuinely match are unaffected — the check can only lower a score, never raise one.
Worth noting the direction. The other author-name fixes on this date all make matching more forgiving, to stop correct citations being wrongly flagged. This one does the opposite: it closes a case where a wrong author slipped through. Still open: “de Jong” and “de Jonge” differ by one letter and are still accepted. That is not the particle effect — it is the one-letter gap measured in the entry above, which we have since established cannot be closed by string comparison at all.
verify_version 2026-07-31Two long-standing causes of a confident wrong verdict on a correct citation, both closed against a purpose-built set of 299 real citations whose first author has a compound surname.
Relocated particles. A record storing “Showk, Sheer El” for an author cited as “El Showk” moves the particle past the given name, so the name parts arrive in a different order. Every existing rescue compared them in order, so none matched, and the citation was reported as a mismatch. Particles are now set aside on both sides before the surname is compared.
The obvious fix — ignore word order entirely and just check the same words are present — was built, measured, and rejected: it also accepts “Smith Jones” against “Jones Smith”, which are two different names. Order carries real information, so only the particle is allowed to move.
Generational suffixes. A record storing “Kennicutt, Jr.” against a citation reading “Robert C. Kennicutt” also returned a mismatch. A suffix does not identify a different person, so it is now dropped before the comparison.
Both were checked against a sealed hold-out set before shipping: no correct citation changed verdict, and nothing was newly sent for review. One thing this does not mean: the measured false-accusation rate on that set was already zero before these fixes, so the honest claim is that two reproducible failures were removed — not that the rate improved. A rate of zero on 299 citations means “under 1.3%”, never “none”.
verify_version 2026-07-31Registries frequently store a Unicode hyphen (U+2010) or an en-dash where the citing author typed an ordinary ASCII “-”. Title comparison already collapsed those to a plain hyphen; author-surname comparison did not. The difference therefore survived normalisation, leaving only the fuzzy string match to absorb it — and that match is length-sensitive.
So the outcome depended on how long the surname was. “Duarte-Cabral” scored 0.969 and matched; “Wu-Li”, “Ng-Yu”, “Lo-Fu” and “Xu-Ma” scored 0.893, under the 0.92 threshold, and returned mismatch/high— the confident “this citation looks fabricated” verdict, on a correct citation that differed only in which dash character a database happened to store. Because it only broke short names, it fell disproportionately on Chinese, Korean, and Vietnamese surnames.
Surname normalisation now unifies dash variants, exactly as title and journal-name normalisation already did. Found on a real Crossref record (Velez-Vega) while probing arXiv LaTeX bibliographies for author-name variation. Both the short-surname cases and a guard that genuinely different hyphenated surnames still get flagged are now pinned by tests.
verify_version 2026-07-31A citation printing a double-barrelled surname with a space (“Hatfield Dodds”) against a record storing it hyphenated (“Hatfield-Dodds, Zac”) returned mismatch/high— a confident false accusation on a correct citation. It scored 0.912 against a 0.92 threshold, missing by 0.008, and neither existing surname rescue engaged: an unsplit “hatfield-dodds” is a single token, so the leading-run rescue (which needs the claimed name to have more tokens) and the token-run rescue (which needs the counts to differ) both declined.
Author-name tokenisation now splits on hyphens as well as “.” and “,”, so hyphenated surnames behave like space-separated ones — which the rescues already tolerated. The asymmetry was arbitrary: “Smith Jones” matched a resolved “Smith” before this change and “Smith-Jones” did not.
Found while measuring the co-author check below. Frequency, measured after the fix shipped: of 1,395 blind-holdout entries, 87 are correct citations of works whose first author has a compound surname. Zero of them false-accused on the pre-fix code, and none differed from the stored form only by a hyphen or space. So this was rare in citation text harvested from registries. It does occur in the external benchmark’s reference strings, which carry encoding damage of unknown origin — so the population it affects is pasted bibliography text rather than registry-harvested fields.
Still open: a relocated nobiliary particle (“El Showk” stored as “Showk, Sheer El”) differs in token order and no ordered-run rescue handles it. It scores 0.550 and still false-accuses. The candidate remedy — set-overlap matching — is materially more permissive, so it is being measured rather than assumed.
verify_version 2026-07-31An external benchmark — Badalova & Mayr's 104-reference labelled set (doi:10.5281/zenodo.21457492), the first independent test set for this category — put numbers on two limitations we had documented but not measured.
Co-authors were never checked. The comparator compared the claimed first author against all resolved authors; the rest of the claimed list was not read. A real paper cited with its real first author and invented co-authors verified clean — the pattern Topaz et al. describe as fabrications attributed to real researchers, and 3 of our 7 misses on that set. Behind ENABLE_COAUTHOR_CHECK (default off) the comparator now requires every claimed co-author to appear somewhere on the resolved record, reporting each one it cannot place as a coauthor row in mismatches[].
It downgrades matched to ambiguous (review); it never accuses. A co-author disagreement is a citation error, not proof of fabrication, and the accusation path is where a false positive costs the most. The check also refuses to run whenever the comparison would be unsafe — a resolved record listing fewer authors than the citation, any group or collaboration name, or a first author that already failed. Matching is set-containment, never positional, since citation styles reorder authors legitimately.
A not_found did not say which not_found it was. An exhausted title search returned a bare verdict, so “we searched Crossref, PubMed and OpenAlex and found nothing” was indistinguishable from “this does not exist”. Standards documents, software repositories, model cards and institutional reports are real, routinely cited, and indexed in none of those registries — on the external set this was our largest false-positive class. Those responses now carry a caveats entry stating the boundary of the search. It stops short of calling the reference non-academic: the API receives no URL, so that would be a guess.
Both are additive to the response shape. mismatches[].field gains coauthor; two caveat strings are added. Nothing that verified clean before now reads as a fabrication.
no version bumpCrossref carries what publishers and Retraction Watch deposit against a DOI; Europe PMC carries what NLM indexes against a PMID. They disagree in practice. In a 25-work sample of articles Europe PMC marks Retracted Publication, Crossref reported no retraction for 15 — 12 of those had no updated-by entry at all.
Behind ENABLE_EPMC_RETRACTION (default off), the retraction check now consults Europe PMC as a second opinion. Its notices merge into the existing notices[] array with source: "europepmc"; the response shape does not change. Europe PMC is skipped when Crossref already reports a retraction — that verdict is terminal — so the extra lookup is only spent where it could change the answer. A Europe PMC failure is swallowed and the Crossref answer still stands. Europe PMC encodes direction in its type suffix: Retraction in means this work is retracted, Retraction of means this record is the notice about another work — only the “… in” direction is read. With the flag off, behaviour is unchanged.
no version bumpThe retraction check compared Crossref update types against a hyphenated spelling (expression-of-concern) that Crossref does not emit — its registered vocabulary is snake_case (expression_of_concern). A paper carrying only an expression of concern therefore came back clean. Confirmed on live records including 10.1371/journal.pone.0317237 and 10.1016/j.chemosphere.2022.133829, both publisher-deposited. All twelve registered update types are now classified: retraction, partial_retraction, withdrawal and removal set isRetracted; expression_of_concern sets hasConcern; correction, corrigendum, erratum, addendum and clarification set hasCorrections (previously only correction did, so errata and corrigenda were missed). new_edition and new_version are returned as notices without raising a flag — a new edition is not an integrity signal. Separately, notice de-duplication now keys on type and DOI: a publisher erratum and a Retraction Watch retraction can cite the same notice DOI, and keying on the DOI alone discarded the retraction. Notice type values are unchanged — they remain the raw Crossref strings.
Affects ?checks=retraction, /api/retraction-check, /api/audit, and the checkRetraction MCP tool: some works that reported status: "ok" now report concern, correction, or retracted. No version tag is bumped: transform_version covers the normalisation, formatting and resolver chain, which is untouched, and verify_version covers the claimed-vs-resolved comparison, which does not consult retraction status. Retraction facts change upstream on their own cadence and are cached independently of both tags (24h TTL), so cached results correct themselves within a day.
.docx auditcontainment_version 2026-07-23A translated or heavily reworded genuine title scores just as low on title containment as a fabricated one, so title overlap alone could not tell them apart. The references[] path now uses a second, language-invariant signal — the resolved record’s author surnames and year. When they appear in your reference the entry is rescued to “review” with an explanatory caveat and is never flagged as fabricated; when the record has an author and year and neither appears, two signals agree and a wider mismatch band applies; when the record carries neither, the conservative band is kept. Reported as _provenance.containment.corroboration (corroborated / refuted / unavailable). Measured on a curated 85-case corpus: 0 false accusations across 31 genuine references (including 6 translated titles), while catching 67% of realistic partial-overlap fabrications that the previous cutoff missed entirely. Verdicts on the same reference can change, so containment_version is bumped. The compare path (BibTeX / RIS / CSL-JSON) and verify_version are untouched.
.docx upload for the bibliography auditcontainment_version 2026-07-21The bibliography audit now accepts a Word .docx manuscript. Its identifier-bearing references are extracted in your browser and verified via a new containment method: each reference’s identifier is resolved and the resolved title is checked for word-containment in your reference text — catching a real DOI paired with a fabricated title, without a clean parsed title. New API input references[] on POST /api/audit, pinned by the new x-scholar-containment-version header. Because a translated or reworded genuine title scores just as low as a fabricated one, a second, language-invariant signal — the resolved record’s author surnames and year— decides the close calls: when they appear in your reference the entry is rescued to “review” with an explanatory caveat and never flagged as fabricated (reported as _provenance.containment.corroboration). The existing compare path (BibTeX / RIS / CSL-JSON) and verify_version are unchanged. PDFs are not supported — export or save as .docx first.
/api/exportno version bump/api/export now reports its batch summary on x-scholar-batch-* response headers (lines-in, resolved, after-dedupe, duplicates, not-found, errored, skipped, warned), matching the JSON meta that /api/format already returns — the export body is a file, so the summary rides on headers instead. Additive metadata only; output bytes are unchanged, so there is no transform_version change.
transform_version 2026-06-16Two builtin-formatter author-rendering bugs are fixed. APA no longer emits a doubled period when the final author ends in an initial (e.g. Aspelmeyer, M.. → Aspelmeyer, M.) — which affected most multi-author APA article citations — and no longer prepends a stray period in the degenerate title-only case. CSE now reduces a single-token given name to one initial instead of upper-casing it in full (e.g. Aspelmeyer MARKUS. → Aspelmeyer M.); already initials-shaped given names like RS are kept as-is. Output-affecting for anyone who pinned APA or CSE plain-text bytes — re-baseline. vancouver, ama, ieee, and all CSL output are unaffected.
* markers in plain-text outputtransform_version 2026-06-11The builtin citation styles (vancouver, ama, apa, ieee) previously emitted Markdown italic markers like *Journal* in their plain-text output, which leaked into the text/output fields of /api/format, /api/format-items, and /api/format/stream (and so into the MCP server, CLI, and extensions). Those fields are now clean plain text; italic intent is carried in a new html field (and renderHtml/outputHtml on the stream) as sanitized <em> markup. Output-affecting for anyone who pinned builtin plain-text bytes - re-baseline. cse and all CSL output are unaffected (they never contained markers). The /api/export plain-text (txt) export and the homepage “Copy as Plain Text” are likewise asterisk-free.
?mode=strict)no version bumpThe resolver’s fixed-order chain now has an explicit, assertable name: ?mode=strict sets x-scholar-resolver-mode: strict and (under ?provenance=1) adds _provenance.mode + selection_reason. Strict means deterministic chain ordering within the currently-deployed transform_version - it is not a version pin or a historical snapshot (see /engineering-principles “What it does not promise”). A best-effort mode (freshest-source selection) is reserved and not yet available; any value other than strict resolves to strict and the header reports strict. Behaviour and output are unchanged - this only names what already happened.
no version bumpThe resolver cache previously used one flat TTL for every record. It is now calibrated by which upstream produced the record (fast-cadence registries like Crossref/DataCite cache shorter than stable ones like doi.org or PubMed) and by the cited work’s publication recency - a current-year paper (most likely to pick up a correction) gets a short TTL, a decades-old one is cached far longer. Net effect: fresher data on recent works, fewer wasted upstream calls on stable old ones. The chosen band is surfaced as _provenance.sources[].ttl_band (recent / mid / frozen) under ?provenance=1. This changes cache lifetime only, never output bytes, so transform_version is unchanged; CACHE_TTL_CALIBRATION=0 reverts to the flat TTL.
_provenance, cached checks, faster arXiv checksno version bumpRetraction + open-access checks (opt-in via ?checks=retraction,oa) gain three refinements. (1) Results are now served from a two-tier cache (memory + KV) with their own TTLs (retraction long, open-access short), independent of transform_version - fewer upstream calls, faster repeat lookups. (2) When ?provenance=1 and ?checks= are both set, the _provenance object carries a new trust_signals summary (per signal: requested / evaluated / status, or skipped_reason: "no_doi"). (3) The open-access check now exposes a direct pdf_url (Unpaywall url_for_pdf). Separately, /api/retraction-check, /api/oa-check, and the ?checks= path now resolve an arXiv input via its deterministic DataCite DOI (10.48550/arXiv.*) instead of the slow arXiv API - removing a recurring timeout. All additive and opt-in; formatted output is byte-identical, so transform_version is unchanged. Retraction/OA remain opt-in (not baked into every resolve).
_provenance (Auditor V2)no version bumpThe opt-in ?provenance=1 object now carries several additive fields beyond transform_version / sources / resolved_at: request_id; fallbacks_tried (resolvers consulted before the winner, omitted when the primary resolved on the first try); per-source cache state (hit/miss/bypass) and upstream_fetched_at; fields_from_source / fields_absent; and normalization steps applied to your input. A new ?provenance=full mode (acknowledged via x-scholar-provenance: full) adds a full_metadata block (funders, ORCID iDs, ROR iDs, license, clinical-trial IDs). Every field is optional and opt-in; default payloads and formatted output are byte-identical, so transform_version is unchanged. Cross-source conflicts are deferred to a later sub-phase. Full shape: /.well-known/sources.json → provenance_schema.
verify_version 2026-06-01 → 2026-06-05A citation whose author is written surname-first with a comma - Chukwuocha, Uchechukwu Madukaku, the form used by many reference styles and by Dublin-Core dc:creator fields (e.g. WHO IRIS records) - is now compared on the surname before the comma. Previously the given names after the comma were folded into the surname comparison, so when the resolved registry record split the same name differently (Crossref stores some names as family Madukaku Chukwuocha) the author scored just below the match threshold and a correct citation was flagged as a high-confidence mismatch. The verifier now reads the comma as the surname boundary and matches the author. The change is additive and recall-safe: a genuinely wrong author before the comma still does not match, so fabricated-author detection is unaffected. transform_version is unchanged (resolver and formatter output bytes are identical).
transform_version 2026-05-26 → 2026-06-05A work published online-first and later in print carries two publication years, often 1–2 years apart (common at Springer, Elsevier, and other publishers). A citation that used the print year was previously scored against the online-first year and flagged with a low-confidence year mismatch - even when the title and author matched exactly. The DOI resolvers now carry the alternate year(s) from Crossref’s published-print / published-online, and the verifier accepts a claimed year matching any of them. A genuinely wrong year - matching neither the online nor the print year - still lowers confidence exactly as before, so erroneous-year detection is unchanged. transform_version is bumped because the resolver record now carries the alternate years (read by the verifier) and to cold-invalidate the resolver cache; the formatted citation output is byte-identical (formatters use the primary issued year only).
Old Wiley/SICI DOIs carry interior angle brackets - e.g. 10.1002/1521-4141(200205)32:5<1223::AID-IMMU1223>3.0.CO;2-5. The identifier detector stopped at the first <, resolving a truncated, non-existent DOI → not_found. The detector now accepts </> inside a DOI body, so these DOIs resolve to the correct record and verify as matched. HTML-adjacent bare DOIs (…/foo</p>) are unaffected - a SICI bracket is followed by a digit, an HTML tag by a letter, so trailing tags are still stripped. Rides the same-day transform_version / verify_version = 2026-06-05.
arXiv identifiers were resolved via export.arxiv.org (the Atom API), which is rate-limited (~1 req/3s per IP) and frequently returns 429/503 → a transient 502 on cold ids. They now resolve via the arXiv DataCite DOI (10.48550/arXiv.{id}) first - a robust, non-rate-limited registry that carries the same arXiv preprint title, the submission date, the authors, and the published-journal DOI link. The Atom API remains as a fallback for the rare id absent from DataCite or during a DataCite outage, and arXiv results are now cached like other identifiers. Rides the same-day transform_version / verify_version = 2026-06-05.
Some publishers (e.g. Dove Press) return titles wrapped in entity-encoded tags - <p>…</p>. The verifier strips raw HTML tags (<i>, <sup>) before comparison, but the entity-encoded form collapsed the brackets to spaces and leaked the tag-name letter (p) as a spurious token, dragging title similarity below the match threshold (a false mismatch). The angle-bracket entities </> are now decoded before the tag strip, so entity-encoded tags are removed cleanly. Rides the same-day verify_version = 2026-06-05.
A citing author often writes a Greek letter out in full (Calcineurin Aalpha, IFN-gamma) where the canonical title carries the symbol (Calcineurin Aα, IFN-γ). The symbol survived normalisation as a lone non-Latin token and dragged title similarity below the match threshold (a false mismatch). Greek letters are now transliterated to their standard Latin romanisation (α→alpha, β→beta, γ→gamma, …) on both sides before comparison, so the spelled-out and symbol forms compare like-for-like. Folding is recall-safe - it can only raise similarity when one side spelled the letter out; a fabricated title is not rescued by folding a single letter. Rides the same-day verify_version = 2026-06-05.
Two title-normalisation gaps found in a coverage sweep: a resolved title carrying a character as a numeric HTML entity (IFN-γ, γ) had the entity dropped to whitespace instead of decoded, so an entity-encoded Greek letter or accent never reached transliteration; and Latin-extended letters with no Unicode decomposition (ø, ł, æ, œ, ð, þ, ß) survived as non-ASCII tokens that dragged title similarity below the match threshold (worst in a leading word - Łukasiewicz, Sørensen). Numeric entities are now decoded to their character, and those letters are folded to their conventional ASCII romanisation (ø→o, æ→ae, ß→ss, …). Both are recall-safe - they only raise similarity when the two sides spell a character differently. Rides the same-day verify_version = 2026-06-05.
not_found / 404 instead of a server errorverify_version 2026-05-26 → 2026-06-01A DOI that no registry can resolve - a fabricated or unregistered identifier, or a real DOI whose registrant returns a body the registration agencies cannot serve as JSON (e.g. some non-Crossref prefixes) - is now classified as a definitive not-found rather than an upstream error. On /api/verify such an identifier now yields a not_found (or ambiguous when a title search finds the real work under a different identifier) verdict instead of a 502 RESOLVE_ERROR - the expected answer for a fabrication check. On /api/format, /api/export, and /api/format-items, a request whose every identifier is unresolvable now returns a clean 404 NOT_FOUND instead of a 500/502. Genuine upstream outages (timeout / 5xx / rate-limit / network) are unchanged - they still return 502/504 so a client can retry. Note: a not_found verdict means “no usable record was found”, which is not by itself proof of fabrication; the verifier runs a title search to separate a fabricated work from a real one cited under a bad identifier. transform_version is unchanged (formatted output bytes are identical).
codeAll 4xx/5xx responses from the API now include a stable code field in the JSON body and mirror it in the x-error-code response header. Previously the field was present only on classified errors (auth, upstream, verifier, maintenance); validation, body-parse, unknown-style, and payload-size errors omitted it. Integrators can now branch on a stable token without parsing the human-readable message. New tokens introduced: VALIDATION_ERROR, UNKNOWN_STYLE, INVALID_CONTENT_TYPE, EMPTY_BODY, MALFORMED_JSON, and PAYLOAD_TOO_LARGE. The OpenAPI ErrorResponse schema now requires code. Formatted citation output is unchanged, so transform_version is not bumped.
When a work resolves with only an arXiv identifier (no DOI), the CSV, EndNote (Refer and XML), RefWorks, Zotero RDF, and MEDLINE/NBIB exporters now emit an arXiv carrier so the record imports cleanly into reference managers. The five URL-based formats add https://arxiv.org/abs/<id> as the URL when no explicit URL or DOI is present; MEDLINE emits AID - arXiv:<id> [arxiv] per NLM convention. This matches the behaviour already shipped for CSL-JSON, BibTeX, and RIS. Output for items that already carried a DOI or URL is byte-identical, so transform_version is not bumped.
verify_version: 2026-05-25 → 2026-05-26Two further false-accusation fixes for correctly-cited papers whose first-author field is stored in reference-list shorthand:
“P Giral”, “MJ Fila”, “Kristine Sørensen”), or a bare surname against an unsplit resolved full name (“kim” vs “Hongryel Kim”) - both common in real reference lists - now match. The claimed surname is recognised as a leading-or-trailing token run of the resolved name in either order. The check is additive and recall-safe: an unrelated first author is neither the leading nor the trailing run, and the title comparison gates fabrications regardless.“Collaborators” (e.g. the GBD “… Collaborators” consortium form) are now recognised as group authors, alongside the existing investigators / group / consortium / collaboration markers.Verdicts are deterministic at verify_version=2026-05-26 going forward. If you pinned verify output against the previous baseline, re-baseline against the current version.
transform_version: 2026-05-25 → 2026-05-26ISBN resolution via Google Books. The ISBN resolver consults Open Library first and Google Books on miss. The Google Books fallback is now authenticated and scoped to country=US; previously the keyless request was quota-limited from server egress and silently failed, so an ISBN that Open Library does not hold could come back unresolved. Affected ISBNs now resolve. The API key is never included in returned source links or error messages.
Output is deterministic at transform_version=2026-05-26 going forward.
verify_version: 2026-05-24 → 2026-05-25Further verifier-accuracy fixes for correctly-cited papers, the retirement of the group-author confidence cap, and a new point-of-use caveats field:
“NETTER-1 trial investigators. Phase 3 trial of…” or “…on behalf of the NETTER-1 trial investigators”) is no longer false-flagged - the group affix is stripped before the title comparison. The strip is additive (scored alongside the unstripped title, best score wins), so it can only rescue a correct citation, never let a fabricated title through.PMID now matches, paired with the resolver change below.“Title. A study of…” or “Title: A study of…”) now matches when the source structures the subtitle separately from the title (Crossref): the verifier recombines the canonical title and subtitle to compare, rather than scoring against the title alone.matched at the confidence the rest of the comparison earns, with a caveat (below) noting the group author itself could not be cross-checked. This removes an asymmetry that keyed off a registry storage quirk._provenance.caveats - a list of deterministic limitations of the verdict (resolved via title-search, an ISSN-only container identifier, a non-Latin-script title, a publication-year gap, or an unconfirmed group author). Additive metadata, omitted entirely on a clean high-confidence match.Verdicts are deterministic at verify_version=2026-05-25 going forward. If you pinned verify output against the previous baseline, re-baseline against the current version.
transform_version: 2026-05-22 → 2026-05-25PubMed collective authors. A paper whose PubMed record stores a collaboration/group author (ESummary authtype: “CollectiveName”, e.g. RECOVERY Collaborative Group) now renders the group name intact in formatted citations and exports, instead of mangling it into a personal name ({family:"RECOVERY", given:"Collaborative Group"}). Affects records resolved via PMID.
Output is deterministic at transform_version=2026-05-25 going forward.
verify_version: 2026-05-22 → 2026-05-24Reduced false accusations on correctly-cited papers whose author or identifier metadata is messy at the source. Crossref and PubMed reference lists routinely store authorship in ways that previously tripped the verifier; five fixes:
“Sørensen Kristine”, “Borchetia S., Das S.C., …”) now matches via the leading surname run when the resolved surname is a token-prefix.{family:"Philip R.", given:"Cohen"}) are now recognised when the claimed surname matches the resolved given name.ambiguous; ambiguous is reserved for a genuinely different work matching the claim.RECOVERY Collaborative Group, PIOPED II Investigators) is no longer false-flagged. When the record carries the group it matches directly (after normalising a leading The); when the record lists only the individual authors, the work is confirmed by title + identifier and returned as matched at medium confidence - signalling that the group author itself could not be cross-checked.Verdicts are deterministic at verify_version=2026-05-24 going forward. If you pinned verify output against the previous baseline, re-baseline against the current version.
transform_version: 2026-05-04 → 2026-05-22Re-baselined to reflect resolver and normalisation refinements shipped since the previous baseline. Output for affected identifiers may differ from the 2026-05-04 baseline:
“Family, Given”) explicitly, fixing a family/given swap and a trailing-comma artifact for arXiv items.<i>, &) is stripped from titles and container titles for similarity comparison and clean display.Output is deterministic at transform_version=2026-05-22 going forward. If you pinned expected output against the previous baseline, re-baseline against the current version.
verify_version: 2026-05-11 → 2026-05-22not_found with skipped_reason: “insufficient_claim” instead of a misleading high-confidence verdict.transform_version)First published transform_version. Established the byte-identical-output guarantee, the unconditional x-scholar-transform-version response header, the /.well-known/sources.json resolver-chain manifest, and the copy-paste verification kit.
transform_version is bumpedtransform_version