linkml-reference-validator 0.2.1rc2__tar.gz → 0.2.1rc3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/PKG-INFO +1 -1
  2. linkml_reference_validator-0.2.1rc3/docs/assets/zotero-find-full-text.png +0 -0
  3. linkml_reference_validator-0.2.1rc3/docs/how-to/export-cache-to-zotero.md +114 -0
  4. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/fetch-full-text-and-pdfs.md +102 -0
  5. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/reference/cli.md +63 -0
  6. linkml_reference_validator-0.2.1rc3/docs/superpowers/plans/2026-08-05-private-library-full-text.md +190 -0
  7. linkml_reference_validator-0.2.1rc3/docs/superpowers/specs/2026-08-05-private-library-full-text-design.md +309 -0
  8. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/mkdocs.yml +1 -0
  9. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/cli/cache.py +373 -0
  10. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/cli/lookup.py +3 -0
  11. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/acquire.py +80 -1
  12. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/etl/csl_export.py +73 -0
  13. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/fulltext/__init__.py +2 -0
  14. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/etl/fulltext/zotero.py +252 -0
  15. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/identifiers.py +19 -0
  16. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/reference_fetcher.py +192 -46
  17. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/pmid.py +109 -27
  18. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/models.py +45 -0
  19. linkml_reference_validator-0.2.1rc3/tests/test_acquire.py +224 -0
  20. linkml_reference_validator-0.2.1rc3/tests/test_cli_cache_enrich.py +227 -0
  21. linkml_reference_validator-0.2.1rc3/tests/test_cli_cache_export.py +246 -0
  22. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_models.py +13 -0
  23. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_reference_fetcher.py +215 -1
  24. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_sources.py +149 -0
  25. linkml_reference_validator-0.2.1rc3/tests/test_zotero_provider.py +277 -0
  26. linkml_reference_validator-0.2.1rc2/src/linkml_reference_validator/cli/cache.py +0 -143
  27. linkml_reference_validator-0.2.1rc2/tests/test_acquire.py +0 -116
  28. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.claude/.mcp.json +0 -0
  29. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.claude/settings.json +0 -0
  30. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.copier-answers.yml +0 -0
  31. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.devcontainer/devcontainer.json +0 -0
  32. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.devcontainer/post_create.sh +0 -0
  33. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.editorconfig +0 -0
  34. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/actions/claude-code-action/action.yml +0 -0
  35. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/actions/claude-issue-summarize-action/action.yml +0 -0
  36. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/actions/claude-issue-triage-action/action.yml +0 -0
  37. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/ai-controllers.json +0 -0
  38. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/copilot-instructions.md +0 -0
  39. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/copilot-setup-steps.yml +0 -0
  40. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/dependabot.yml +0 -0
  41. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/workflows/claude-code-review.yml +0 -0
  42. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/workflows/claude-issue-summarize.yml +0 -0
  43. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/workflows/claude-issue-triage.yml +0 -0
  44. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/workflows/claude-ontology-review.yml +0 -0
  45. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/workflows/claude.yml +0 -0
  46. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/workflows/deploy-docs.yaml +0 -0
  47. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/workflows/dragon-ai.yml +0 -0
  48. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/workflows/main.yaml +0 -0
  49. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.github/workflows/pypi-publish.yaml +0 -0
  50. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.gitignore +0 -0
  51. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.goosehints +0 -0
  52. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.pre-commit-config.yaml +0 -0
  53. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/.yamllint.yaml +0 -0
  54. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/AGENTS.md +0 -0
  55. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/BUG_GEO_ACCESSION_TO_UID.md +0 -0
  56. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/BUG_ZENODO_DOI_AND_URL_HANDLING.md +0 -0
  57. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/CLAUDE.md +0 -0
  58. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/CLI-ISSUES.md +0 -0
  59. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/CODE_OF_CONDUCT.md +0 -0
  60. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/CONTRIBUTING.md +0 -0
  61. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/FEATURE_REQUEST_REPAIR.md +0 -0
  62. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/LICENSE +0 -0
  63. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/README.md +0 -0
  64. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/REVIEW2.md +0 -0
  65. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/REVIEW3.md +0 -0
  66. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/ai.just +0 -0
  67. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/copilot-instructions.md +0 -0
  68. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/about.md +0 -0
  69. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/concepts/content-types.md +0 -0
  70. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/concepts/editorial-conventions.md +0 -0
  71. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/concepts/how-it-works.md +0 -0
  72. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/elements/.gitkeep +0 -0
  73. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/add-reference-source.md +0 -0
  74. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/repair-validation-errors.md +0 -0
  75. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/skip-unsupported-references.md +0 -0
  76. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/use-local-files-and-urls.md +0 -0
  77. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/validate-clinical-trials.md +0 -0
  78. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/validate-dois.md +0 -0
  79. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/validate-entrez.md +0 -0
  80. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/validate-obo-files.md +0 -0
  81. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/validate-titles.md +0 -0
  82. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/how-to/validate-urls.md +0 -0
  83. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/index.md +0 -0
  84. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/notebooks/01_getting_started.ipynb +0 -0
  85. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/notebooks/02_advanced_usage.ipynb +0 -0
  86. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/notebooks/03_python_api.ipynb +0 -0
  87. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/notebooks/04_obo_validation.ipynb +0 -0
  88. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/notebooks/05_geo_validation.ipynb +0 -0
  89. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/quickstart.md +0 -0
  90. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/setup-guide.md +0 -0
  91. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/superpowers/plans/2026-06-12-modular-pdf-fulltext-fetching.md +0 -0
  92. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/superpowers/specs/2026-06-12-modular-pdf-fulltext-fetching-design.md +0 -0
  93. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/templates-linkml/README.md +0 -0
  94. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/todo.md +0 -0
  95. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/troubleshooting.md +0 -0
  96. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/docs/tutorials/complete-workflow.md +0 -0
  97. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/examples/README.md +0 -0
  98. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/justfile +0 -0
  99. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/mypy.ini +0 -0
  100. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/notebooks/01_getting_started.ipynb +0 -0
  101. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/notebooks/02_advanced_usage.ipynb +0 -0
  102. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/notebooks/03_python_api.ipynb +0 -0
  103. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/notebooks/README.md +0 -0
  104. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/project/README.md +0 -0
  105. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/project.justfile +0 -0
  106. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/pyproject.toml +0 -0
  107. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/pytest.ini +0 -0
  108. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/__init__.py +0 -0
  109. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/_version.py +0 -0
  110. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/cli/__init__.py +0 -0
  111. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/cli/repair.py +0 -0
  112. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/cli/shared.py +0 -0
  113. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/cli/validate.py +0 -0
  114. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/__init__.py +0 -0
  115. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/extract/__init__.py +0 -0
  116. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/extract/base.py +0 -0
  117. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/extract/html.py +0 -0
  118. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/extract/pdf.py +0 -0
  119. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/extract/xml.py +0 -0
  120. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/fulltext/base.py +0 -0
  121. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/fulltext/epmc_preprint.py +0 -0
  122. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/fulltext/json_api.py +0 -0
  123. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/fulltext/loader.py +0 -0
  124. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/fulltext/openalex.py +0 -0
  125. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/fulltext/pmc.py +0 -0
  126. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/fulltext/unpaywall.py +0 -0
  127. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/__init__.py +0 -0
  128. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/base.py +0 -0
  129. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/clinicaltrials.py +0 -0
  130. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/doi.py +0 -0
  131. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/entrez.py +0 -0
  132. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/file.py +0 -0
  133. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/json_api.py +0 -0
  134. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/loader.py +0 -0
  135. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/ppr.py +0 -0
  136. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/url.py +0 -0
  137. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/sources/utils.py +0 -0
  138. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/etl/text_extractor.py +0 -0
  139. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/field_detection.py +0 -0
  140. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/plugins/REVIEW.md +0 -0
  141. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/plugins/__init__.py +0 -0
  142. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/plugins/reference_validation_plugin.py +0 -0
  143. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/validation/__init__.py +0 -0
  144. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/validation/fuzzy_text_utils.py +0 -0
  145. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/validation/repairer.py +0 -0
  146. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/src/linkml_reference_validator/validation/supporting_text_validator.py +0 -0
  147. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/__init__.py +0 -0
  148. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/conftest.py +0 -0
  149. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/data/test_data_deep_nesting_invalid.yaml +0 -0
  150. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/data/test_data_deep_nesting_valid.yaml +0 -0
  151. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/data/test_data_invalid.yaml +0 -0
  152. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/data/test_data_valid.yaml +0 -0
  153. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/data/test_schema.yaml +0 -0
  154. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/data/test_schema_deep_nesting.yaml +0 -0
  155. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/CLINICALTRIALS_NCT00000001.md +0 -0
  156. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/GEO_GSE12345.md +0 -0
  157. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/PMC_TEST001.md +0 -0
  158. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/PMC_TEST001.txt +0 -0
  159. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/PMC_TEST002.md +0 -0
  160. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/PMC_TEST002.txt +0 -0
  161. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/PMID_TEST001.md +0 -0
  162. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/PMID_TEST001.txt +0 -0
  163. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/PMID_TEST002.md +0 -0
  164. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/PMID_TEST002.txt +0 -0
  165. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/PMID_TEST003.md +0 -0
  166. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/PMID_TEST003.txt +0 -0
  167. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/fixtures/sample.obo +0 -0
  168. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_cli.py +0 -0
  169. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_cli_lookup.py +0 -0
  170. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_cli_repair.py +0 -0
  171. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_clinicaltrials_integration.py +0 -0
  172. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_deep_nesting.py +0 -0
  173. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_doi_preprint.py +0 -0
  174. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_e2e_integration.py +0 -0
  175. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_epmc_preprint_provider.py +0 -0
  176. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_extractors.py +0 -0
  177. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_field_detection.py +0 -0
  178. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_fulltext_loader.py +0 -0
  179. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_fulltext_providers.py +0 -0
  180. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_fuzzy_suggestions.py +0 -0
  181. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_geo_integration.py +0 -0
  182. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_identifiers.py +0 -0
  183. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_json_api_source.py +0 -0
  184. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_optional_linkml_dependency.py +0 -0
  185. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_plugin_integration.py +0 -0
  186. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_pmc_fulltext.py +0 -0
  187. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_ppr_source.py +0 -0
  188. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_real_validation.py +0 -0
  189. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_repair.py +0 -0
  190. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_source_utils.py +0 -0
  191. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_supporting_text_validator.py +0 -0
  192. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_text_extractor.py +0 -0
  193. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_title_validation.py +0 -0
  194. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_unknown_slot_traversal.py +0 -0
  195. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/tests/test_validation_config.py +0 -0
  196. {linkml_reference_validator-0.2.1rc2 → linkml_reference_validator-0.2.1rc3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: linkml_reference_validator
3
- Version: 0.2.1rc2
3
+ Version: 0.2.1rc3
4
4
  Summary: Validation of supporting text from references and publications
5
5
  Author-email: Chris Mungall <cjmungall@lbl.gov>
6
6
  License-Expression: Apache-2.0
@@ -0,0 +1,114 @@
1
+ # Export a Reference Cache to Zotero
2
+
3
+ This workflow turns a checked-in public reference cache into a Zotero collection,
4
+ asks Zotero to find available PDFs, and then inventories the resulting attachments
5
+ without putting closed manuscripts into the project repository.
6
+
7
+ The three stores remain separate:
8
+
9
+ | Store | Purpose | Safe to commit? |
10
+ |---|---|---:|
11
+ | Project `references_cache` | Reproducible public validation evidence | Yes |
12
+ | Zotero library | User-managed references and PDF attachments | No |
13
+ | Private research cache | Extracted user-library text for agent context | No |
14
+
15
+ Normal validation reads only the first store.
16
+
17
+ ## 1. Export metadata from the public cache
18
+
19
+ Run the exporter from the `linkml-reference-validator` checkout. For dismech:
20
+
21
+ ```bash
22
+ uv run linkml-reference-validator cache export \
23
+ --cache-dir /Users/cjm/repos/dismech/references_cache \
24
+ --needs-full-text \
25
+ --output /Users/cjm/Downloads/dismech-zotero.json
26
+ ```
27
+
28
+ `--needs-full-text` is the default. It excludes cache entries that already have
29
+ full text, excludes non-publication identifiers, and deduplicates by normalized
30
+ DOI and then PMID. The CSL JSON output uses an explicit metadata allowlist:
31
+
32
+ - title and authors;
33
+ - journal and year;
34
+ - DOI; or
35
+ - PMID and its public PubMed URL.
36
+
37
+ It never contains cached article text, excerpts, provenance, PDFs, local paths,
38
+ or private-cache data. The command refuses to replace an existing output file;
39
+ use `--force` only after checking the destination. Use `--all` if you explicitly
40
+ want eligible references that already contain full text.
41
+
42
+ ## 2. Import the file into Zotero
43
+
44
+ In Zotero:
45
+
46
+ 1. Choose **File → Import**.
47
+ 2. Choose **A file**.
48
+ 3. Select `dismech-zotero.json`.
49
+ 4. Keep the imported records in a project-specific collection such as
50
+ **dismech**.
51
+
52
+ CSL JSON is a standard format supported by Zotero. Importing the file adds only
53
+ bibliographic records; it does not copy anything from the validator's caches.
54
+
55
+ ## 3. Ask Zotero to find PDFs
56
+
57
+ Select one or more top-level journal articles in the center item list,
58
+ right-click the selection, and choose **Find Full Text**. It is a built-in Zotero
59
+ command, not a plugin.
60
+
61
+ ![Zotero Find Full Text processing a batch of imported references](../assets/zotero-find-full-text.png)
62
+
63
+ The command appears for an eligible parent item that has a DOI and does not
64
+ already have a file attachment. It may also be unavailable in a group library
65
+ where files cannot be added. If it is absent, first test a single article in
66
+ your personal library and confirm that its DOI field is populated.
67
+
68
+ For a large import, start with a modest batch rather than all records at once.
69
+ Publishers can temporarily rate-limit repeated requests, and a later retry may
70
+ find additional files. Zotero reports **Full Text PDF** or **No file found** for
71
+ each attempted item.
72
+
73
+ ## 4. Inventory the enriched Zotero library
74
+
75
+ Once Zotero finishes, scan the project cache again:
76
+
77
+ ```bash
78
+ uv run linkml-reference-validator cache enrich \
79
+ --provider zotero \
80
+ --cache-dir /Users/cjm/repos/dismech/references_cache \
81
+ --dry-run
82
+ ```
83
+
84
+ This read-only scan matches exact DOI, PMID, or PMCID identifiers and reports
85
+ which cached references now have usable Zotero text or PDF attachments. It does
86
+ not invoke **Find Full Text** and does not modify Zotero.
87
+
88
+ After reviewing the matches, materialize them into the separate private research
89
+ cache:
90
+
91
+ ```bash
92
+ uv run linkml-reference-validator cache enrich \
93
+ --provider zotero \
94
+ --cache-dir /Users/cjm/repos/dismech/references_cache \
95
+ --apply
96
+ ```
97
+
98
+ Apply mode leaves `references_cache` unchanged. Private content goes to
99
+ `~/.cache/linkml-reference-validator/private` by default with owner-only
100
+ permissions. Agents may inspect it for context, but validation cannot use it as
101
+ evidence.
102
+
103
+ ## 5. Validate reproducibly
104
+
105
+ Run validation normally. Results remain reproducible because the validator
106
+ reads the public project cache and accepts only public full-text provider results.
107
+ Neither the contents of Zotero nor the machine-specific private cache affect
108
+ validation.
109
+
110
+ ## See also
111
+
112
+ - [Fetching Full Text and PDFs](fetch-full-text-and-pdfs.md)
113
+ - [Zotero: importing standardized formats](https://www.zotero.org/support/kb/importing_standardized_formats)
114
+ - [Zotero: adding files to a library](https://www.zotero.org/support/attaching_files)
@@ -46,9 +46,11 @@ your config YAML or on the `ReferenceValidationConfig` object):
46
46
  |-----|---------|-------------|
47
47
  | `fetch_full_text` | `true` | Attempt to obtain full text via the provider chain when a metadata source does not already return full text. |
48
48
  | `full_text_providers` | `[pmc, epmc_preprint, unpaywall, openalex]` | Ordered list of provider names to try until one yields usable full text. |
49
+ | `private_cache_dir` | `~/.cache/linkml-reference-validator/private` | Separate research cache for closed/user-library full text; never read by validation. |
49
50
  | `pdf_backend` | `pypdf` | Name of the PDF text-extraction backend. |
50
51
  | `download_pdfs` | `true` | If true, persist downloaded PDFs to the files cache directory. |
51
52
  | `full_text_providers_file` | `null` | Optional path to a YAML file defining custom full-text providers. |
53
+ | `zotero_base_url` | `http://localhost:23119/api/users/0` | Read-only Zotero local API library used by the opt-in `zotero` provider. |
52
54
 
53
55
  Two existing keys are also reused by the full-text machinery:
54
56
 
@@ -71,6 +73,106 @@ pdf_backend: pypdf
71
73
  download_pdfs: true
72
74
  ```
73
75
 
76
+ ## Private manuscripts in Zotero
77
+
78
+ The opt-in **`zotero`** provider can find non-open manuscripts that are already
79
+ in your personal Zotero library. It matches exact DOI, PMID, or PMCID identifiers;
80
+ it does not automatically accept fuzzy title matches.
81
+
82
+ Zotero must be running, and its local API must be enabled in Zotero's advanced
83
+ settings. Inventory the reference cache before changing it:
84
+
85
+ ```bash
86
+ linkml-reference-validator cache enrich \
87
+ --provider zotero \
88
+ --cache-dir references_cache \
89
+ --dry-run
90
+ ```
91
+
92
+ The report distinguishes `found`, `not_found`, `already_full_text`, and `error`.
93
+ Dry-run is the default and never changes cache files. After reviewing exact
94
+ matches, apply usable text with:
95
+
96
+ ```bash
97
+ linkml-reference-validator cache enrich \
98
+ --provider zotero \
99
+ --cache-dir references_cache \
100
+ --apply
101
+ ```
102
+
103
+ Apply mode does **not** modify `references_cache`. It writes the enriched entry
104
+ to a separate research cache at `~/.cache/linkml-reference-validator/private`
105
+ by default. Agents may inspect that cache for background context, but ordinary
106
+ validation never reads it and never cites it as evidence. Validation therefore
107
+ has the same inputs locally, in CI, and on another contributor's machine.
108
+
109
+ To keep the research cache in a private repository or another secured location:
110
+
111
+ ```bash
112
+ linkml-reference-validator cache enrich \
113
+ --provider zotero \
114
+ --cache-dir references_cache \
115
+ --private-cache-dir /path/to/private-reference-cache \
116
+ --apply
117
+ ```
118
+
119
+ The same location can be configured for all commands:
120
+
121
+ ```yaml
122
+ cache_dir: references_cache
123
+ private_cache_dir: /path/to/private-reference-cache
124
+ ```
125
+
126
+ Zotero-indexed text is preferred. If Zotero has not indexed an attachment, the
127
+ provider downloads it from Zotero's local PDF endpoint and sends it through the
128
+ normal PDF extractor. The source attachment key and `user_library` access type
129
+ are persisted, but the ephemeral localhost URL is not.
130
+
131
+ !!! warning "Keep research caches private"
132
+
133
+ Private cache entries contain extracted manuscript text and may contain a
134
+ copied PDF. Do not place this cache in a public repository or share it
135
+ unless you have permission to redistribute that content.
136
+
137
+ Do not add `zotero` to `full_text_providers`. Even if it is configured there,
138
+ ordinary validation rejects its `user_library` result and continues to public
139
+ providers. Use `cache enrich` when you want to search Zotero.
140
+
141
+ Support for reproducible validation backed by private manuscripts is a future,
142
+ separate feature. The proposed design is a trusted offline job that emits a
143
+ minimal signed excerpt attestation into the public cache. The validator would
144
+ verify the signature using a checked-in public key; neither agents nor CI would
145
+ have the signing key or permission to generate those files directly.
146
+
147
+ ## Export a cache to a Zotero collection
148
+
149
+ Export references that still need full text as metadata-only CSL JSON:
150
+
151
+ ```bash
152
+ linkml-reference-validator cache export \
153
+ --cache-dir references_cache \
154
+ --format csl-json \
155
+ --needs-full-text \
156
+ --output project-zotero.json
157
+ ```
158
+
159
+ The default `--needs-full-text` mode excludes cache entries that already contain
160
+ full text. It also excludes non-publication identifiers, deduplicates by
161
+ normalized DOI and then PMID, and exports only an explicit metadata allowlist:
162
+ title, authors, journal, year, DOI, PMID, and PubMed URL. Cached article text,
163
+ excerpts, provenance, PDFs, and local paths are never exported. Use `--all` only
164
+ when you deliberately want eligible records that already have full text.
165
+
166
+ The exporter refuses to replace an existing output file unless `--force` is
167
+ given. Review the reported counts, then in Zotero choose **File → Import → A
168
+ file**, select the JSON file, and place the imported items in a project-specific
169
+ collection. Zotero supports CSL JSON as a standard import format. After import,
170
+ select the collection and run **Find Full Text**, then rerun `cache enrich`.
171
+
172
+ See [Zotero's standardized-format import instructions](https://www.zotero.org/support/kb/importing_standardized_formats).
173
+ For the complete workflow with screenshots, see
174
+ [Export a Reference Cache to Zotero](export-cache-to-zotero.md).
175
+
74
176
  ## Preprints
75
177
 
76
178
  Preprints are first-class references. They are increasingly cited for early
@@ -761,6 +761,69 @@ linkml-reference-validator cache lookup PMID:16888623 --content
761
761
  linkml-reference-validator cache lookup PMID:16888623 --no-cache
762
762
  ```
763
763
 
764
+ ## cache export
765
+
766
+ Export public bibliographic metadata to CSL JSON for Zotero import.
767
+
768
+ ```bash
769
+ linkml-reference-validator cache export --output project-zotero.json [OPTIONS]
770
+ ```
771
+
772
+ **Options:**
773
+
774
+ - `--output PATH, -o PATH` - Required destination file
775
+ - `--format TEXT` - Export format (default and currently supported: `csl-json`)
776
+ - `--needs-full-text` - Export only publication records needing full text (default)
777
+ - `--all` - Include eligible records that already contain full text
778
+ - `--cache-dir PATH, -c PATH` - Public reference cache directory
779
+ - `--force, -f` - Replace an existing output file
780
+ - `--config PATH` - Validation configuration file
781
+ - `--verbose, -v` - Enable detailed logging
782
+
783
+ ```bash
784
+ linkml-reference-validator cache export \
785
+ --cache-dir references_cache \
786
+ --needs-full-text \
787
+ --output project-zotero.json
788
+ ```
789
+
790
+ The output is a DOI/PMID-deduplicated metadata allowlist. It never includes
791
+ cached article content, excerpts, PDFs, local paths, or private-cache data.
792
+
793
+ ---
794
+
795
+ ## cache enrich
796
+
797
+ Inventory or enrich existing abstract-only cache entries through one full-text
798
+ provider. This is primarily intended for opt-in private-library providers such
799
+ as Zotero.
800
+
801
+ ```bash
802
+ linkml-reference-validator cache enrich [OPTIONS]
803
+ ```
804
+
805
+ **Options:**
806
+
807
+ - `--provider TEXT` - Registered provider name (default: `zotero`)
808
+ - `--cache-dir PATH, -c PATH` - Reference cache directory
809
+ - `--private-cache-dir PATH` - Separate private research-cache destination (default: `~/.cache/linkml-reference-validator/private`)
810
+ - `--dry-run` - Report matches without changing files (default)
811
+ - `--apply` - Materialize usable matches into the private research cache
812
+ - `--config PATH` - Validation configuration file
813
+ - `--verbose, -v` - Enable detailed logging
814
+
815
+ ```bash
816
+ # Safe inventory
817
+ linkml-reference-validator cache enrich --provider zotero --dry-run
818
+
819
+ # Apply reviewed exact matches
820
+ linkml-reference-validator cache enrich --provider zotero --apply
821
+ ```
822
+
823
+ The public source cache is never modified by this command, and validation never
824
+ reads the private destination. Private-library content may be copyrighted; keep
825
+ the research cache private.
826
+
764
827
  ---
765
828
 
766
829
  ## Reference ID Formats
@@ -0,0 +1,190 @@
1
+ # Private Library Full-Text Integration — Implementation Plan
2
+
3
+ **Date:** 2026-08-05
4
+ **Design:** [Private Library Full-Text Integration](../specs/2026-08-05-private-library-full-text-design.md)
5
+
6
+ All implementation tasks follow test-driven development: add a failing test,
7
+ implement the smallest production change, then run the focused test and the full
8
+ `just test` suite. Network tests use realistic recorded/constructed API responses;
9
+ live tests remain opt-in.
10
+
11
+ ## Phase 1: Zotero local API minimum viable workflow
12
+
13
+ ### 1. Generalize full-text locations
14
+
15
+ Add tests for materializing a local PDF path, rejecting a path over the size cap,
16
+ rejecting a non-PDF file with a `.pdf` suffix, and passing ephemeral request
17
+ headers without persisting them.
18
+
19
+ Then:
20
+
21
+ - add `local_path`, `headers`, `access_type`, and `source_item_id` to
22
+ `FullTextLocation`;
23
+ - teach `ReferenceFetcher._materialize()` to read a local file under the same size
24
+ and format-sniffing rules as HTTP content;
25
+ - teach `ContentAcquirer` to accept per-location headers;
26
+ - add private provenance fields to `ReferenceContent` and cache round-tripping;
27
+ - ensure secrets and localhost URLs are not written to frontmatter.
28
+
29
+ ### 2. Add a Zotero API client and exact identifier index
30
+
31
+ Add contract tests using representative Zotero v3 JSON for:
32
+
33
+ - local API availability detection;
34
+ - paginated top-level item loading;
35
+ - DOI normalization;
36
+ - PMID/PMCID extraction from structured fields and `extra`;
37
+ - incremental index refresh metadata;
38
+ - duplicate and ambiguous identifiers;
39
+ - child attachment selection;
40
+ - indexed-full-text success and PDF fallback.
41
+
42
+ Then implement a small read-only client rather than coupling HTTP and matching
43
+ logic directly inside the provider. The default base URL is
44
+ `http://localhost:23119/api`; web mode remains disabled in this phase.
45
+
46
+ ### 3. Add `ZoteroFullTextProvider`
47
+
48
+ Add provider tests showing:
49
+
50
+ - no identifier returns `None`;
51
+ - an exact DOI/PMID hit returns Zotero indexed text when usable;
52
+ - short or absent indexed text returns the PDF file location;
53
+ - ambiguity returns no location and a diagnostic;
54
+ - connection errors remain retryable in the provider chain;
55
+ - provenance is `provider=zotero` and `access_type=user_library` with no OA claim.
56
+
57
+ Register `zotero` for the explicit `cache enrich` workflow. Ordinary validation
58
+ must reject its `user_library` locations even if it is accidentally included in
59
+ the configured provider chain.
60
+
61
+ ### 4. Add `cache enrich`
62
+
63
+ Add CLI tests first for dry-run, apply, misses, ambiguity, provider-unavailable,
64
+ and stable tabular output. Implement:
65
+
66
+ ```text
67
+ cache enrich --provider zotero --cache-dir PATH [--dry-run]
68
+ ```
69
+
70
+ The command enumerates Markdown cache files, loads them through `ReferenceFetcher`,
71
+ and runs only the selected private provider even if `full_text_attempted` is
72
+ already true. Apply mode reuses the normal materialization path but writes an
73
+ owner-only private research cache outside the project by default.
74
+
75
+ ### 5. Positive local smoke test
76
+
77
+ Run the dry-run command against a temporary cache entry whose DOI is known to
78
+ exist in the local Zotero library while Zotero is running. Verify:
79
+
80
+ - exact DOI match;
81
+ - at least 500 extracted characters;
82
+ - cache provenance and checksum;
83
+ - no credential or private URL leakage;
84
+ - the source Zotero library remains unchanged.
85
+
86
+ The checked-in test suite must not depend on the developer's personal library.
87
+
88
+ ## Phase 2: Zotero Web API
89
+
90
+ ### 6. Add authenticated read-only web mode
91
+
92
+ Test and implement user/group library configuration, pagination, `since`-based
93
+ index refresh, backoff/rate-limit headers, attachment download authentication,
94
+ and Zotero storage/WebDAV absence behavior.
95
+
96
+ Credentials come from `ZOTERO_API_KEY` or a credential callback. Add explicit
97
+ configuration for user/group library ID; never accept API keys in URLs.
98
+
99
+ ### 7. Operational documentation
100
+
101
+ Document how to enable the local API, create a read-only Zotero key, scan before
102
+ applying, protect private caches, and keep private content out of validation.
103
+
104
+ ## Phase 3: Paperpile local sync
105
+
106
+ ### 8. Build a generic local library index
107
+
108
+ Extract identifier normalization and match reporting from the Zotero client into
109
+ a source-neutral index. Add BibTeX ingestion with tests for DOI, PMID, title,
110
+ year, author, duplicate keys, and malformed entries. Add a cacheable PDF probe
111
+ that reads metadata/first-page text only once per file and stores its checksum.
112
+
113
+ ### 9. Add `PaperpileFullTextProvider` for local files
114
+
115
+ Test and implement configuration for:
116
+
117
+ - a local Paperpile/Google Drive PDF root;
118
+ - a Paperpile automatic BibTeX export file or HTTPS download link;
119
+ - an optional manual Paperpile JSON export.
120
+
121
+ Only exact DOI/PMID matches apply automatically. Title/year/author candidates are
122
+ reported by `cache enrich --dry-run` and require explicit user confirmation in a
123
+ future task.
124
+
125
+ ### 10. Measure matching quality
126
+
127
+ Run dry-run evaluation on a representative private cache and record precision,
128
+ coverage, ambiguity, and unmatched reasons separately. Do not weaken matching
129
+ thresholds to improve coverage. Use findings to decide whether guarded metadata
130
+ matching should ever be enabled for apply mode.
131
+
132
+ ## Phase 4: Paperpile via Google Drive
133
+
134
+ ### 11. Add a read-only Drive adapter
135
+
136
+ Test with Google Drive API fixtures, then implement OAuth read-only listing and
137
+ download, incremental change tracking, native Google shortcut handling, and
138
+ streamed size limits. Reuse the Paperpile BibTeX identity index from Phase 3.
139
+
140
+ This phase must not mutate Drive or Paperpile. Tokens use the system credential
141
+ store and are excluded from logs and config serialization.
142
+
143
+ ## Phase 5: Privacy hardening and signed attestations
144
+
145
+ ### 12. Separate private content from shareable cache metadata
146
+
147
+ Completed in the first implementation: private content uses an owner-only
148
+ research cache outside the project by default. Validation reads only the public
149
+ cache, and its ordinary provider chain rejects explicitly non-open locations.
150
+
151
+ ### 13. Specify and implement offline signed excerpt attestations
152
+
153
+ Define a canonical, versioned `excerpts.yaml` schema and bind each record to its
154
+ reference ID, claim hash, normalized excerpt hash, private source checksum,
155
+ location, result, generator version, timestamp, and key ID. A trusted offline
156
+ job reads the private cache and signs with Ed25519. Only the verification key is
157
+ checked in; agents and CI cannot write trusted attestations. Add tests for valid,
158
+ tampered, wrong-reference, wrong-claim, unknown-key, expired-key, and unsigned
159
+ artifacts before enabling this optional validation mode.
160
+
161
+ ## Phase 6: Browse a cache in Zotero
162
+
163
+ ### 14. Export cache metadata for Zotero import
164
+
165
+ Implemented `cache export --format csl-json` with deterministic DOI/PMID
166
+ deduplication and missing-full-text filtering. It exports an explicit
167
+ bibliographic metadata allowlist and always excludes cached text, excerpts,
168
+ provenance, PDFs, private-cache data, and local paths. The command refuses to
169
+ overwrite an output file without `--force`.
170
+
171
+ ### 15. Consider an opt-in Zotero collection sync
172
+
173
+ Only after export is stable, design an idempotent write integration that creates
174
+ or updates a dedicated Zotero collection through the documented write API. It
175
+ must require explicit write authorization and a dry-run diff; it must never
176
+ alter unrelated Zotero items or attach private files implicitly.
177
+
178
+ ## Definition of done for the minimal first task
179
+
180
+ - `cache enrich --provider zotero --dry-run` inventories an existing reference
181
+ cache without modifying it.
182
+ - Exact DOI/PMID matches identify PDF attachments in the supported Zotero local
183
+ API.
184
+ - Apply mode extracts usable text into the private research cache without changing the
185
+ public source cache.
186
+ - Normal validation ignores the private research cache and rejects private
187
+ provider results.
188
+ - Misses and ambiguities are visible and safe.
189
+ - No private API, direct SQLite dependency, secret persistence, or library write.
190
+ - Focused tests, doctests, mypy, Ruff, and the full `just test` suite pass.