{"id":51504940,"url":"https://github.com/dcondrey/zotero-validate","last_synced_at":"2026-07-07T23:01:27.816Z","repository":{"id":360153982,"uuid":"1248926757","full_name":"dcondrey/zotero-validate","owner":"dcondrey","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-24T10:04:15.000Z","size":178,"stargazers_count":0,"open_issues_count":12,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T10:45:37.438Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dcondrey.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-25T07:18:57.000Z","updated_at":"2026-06-24T10:02:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dcondrey/zotero-validate","commit_stats":null,"previous_names":["dcondrey/zotero-validate"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dcondrey/zotero-validate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcondrey%2Fzotero-validate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcondrey%2Fzotero-validate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcondrey%2Fzotero-validate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcondrey%2Fzotero-validate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dcondrey","download_url":"https://codeload.github.com/dcondrey/zotero-validate/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcondrey%2Fzotero-validate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35245324,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-07T02:00:07.222Z","response_time":90,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-07T23:01:27.141Z","updated_at":"2026-07-07T23:01:27.802Z","avatar_url":"https://github.com/dcondrey.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zotero Reference Validator\n\n[![CI](https://github.com/dcondrey/zotero-validate/actions/workflows/ci.yml/badge.svg)](https://github.com/dcondrey/zotero-validate/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nA Zotero 7 plugin that validates reference metadata against scholarly sources to ensure your library is accurate. Cross-references each item against authoritative databases, flags discrepancies, suggests corrections, and optionally uses LLM-based semantic adjudication for ambiguous cases.\n\n## Installation\n\n1. Download the latest `.xpi` from the [Releases](../../releases) page.\n2. In Zotero, go to **Tools \u003e Add-ons**.\n3. Click the gear icon and select **Install Add-on From File**, then choose the `.xpi`.\n\nThe plugin will check for updates automatically.\n\n## Quick Start\n\n1. Open **Zotero Preferences** and find the **Reference Validator** pane.\n2. (Optional) Enter your email for Crossref/OpenAlex polite pools, or add API keys for Semantic Scholar and LLM providers.\n3. Right-click any item (or select multiple) and choose **Validate Reference**.\n4. View results in the validation summary window. The **Details** column shows diagnostic information including any source errors.\n5. Access the **Validated References Library** from the right-click menu to browse all validated items, revalidate, or remove entries.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eFeatures\u003c/strong\u003e -- multi-source verification, LLM fallback, batch processing, auto-updates\u003c/summary\u003e\n\n- **Multi-source verification** -- validates against up to 14 scholarly databases simultaneously\n- **Global reference library** -- validated references are cached locally with compressed storage, usage tracking, and a dedicated viewer UI\n- **Tiered classification** -- items are marked VERIFIED, VERIFIED WITH CORRECTIONS, or FLAGGED based on cross-source consensus\n- **Smart comparison** -- order-agnostic author matching, two-row Levenshtein title similarity, ISBN-10/13 normalization, arXiv version handling\n- **Polymorphic field validation** -- checks volume/issue/pages for journal articles, publisher for books, based on item type\n- **LLM fallback** -- optional semantic adjudication via OpenAI, Anthropic, or Google Gemini with structured JSON output\n- **Batch processing** -- validate entire collections with native Zotero progress feedback and fault-tolerant execution\n- **Per-adapter rate limiting** -- token bucket algorithm with FIFO queue and processing lock respects each API's rate limits\n- **In-flight deduplication** -- duplicate items in a batch share a single network request\n- **Freshness caching** -- skips recently validated items (configurable window, default 90 days)\n- **Auto-updates** -- plugin checks for new versions automatically via GitHub releases\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eSupported Sources\u003c/strong\u003e -- 14 scholarly databases across three tiers\u003c/summary\u003e\n\n### Tier 1 (Authoritative)\n\n| Source | Credentials | Lookup | Search |\n|--------|-------------|--------|--------|\n| [Crossref](https://www.crossref.org/) | Email (polite pool) | DOI | Title + Author |\n| [OpenAlex](https://openalex.org/) | Email (polite pool) | DOI, PMID, arXiv | Title |\n| [Semantic Scholar](https://www.semanticscholar.org/) | API key (optional) | DOI, arXiv, PMID, ISBN | Title + Author |\n| [arXiv](https://arxiv.org/) | None | arXiv ID | Title + Author |\n| [PubMed](https://pubmed.ncbi.nlm.nih.gov/) | None | PMID, DOI | Title + Author |\n| [DataCite](https://datacite.org/) | None | DOI | Title |\n| [Europe PMC](https://europepmc.org/) | None | DOI, PMID, arXiv | Title |\n\n### Tier 2 (Supplementary)\n\n| Source | Credentials | Lookup | Search |\n|--------|-------------|--------|--------|\n| [DBLP](https://dblp.org/) | None | DBLP key | Title + Author |\n| [ACL Anthology](https://aclanthology.org/) | None | ACL ID | Title (via Crossref) |\n| [OpenReview](https://openreview.net/) | None | OpenReview ID | Title |\n| [Open Library](https://openlibrary.org/) | None | ISBN | Title + Author |\n| [ORCID](https://orcid.org/) | None | DOI | -- |\n\n### Tier 3 (Supporting)\n\n| Source | Credentials | Lookup | Search |\n|--------|-------------|--------|--------|\n| [OpenCitations](https://opencitations.net/) | None | DOI | -- |\n| [Unpaywall](https://unpaywall.org/) | Email (reused) | DOI | -- |\n\n**Tier 1** and **Tier 2** sources contribute to the verification threshold. The default minimum is 2 agreeing sources. Tier 3 sources provide supplementary metadata.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eHow It Works\u003c/strong\u003e -- classification, LLM adjudication, validation pipeline\u003c/summary\u003e\n\n### Classification\n\n| Status | Tag Applied | Meaning |\n|--------|------------|---------|\n| **VERIFIED** | `validated` | 2+ sources confirm all critical fields match |\n| **VERIFIED WITH CORRECTIONS** | `validated-with-corrections` | Sources confirm the item but found field discrepancies |\n| **FLAGGED** | `validation-flagged` | Insufficient matches or conflicting data |\n\nThe classifier distinguishes between **conflicting data** (active mismatches that block verification) and **missing data** (fields a source doesn't return, which generate corrections but don't penalize the match). This prevents a strong DOI match from being downgraded just because a source omits a year.\n\n### LLM Adjudication\n\nWhen an item is FLAGGED and LLM adjudication is enabled, the plugin sends item metadata and candidate records to the configured LLM. The LLM returns a structured JSON verdict:\n\n```json\n{\n  \"match\": true,\n  \"explanation\": \"Same paper, preprint vs published version\",\n  \"corrections\": [{\"field\": \"year\", \"suggested\": \"2024\"}]\n}\n```\n\nCorrections from the LLM flow through to the results UI alongside source-derived corrections. The prompt includes injection defenses (newline stripping, length caps, instruction boundary).\n\n### Validation Pipeline\n\n```\nItem selected\n  |\n  v\nGlobal library check --\u003e cached \u0026 fresh? --\u003e return cached result\n  |\n  v\nFreshness check (extra field) --\u003e cached? --\u003e return cached result\n  |\n  v\nExtract \u0026 validate identifiers (DOI, ISBN, PMID, arXiv ID)\n  |\n  v\nParallel adapter queries (per-adapter token bucket rate limiting)\n  |  - In-flight deduplication coalesces identical queries\n  |  - Fault-tolerant: individual adapter failures don't kill the batch\n  |  - Cross-identifier extraction enriches downstream lookups\n  |\n  v\nField comparison (polymorphic by item type)\n  |  - Titles: two-row Levenshtein similarity \u003e= 0.95\n  |  - Authors: order-agnostic set matching with initials support\n  |  - Year: exact match\n  |  - Journal: volume, issue, pages\n  |  - Book: publisher\n  |\n  v\nTiered classification (Tier 1+2 consensus)\n  |\n  v\nLLM adjudication (optional, for FLAGGED items only)\n  |\n  v\nPersist: tags + report in extra field (mutation-shielded)\n  |\n  v\nAdd to global reference library (with usage tracking)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eConfiguration\u003c/strong\u003e -- source emails, API keys, freshness window, LLM adjudication\u003c/summary\u003e\n\nAll settings are accessible from **Zotero Preferences \u003e Reference Validator**.\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| Source emails | -- | Email for Crossref/OpenAlex polite pool access. Also used by Unpaywall. |\n| Semantic Scholar API key | -- | Optional. Increases rate limits from 100/5min to 1000/5min. |\n| Minimum sources | 2 | Number of agreeing Tier 1/2 sources required for VERIFIED status. |\n| Freshness window | 90 days | Skip re-validation if item was checked within this period. |\n| Request timeout | 10 seconds | Per-request timeout for source API calls. |\n| LLM adjudication | Off | Enable LLM fallback for FLAGGED items. Requires an API key. |\n| LLM API keys | -- | OpenAI, Anthropic, or Gemini. First configured key is used. |\n\n### Recommended Configuration\n\n- **General use**: Enter your email for Crossref and OpenAlex. Leave other defaults.\n- **CS/ML researchers**: DBLP, ACL Anthology, and OpenReview are enabled by default.\n- **Biomedical researchers**: PubMed and Europe PMC are enabled by default. Consider adding a Semantic Scholar API key.\n- **Book-heavy libraries**: Open Library is enabled by default for ISBN lookups.\n- **Dataset/software citations**: DataCite is enabled by default for non-journal DOIs.\n- **Large batch validation**: Increase the request timeout if you experience rate limiting.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eGlobal Reference Library\u003c/strong\u003e -- local cache, compressed storage, usage tracking\u003c/summary\u003e\n\nValidated references are automatically saved to a local library stored in your Zotero data directory. The library:\n\n- **Caches validation results** -- subsequent validations of the same reference skip remote API calls entirely if the cached result is fresh\n- **Compresses automatically** -- uses gzip compression when the library exceeds 500 entries\n- **Tracks usage** -- records how many times each reference has been used and in which collections/papers\n- **Supports manual management** -- view, revalidate, or remove entries from the library viewer (right-click menu \u003e \"View Validated References Library\")\n\nThe library viewer displays each entry's validation status, title, identifier, validation date, usage count, associated papers, and provides Revalidate and Remove buttons.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eDevelopment\u003c/strong\u003e -- build, test, project structure\u003c/summary\u003e\n\n### Prerequisites\n\n- Node.js 20+\n- npm\n\n### Build\n\n```sh\nnpm install\nnpm run build\n```\n\n### Package\n\n```sh\nnpm run package\n```\n\nCreates `zotero-reference-validator.xpi` in the project root.\n\n### Test\n\n```sh\nnpm test\n```\n\n91 tests across 5 suites covering all adapters, the orchestrator, LLM client, comparison engine, and classifier.\n\n### Type Check\n\n```sh\nnpx tsc --noEmit\n```\n\n### Project Structure\n\n```\nsrc/\n  bootstrap.ts          Plugin lifecycle with window observer pattern\n  orchestrator.ts       Validation pipeline: rate limiting, deduplication, caching\n  classifier.ts         Tiered consensus classification\n  comparison.ts         Field comparison with LaTeX parser and set-based author matching\n  llm.ts                LLM adjudication (OpenAI, Anthropic, Gemini)\n  library.ts            Global validated references library with compressed storage\n  library-ui.ts         Library viewer window\n  http.ts               Shared fetch wrapper with timeout and Content-Type validation\n  menu.ts               MenuManager with per-window lifecycle\n  preferences.ts        Preference pane registration and teardown\n  ui.ts                 Validation results window\n  types.ts              Shared type definitions\n  zotero.d.ts           Zotero runtime type declarations\n  adapters/\n    utils.ts            Shared author name parsing and date utilities\n    crossref.ts         Crossref (Tier 1)\n    openalex.ts         OpenAlex (Tier 1)\n    semanticscholar.ts  Semantic Scholar (Tier 1)\n    arxiv.ts            arXiv (Tier 1)\n    pubmed.ts           PubMed E-utilities (Tier 1)\n    datacite.ts         DataCite (Tier 1)\n    europepmc.ts        Europe PMC (Tier 1)\n    dblp.ts             DBLP (Tier 2)\n    aclanthology.ts     ACL Anthology (Tier 2)\n    openreview.ts       OpenReview (Tier 2)\n    openlibrary.ts      Open Library (Tier 2)\n    orcid.ts            ORCID (Tier 2)\n    opencitations.ts    OpenCitations (Tier 3)\n    unpaywall.ts        Unpaywall (Tier 3)\ntests/\n  orchestrator.test.ts  Validation flow, caching, persistence\n  llm.test.ts           Response parsing, structured output, prompt safety\n  adapters.test.ts      Adapter normalize/transform methods and shared utils\n  comparison.test.ts    Title, author, identifier, venue comparison\n  classifier.test.ts    Classification thresholds and edge cases\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eArchitecture\u003c/strong\u003e -- rate limiting, deduplication, mutation shield, confidence calibration\u003c/summary\u003e\n\n### Rate Limiting\n\nEach adapter has its own token bucket rate limiter with configurable `perSecond` and `concurrent` limits. The limiter uses a FIFO promise queue with a processing lock to prevent timer storms under burst load. When a slot opens (via `release()`), queued requests are drained immediately with dynamically calculated delays based on token deficit.\n\n### In-Flight Deduplication\n\nWhen validating a batch containing duplicate items (common in messy libraries), the orchestrator generates a deterministic cache key per adapter + identifier. If an identical query is already in flight, subsequent items hook into the same promise rather than issuing redundant network requests.\n\n### Mutation Shield\n\nTo prevent infinite loops when background notifiers observe item changes, the orchestrator tracks item IDs in a `programmaticMutations` set during persistence. External observers can check `Orchestrator.isShielded(itemId)` to skip programmatic modifications.\n\n### Confidence Calibration\n\nEach adapter returns a confidence score with its results. ID-based lookups (DOI, PMID) return 1.0 confidence. Title-based searches return 0.8-0.85. This prevents the classifier from treating fuzzy title matches with the same weight as exact DOI lookups.\n\n### Cross-Identifier Extraction\n\nWhen an adapter fetches a record by DOI, it also extracts secondary identifiers (PMID, arXiv ID, ISBN) from the response. These enriched identifiers enable downstream adapters to perform their own direct lookups rather than falling back to title search.\n\n\u003c/details\u003e\n\n## Troubleshooting\n\n| Symptom | Cause | Fix |\n|---------|-------|-----|\n| All items FLAGGED | Only 1 adapter returning results | Check network; add email for polite pool; lower min_sources to 1 |\n| Validation hangs | API timeout | Increase `behavior.timeout_sec` in preferences |\n| \"Errors: Crossref: AbortError\" | Request timed out | Increase timeout or reduce batch size |\n| Duplicate menu items | Plugin re-enabled without restart | Restart Zotero (fixed in v0.1.0+) |\n| Preferences don't save | Missing preference binding | Update to latest version |\n| Library file growing large | Many validated references | Library auto-compresses above 500 entries |\n\n## Security and Privacy\n\nSee [SECURITY.md](SECURITY.md) for the full threat model and data handling details.\n\n**Summary**: The plugin sends item metadata (titles, authors, identifiers) to configured scholarly APIs. If LLM adjudication is enabled, flagged item metadata is also sent to the configured LLM provider. No data is sent to any telemetry, tracking, or author-controlled services. API keys are stored locally in Zotero's preferences. The global reference library is stored locally in your Zotero data directory.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcondrey%2Fzotero-validate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdcondrey%2Fzotero-validate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcondrey%2Fzotero-validate/lists"}