feat: offline / reproducible retractions via Retraction Watch snapshot (v0.2.0)#6
Closed
musharna wants to merge 12 commits into
Closed
feat: offline / reproducible retractions via Retraction Watch snapshot (v0.2.0)#6musharna wants to merge 12 commits into
musharna wants to merge 12 commits into
Conversation
- README: "Offline / reproducible retractions" section (fetch-retractions, --retraction-db, snapshot-is-authoritative, byline still live). - NOTICE: attribute Crossref + Retraction Watch (Center for Scientific Integrity); state ghostcite downloads on demand and does not redistribute the data (the data carries no explicit CC0/permissive grant — verified live). - CHANGELOG: 0.2.0 entry. - Bump version 0.1.0 -> 0.2.0 (pyproject, __init__, version-string tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a DOI is absent from CrossRef (404 or unreachable), the snapshot lookup was silently skipped, dropping retraction signals the DB knew about. Decouple the RW DB lookup from CrossRef resolution: if the DB flags a DOI and CrossRef returned nothing, synthesize a minimal CanonicalRecord so evaluate() still emits the retraction tier. Regression test: test_retraction_db_flags_even_when_crossref_404s (was: exit 0 / no retraction line; now: exit 1 + "RETRACTED per Retraction Watch"). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ride; correct row_count
- Add `retraction_override` kwarg to `evaluate()` so the offline DB can
assert retraction even when CrossRef returns None (no record), without
synthesizing a stub CanonicalRecord that triggers the misleading
"CrossRef record has no author data" UNRESOLVABLE message.
When canonical is None and override fires, output is RETRACTION + a
clean UNRESOLVABLE("DOI not found"), not the old spurious author-data
warning.
- Remove the CanonicalRecord stub construction from cli.py; the rdb path
now passes retraction_override=(retracted, eoc) to evaluate() directly.
CanonicalRecord removed from cli.py imports (unused).
- Fix row_count off-by-one in fetch_retractions: bodies without a
trailing newline were undercounted by 1 (e.g. 2-row body reported 1).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Owner
Author
|
Closing — keeping development private until the differentiating moat is built. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a deterministic, no-GPU, offline retraction source — the determinism-axis deepening from the Lane-C plan. ghostcite can now flag retractions from a local Retraction Watch snapshot instead of the live CrossRef
updated-bysignal, making the retraction tier byte-reproducible and broader-coverage (RW ~43k vs CrossRef ~14k flags).ghostcite fetch-retractions --mailto you@org→ downloads the snapshot to the XDG cache with a provenance.meta.jsonsidecar.--retraction-db <path>pins an exact snapshot (CI-reproducible);--retraction-db noneforces live CrossRef; auto-uses the cache if present.New module
src/ghostcite/retractions.py(RetractionDB.load/lookup,normalize_doi,default_cache_path,resolve_db,fetch_retractions). Version bump 0.1.0 → 0.2.0.Provenance / license
Retraction Watch data is publicly available via Crossref Labs but carries no explicit CC0/permissive grant (verified live). ghostcite downloads it on demand and does not redistribute it;
NOTICE+ README credit Crossref + Retraction Watch (The Center for Scientific Integrity).Test Plan
pytest -m "not live"— 144 passed, ruff + mypy clean, coverage 89%tests/test_retractions_live.pyfetch→load→lookup round-trip against the real Crossref Labs endpoint passes (confirms Wakefield 1998 flagged; runs weekly inlive-tests.yml)--retraction-dbflags a DOI CrossRef didn't (incl. the 404 case →RETRACTED+ accurateDOI not found, no misleading message);--retraction-db noneheader🤖 Generated with Claude Code