linkml-reference-validator 0.2.1rc3__tar.gz → 0.3.0rc2__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 (229) hide show
  1. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/PKG-INFO +2 -2
  2. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/concepts/editorial-conventions.md +9 -4
  3. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/concepts/how-it-works.md +45 -0
  4. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/fetch-full-text-and-pdfs.md +43 -0
  5. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/repair-validation-errors.md +43 -1
  6. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/validate-clinical-trials.md +4 -0
  7. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/reference/cli.md +81 -24
  8. linkml_reference_validator-0.3.0rc2/docs/reference/splitting.md +69 -0
  9. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/setup-guide.md +4 -0
  10. linkml_reference_validator-0.3.0rc2/docs/troubleshooting.md +968 -0
  11. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/tutorials/complete-workflow.md +1 -1
  12. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/mkdocs.yml +1 -0
  13. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/cli/cache.py +38 -1
  14. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/cli/repair.py +66 -12
  15. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/cli/validate.py +53 -21
  16. linkml_reference_validator-0.3.0rc2/src/linkml_reference_validator/etl/__init__.py +12 -0
  17. linkml_reference_validator-0.3.0rc2/src/linkml_reference_validator/etl/extract/__init__.py +31 -0
  18. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/extract/base.py +11 -3
  19. linkml_reference_validator-0.3.0rc2/src/linkml_reference_validator/etl/extract/html.py +245 -0
  20. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/extract/pdf.py +9 -2
  21. linkml_reference_validator-0.3.0rc2/src/linkml_reference_validator/etl/extract/xml.py +264 -0
  22. linkml_reference_validator-0.3.0rc2/src/linkml_reference_validator/etl/fulltext/base.py +200 -0
  23. linkml_reference_validator-0.3.0rc2/src/linkml_reference_validator/etl/fulltext/openalex.py +106 -0
  24. linkml_reference_validator-0.3.0rc2/src/linkml_reference_validator/etl/fulltext/pmc.py +202 -0
  25. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/fulltext/unpaywall.py +25 -9
  26. linkml_reference_validator-0.3.0rc2/src/linkml_reference_validator/etl/reference_fetcher.py +1898 -0
  27. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/pmid.py +181 -55
  28. linkml_reference_validator-0.3.0rc2/src/linkml_reference_validator/matching.py +75 -0
  29. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/models.py +93 -19
  30. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/plugins/reference_validation_plugin.py +50 -1
  31. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/validation/repairer.py +83 -1
  32. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/validation/supporting_text_validator.py +191 -21
  33. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/GEO_GSE12345.md +1 -0
  34. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/PMC_TEST001.md +1 -0
  35. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/PMC_TEST002.md +2 -0
  36. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/PMID_TEST001.md +1 -0
  37. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/PMID_TEST002.md +1 -0
  38. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/PMID_TEST003.md +2 -0
  39. linkml_reference_validator-0.2.1rc3/tests/fixtures/CLINICALTRIALS_NCT00000001.md → linkml_reference_validator-0.3.0rc2/tests/fixtures/clinicaltrials_NCT00000001.md +1 -0
  40. linkml_reference_validator-0.3.0rc2/tests/fixtures/fulltext_html/README.md +22 -0
  41. linkml_reference_validator-0.3.0rc2/tests/fixtures/fulltext_html/dspace-32894695.html +1 -0
  42. linkml_reference_validator-0.3.0rc2/tests/fixtures/fulltext_html/iris-19049553.html +359 -0
  43. linkml_reference_validator-0.3.0rc2/tests/fixtures/fulltext_html/plos-0000308.html +78 -0
  44. linkml_reference_validator-0.3.0rc2/tests/fixtures/jats/PMC5593426.xml +5 -0
  45. linkml_reference_validator-0.3.0rc2/tests/fixtures/jats/README.md +21 -0
  46. linkml_reference_validator-0.3.0rc2/tests/test_absent_abstract_recovery.py +140 -0
  47. linkml_reference_validator-0.3.0rc2/tests/test_cache_author_recovery.py +102 -0
  48. linkml_reference_validator-0.3.0rc2/tests/test_cli_cache_reference.py +271 -0
  49. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_cli_repair.py +134 -0
  50. linkml_reference_validator-0.3.0rc2/tests/test_clinicaltrials_cache.py +234 -0
  51. linkml_reference_validator-0.3.0rc2/tests/test_empty_snippets.py +794 -0
  52. linkml_reference_validator-0.3.0rc2/tests/test_extractor_fidelity.py +665 -0
  53. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_extractors.py +13 -0
  54. linkml_reference_validator-0.3.0rc2/tests/test_frontmatter_and_doi_case.py +334 -0
  55. linkml_reference_validator-0.3.0rc2/tests/test_fulltext_providers.py +356 -0
  56. linkml_reference_validator-0.3.0rc2/tests/test_html_fulltext_acceptance.py +432 -0
  57. linkml_reference_validator-0.3.0rc2/tests/test_jats_tables.py +344 -0
  58. linkml_reference_validator-0.3.0rc2/tests/test_ligature_normalization.py +91 -0
  59. linkml_reference_validator-0.3.0rc2/tests/test_no_cache_downgrade.py +540 -0
  60. linkml_reference_validator-0.3.0rc2/tests/test_open_access_policy.py +686 -0
  61. linkml_reference_validator-0.3.0rc2/tests/test_pmc_modern_container.py +59 -0
  62. linkml_reference_validator-0.3.0rc2/tests/test_pmid_network.py +327 -0
  63. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_reference_fetcher.py +477 -2
  64. linkml_reference_validator-0.3.0rc2/tests/test_serve_stale_html_optin.py +75 -0
  65. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_sources.py +151 -0
  66. linkml_reference_validator-0.3.0rc2/tests/test_split_supporting_text.py +75 -0
  67. linkml_reference_validator-0.3.0rc2/tests/test_validation_counts.py +407 -0
  68. linkml_reference_validator-0.2.1rc3/docs/troubleshooting.md +0 -706
  69. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/etl/__init__.py +0 -9
  70. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/etl/extract/__init__.py +0 -16
  71. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/etl/extract/html.py +0 -46
  72. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/etl/extract/xml.py +0 -46
  73. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/etl/fulltext/base.py +0 -70
  74. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/etl/fulltext/openalex.py +0 -70
  75. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/etl/fulltext/pmc.py +0 -124
  76. linkml_reference_validator-0.2.1rc3/src/linkml_reference_validator/etl/reference_fetcher.py +0 -932
  77. linkml_reference_validator-0.2.1rc3/tests/test_fulltext_providers.py +0 -175
  78. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.claude/.mcp.json +0 -0
  79. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.claude/settings.json +0 -0
  80. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.copier-answers.yml +0 -0
  81. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.devcontainer/devcontainer.json +0 -0
  82. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.devcontainer/post_create.sh +0 -0
  83. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.editorconfig +0 -0
  84. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/actions/claude-code-action/action.yml +0 -0
  85. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/actions/claude-issue-summarize-action/action.yml +0 -0
  86. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/actions/claude-issue-triage-action/action.yml +0 -0
  87. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/ai-controllers.json +0 -0
  88. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/copilot-instructions.md +0 -0
  89. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/copilot-setup-steps.yml +0 -0
  90. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/dependabot.yml +0 -0
  91. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/workflows/claude-code-review.yml +0 -0
  92. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/workflows/claude-issue-summarize.yml +0 -0
  93. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/workflows/claude-issue-triage.yml +0 -0
  94. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/workflows/claude-ontology-review.yml +0 -0
  95. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/workflows/claude.yml +0 -0
  96. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/workflows/deploy-docs.yaml +0 -0
  97. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/workflows/dragon-ai.yml +0 -0
  98. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/workflows/main.yaml +0 -0
  99. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.github/workflows/pypi-publish.yaml +0 -0
  100. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.gitignore +0 -0
  101. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.goosehints +0 -0
  102. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.pre-commit-config.yaml +0 -0
  103. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/.yamllint.yaml +0 -0
  104. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/AGENTS.md +0 -0
  105. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/BUG_GEO_ACCESSION_TO_UID.md +0 -0
  106. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/BUG_ZENODO_DOI_AND_URL_HANDLING.md +0 -0
  107. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/CLAUDE.md +0 -0
  108. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/CLI-ISSUES.md +0 -0
  109. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/CODE_OF_CONDUCT.md +0 -0
  110. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/CONTRIBUTING.md +0 -0
  111. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/FEATURE_REQUEST_REPAIR.md +0 -0
  112. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/LICENSE +0 -0
  113. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/README.md +0 -0
  114. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/REVIEW2.md +0 -0
  115. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/REVIEW3.md +0 -0
  116. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/ai.just +0 -0
  117. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/copilot-instructions.md +0 -0
  118. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/about.md +0 -0
  119. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/assets/zotero-find-full-text.png +0 -0
  120. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/concepts/content-types.md +0 -0
  121. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/elements/.gitkeep +0 -0
  122. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/add-reference-source.md +0 -0
  123. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/export-cache-to-zotero.md +0 -0
  124. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/skip-unsupported-references.md +0 -0
  125. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/use-local-files-and-urls.md +0 -0
  126. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/validate-dois.md +0 -0
  127. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/validate-entrez.md +0 -0
  128. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/validate-obo-files.md +0 -0
  129. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/validate-titles.md +0 -0
  130. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/how-to/validate-urls.md +0 -0
  131. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/index.md +0 -0
  132. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/notebooks/01_getting_started.ipynb +0 -0
  133. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/notebooks/02_advanced_usage.ipynb +0 -0
  134. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/notebooks/03_python_api.ipynb +0 -0
  135. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/notebooks/04_obo_validation.ipynb +0 -0
  136. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/notebooks/05_geo_validation.ipynb +0 -0
  137. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/quickstart.md +0 -0
  138. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/superpowers/plans/2026-06-12-modular-pdf-fulltext-fetching.md +0 -0
  139. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/superpowers/plans/2026-08-05-private-library-full-text.md +0 -0
  140. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/superpowers/specs/2026-06-12-modular-pdf-fulltext-fetching-design.md +0 -0
  141. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/superpowers/specs/2026-08-05-private-library-full-text-design.md +0 -0
  142. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/templates-linkml/README.md +0 -0
  143. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/docs/todo.md +0 -0
  144. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/examples/README.md +0 -0
  145. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/justfile +0 -0
  146. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/mypy.ini +0 -0
  147. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/notebooks/01_getting_started.ipynb +0 -0
  148. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/notebooks/02_advanced_usage.ipynb +0 -0
  149. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/notebooks/03_python_api.ipynb +0 -0
  150. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/notebooks/README.md +0 -0
  151. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/project/README.md +0 -0
  152. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/project.justfile +0 -0
  153. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/pyproject.toml +0 -0
  154. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/pytest.ini +0 -0
  155. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/__init__.py +0 -0
  156. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/_version.py +0 -0
  157. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/cli/__init__.py +0 -0
  158. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/cli/lookup.py +0 -0
  159. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/cli/shared.py +0 -0
  160. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/acquire.py +0 -0
  161. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/csl_export.py +0 -0
  162. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/fulltext/__init__.py +0 -0
  163. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/fulltext/epmc_preprint.py +0 -0
  164. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/fulltext/json_api.py +0 -0
  165. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/fulltext/loader.py +0 -0
  166. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/fulltext/zotero.py +0 -0
  167. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/identifiers.py +0 -0
  168. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/__init__.py +0 -0
  169. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/base.py +0 -0
  170. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/clinicaltrials.py +0 -0
  171. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/doi.py +0 -0
  172. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/entrez.py +0 -0
  173. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/file.py +0 -0
  174. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/json_api.py +0 -0
  175. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/loader.py +0 -0
  176. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/ppr.py +0 -0
  177. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/url.py +0 -0
  178. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/sources/utils.py +0 -0
  179. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/etl/text_extractor.py +0 -0
  180. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/field_detection.py +0 -0
  181. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/plugins/REVIEW.md +0 -0
  182. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/plugins/__init__.py +0 -0
  183. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/validation/__init__.py +0 -0
  184. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/src/linkml_reference_validator/validation/fuzzy_text_utils.py +0 -0
  185. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/__init__.py +0 -0
  186. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/conftest.py +0 -0
  187. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/data/test_data_deep_nesting_invalid.yaml +0 -0
  188. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/data/test_data_deep_nesting_valid.yaml +0 -0
  189. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/data/test_data_invalid.yaml +0 -0
  190. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/data/test_data_valid.yaml +0 -0
  191. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/data/test_schema.yaml +0 -0
  192. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/data/test_schema_deep_nesting.yaml +0 -0
  193. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/PMC_TEST001.txt +0 -0
  194. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/PMC_TEST002.txt +0 -0
  195. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/PMID_TEST001.txt +0 -0
  196. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/PMID_TEST002.txt +0 -0
  197. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/PMID_TEST003.txt +0 -0
  198. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/fixtures/sample.obo +0 -0
  199. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_acquire.py +0 -0
  200. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_cli.py +0 -0
  201. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_cli_cache_enrich.py +0 -0
  202. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_cli_cache_export.py +0 -0
  203. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_cli_lookup.py +0 -0
  204. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_clinicaltrials_integration.py +0 -0
  205. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_deep_nesting.py +0 -0
  206. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_doi_preprint.py +0 -0
  207. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_e2e_integration.py +0 -0
  208. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_epmc_preprint_provider.py +0 -0
  209. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_field_detection.py +0 -0
  210. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_fulltext_loader.py +0 -0
  211. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_fuzzy_suggestions.py +0 -0
  212. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_geo_integration.py +0 -0
  213. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_identifiers.py +0 -0
  214. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_json_api_source.py +0 -0
  215. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_models.py +0 -0
  216. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_optional_linkml_dependency.py +0 -0
  217. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_plugin_integration.py +0 -0
  218. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_pmc_fulltext.py +0 -0
  219. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_ppr_source.py +0 -0
  220. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_real_validation.py +0 -0
  221. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_repair.py +0 -0
  222. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_source_utils.py +0 -0
  223. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_supporting_text_validator.py +0 -0
  224. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_text_extractor.py +0 -0
  225. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_title_validation.py +0 -0
  226. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_unknown_slot_traversal.py +0 -0
  227. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_validation_config.py +0 -0
  228. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/tests/test_zotero_provider.py +0 -0
  229. {linkml_reference_validator-0.2.1rc3 → linkml_reference_validator-0.3.0rc2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: linkml_reference_validator
3
- Version: 0.2.1rc3
3
+ Version: 0.3.0rc2
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
@@ -248,7 +248,7 @@ Editorial conventions work the same in data files:
248
248
 
249
249
  ### Normalization Order
250
250
 
251
- 1. Remove everything inside `[...]`
251
+ 1. Remove editorial `[...]` content unless a configured literal pattern matches
252
252
  2. Split on `...` if present
253
253
  3. For each part:
254
254
  - Lowercase
@@ -258,10 +258,15 @@ Editorial conventions work the same in data files:
258
258
 
259
259
  ### Regular Expressions
260
260
 
261
- Brackets: `\[([^\]]*)\]` (removes content and brackets)
262
- Ellipsis: `\.\.\.` (splits on three dots exactly)
261
+ Brackets: `\[.*?\]` (single-line, non-nested matching; literal patterns can preserve matches)
263
262
 
264
- Note: `..` (two dots) and `....` (four dots) are NOT treated as ellipsis.
263
+ Ellipsis: `\s*\.{2,}\s*` (splits on two or more ASCII periods)
264
+
265
+ Both `..` (two dots) and `....` (four dots) are treated as separators. The Unicode
266
+ ellipsis `…` is not a separator.
267
+
268
+ Python callers can use the public [supporting text splitting helper](../reference/splitting.md)
269
+ without constructing a validator.
265
270
 
266
271
  ## See Also
267
272
 
@@ -36,6 +36,51 @@ reference_content = "...The MUC1 oncoprotein blocks nuclear..."
36
36
  # Match: "muc1 oncoprotein" found in normalized reference
37
37
  ```
38
38
 
39
+ **Empty excerpts are rejected:**
40
+
41
+ An empty or whitespace-only excerpt is always an error, never a pass. The
42
+ empty string is a substring of every document, so a blank excerpt would
43
+ otherwise be reported as verified evidence while quoting nothing at all:
44
+
45
+ ```python
46
+ supporting_text = "" # or " ", "\n"
47
+ # ERROR: Supporting text is empty
48
+ ```
49
+
50
+ The check runs before the reference is fetched, and applies even when the
51
+ reference prefix is on the [skip list](../how-to/skip-unsupported-references.md):
52
+ a blank excerpt is a defect in the data regardless of what it cites. An
53
+ excerpt slot that is *absent* is a different matter — whether it is required
54
+ is up to your schema.
55
+
56
+ **Minimum excerpt length (optional):**
57
+
58
+ A blank excerpt is the extreme case. A two-character one is barely better: a
59
+ short string matches almost any paper by chance, so the match succeeds without
60
+ the excerpt being real evidence. Set `min_excerpt_length` to require a minimum:
61
+
62
+ ```yaml
63
+ # .linkml-reference-validator.yaml
64
+ validation:
65
+ min_excerpt_length: 20
66
+ ```
67
+
68
+ Keep it under `validation:`. A config file that has a `validation:` section
69
+ ignores validation keys placed at the top level — silently, which for an
70
+ opt-in check means it looks enabled while never running.
71
+
72
+ The check is off by default (`0`), so existing data keeps validating as before.
73
+ Length is measured in **non-whitespace characters of quoted text**:
74
+
75
+ - Whitespace is ignored, so PDF-to-text extraction damage does not change the
76
+ count — `"t he pro tein"` and `"the protein"` both measure 10.
77
+ - No tokenizing, so chemical nomenclature and other punctuation-dense strings
78
+ are counted in full — `"2,3-dihydroxybenzoate"` measures 21, not 2 words.
79
+ - Editorial `[brackets]` and `...` separators do not count, since they are
80
+ never matched against the reference.
81
+
82
+ Like the blank check, this runs before the reference is fetched.
83
+
39
84
  ### 3. Ellipsis Handling
40
85
 
41
86
  When supporting text contains `...`, each part is matched separately:
@@ -285,3 +285,46 @@ downloaded PDF lives under the cache directory and can be located at
285
285
  - [Validating DOIs](validate-dois.md) — DOI metadata sources and supplementary files
286
286
  - [Validating Entrez Accessions](validate-entrez.md) — PubMed/PMC references
287
287
  - [CLI Reference](../reference/cli.md) — complete command documentation
288
+
289
+ ## HTML article checks and cache recovery
290
+
291
+ An OpenAlex or Unpaywall URL may lead to a repository item record containing
292
+ only an abstract and metadata. HTML full-text acquisition now requires an
293
+ identifiable research body: publisher body markup or narrative research sections
294
+ with paragraphs, after removing abstracts and navigation. Length and additional
295
+ metadata text alone do not establish full text. This also applies when a URL
296
+ advertised as a PDF actually returns HTML. Genuine HTML research bodies remain
297
+ eligible, including short bodies above the existing 500-character minimum.
298
+
299
+ Ambiguous layouts are rejected and the next configured provider is tried. The
300
+ fetcher does not crawl repository download links. If every provider completes
301
+ without usable text or an error, the reference retains its original abstract
302
+ and records `full_text_attempted: true`.
303
+
304
+ New `full_text_html` cache entries carry `html_full_text_version: 1`. Older
305
+ Markdown HTML full-text entries without this stamp are refreshed on their next
306
+ validation read. This migration covers all providers because cached plain text
307
+ cannot establish which HTML body structure was present. It does not invalidate
308
+ current PDF, XML, or abstract caches.
309
+
310
+ If refreshing an old HTML entry fails, validation does **not** fall back to that
311
+ entry: its text may be repository metadata rather than article evidence. The
312
+ file remains untouched for inventory/export, and the next validation run retries
313
+ recovery. Once the source is available, normal fetching replaces it with a
314
+ verified article or the available metadata/abstract. Other content types retain
315
+ the existing stale-cache fallback. Legacy `.txt` caches remain a separate,
316
+ read-only compatibility path and are not migrated by this HTML stamp.
317
+
318
+ Research headings establish body presence; they do not filter the accepted
319
+ article's other sections. For example, `Main`, `Case presentation`, and
320
+ `Limitations` remain in an accepted article. A separately marked body is also
321
+ preserved when an abstract heading uses a higher heading level. Without that
322
+ structural boundary, an unmarked structured abstract remains ambiguous and is
323
+ rejected conservatively.
324
+
325
+ A provider's `FullTextLocation.text` (including a custom API `text_field`) is a
326
+ trusted **already-extracted article body** contract. Configure it only for APIs
327
+ that return article text, not arbitrary page text or metadata. Plain extracted
328
+ text cannot be structurally rechecked; raw HTML supplied with an HTML hint still
329
+ passes the body classifier. The acceptance stamp records that either the HTML
330
+ check or this trusted provider contract was used.
@@ -13,6 +13,21 @@ After validating your data files, you may find validation errors due to:
13
13
 
14
14
  The `repair` command attempts to fix these issues automatically or flags them for manual review.
15
15
 
16
+ ### Typographic ligatures already match
17
+
18
+ Validation folds Latin compatibility ligatures on both the reference content and
19
+ the quote, including each part separated by `...`. For example, PDF-extracted
20
+ `amyloid fibrils` matches `amyloid fibrils` without a repair. The supported folds
21
+ are `ff → ff`, `fi → fi`, `fl → fl`, `ffi → ffi`, `ffl → ffl`, `ſt/st → st`, and
22
+ `IJ/ij → IJ/ij`. IJ is included because Unicode gives it a compatibility
23
+ decomposition into I and J; ordinary case normalization still applies.
24
+
25
+ The distinct letters `Æ/æ` and `Œ/œ` remain distinct from `AE/ae` and `OE/oe`.
26
+ Equating these letters would accept spelling changes beyond typographic repair.
27
+ Validation uses an explicit ligature table instead of broad Unicode NFKC
28
+ normalization so this change does not equate scientific notation such as `H₂O`
29
+ with `H2O`, `10⁶` with `106`, or the micro sign `µ` with Greek `μ`.
30
+
16
31
  ## Quick Start
17
32
 
18
33
  ### Repair a Single Quote
@@ -112,7 +127,7 @@ Flags text that appears fabricated or hallucinated:
112
127
 
113
128
  ```
114
129
  RECOMMENDED REMOVALS:
115
- PMID:34567890 at evidence[2]:
130
+ PMID:34567890 at evidence[2].supporting_text:
116
131
  Similarity: 8%
117
132
  Snippet: 'This completely made up text...'
118
133
  ```
@@ -290,6 +305,33 @@ The supporting text wasn't found in the reference. Possible causes:
290
305
  - Wrong PMID
291
306
  - AI-generated/hallucinated quote
292
307
 
308
+ ### "Supporting text is empty"
309
+
310
+ The excerpt is empty or whitespace-only, so there is nothing to match against
311
+ the reference. This is usually a bug in whatever generated the data (for
312
+ example, a string slice with a backwards range yielding `""`).
313
+
314
+ Empty excerpts cannot be repaired automatically — there is no text to correct,
315
+ and fuzzy-matching nothing against the reference would invent a quote. They
316
+ appear under **INSUFFICIENT EXCERPTS (nothing to verify)** in the report, so
317
+ you can supply the real quote or drop the evidence item.
318
+
319
+ They are deliberately *not* counted as removals. A removal recommendation means
320
+ "this quote does not match the reference", a verdict reached by comparing the
321
+ two; here no comparison happened. So `removal_count` excludes them and the
322
+ action type is `INSUFFICIENT_EXCERPT`, not `REMOVAL` — worth knowing if you
323
+ script against the report. They do still make `repair data` exit non-zero.
324
+
325
+ ### "Supporting text is too short"
326
+
327
+ The excerpt is below the `min_excerpt_length` you configured (off by default,
328
+ and set under `validation:`; see
329
+ [How It Works](../concepts/how-it-works.md)). A very short excerpt matches
330
+ almost any paper by chance, so it is weak evidence even when the match succeeds.
331
+
332
+ Like empty excerpts, these report under **INSUFFICIENT EXCERPTS** rather than
333
+ being repaired: there is no way to guess which longer passage the curator meant.
334
+
293
335
  ### "Reference content not available"
294
336
 
295
337
  The reference exists but has no retrievable content:
@@ -40,6 +40,10 @@ clinicaltrials:NCT00000001
40
40
  CLINICALTRIALS:NCT00000001
41
41
  ```
42
42
 
43
+ These forms normalize to `clinicaltrials:NCT00000001` and share the cache file
44
+ `clinicaltrials_NCT00000001.md`. Existing files with that canonical name are
45
+ reused without renaming or fetching the trial again.
46
+
43
47
  ## Prefix Aliases and Normalization
44
48
 
45
49
  If your data uses alternate prefix styles (e.g., the legacy `NCT:` prefix), configure normalization in `.linkml-reference-validator.yaml`:
@@ -378,29 +378,57 @@ linkml-reference-validator validate data \
378
378
 
379
379
  ### Output Format
380
380
 
381
- **Success:**
382
- ```
383
- Validating data.yaml against schema schema.yaml
384
- Cache directory: references_cache
385
-
386
- Validation Summary:
387
- Total checks: 3
388
- All validations passed!
389
- ```
390
-
391
- **Failure:**
392
- ```
393
- Validating data.yaml against schema schema.yaml
394
- Cache directory: references_cache
395
-
396
- Validation Issues (2):
397
- [ERROR] Text part not found as substring: 'MUC1 activates JAK-STAT'
398
- Location: Statement
381
+ The summary separates work performed from issues found:
399
382
 
383
+ ```text
400
384
  Validation Summary:
401
- Total checks: 3
402
- Issues found: 2
403
- ```
385
+ Input files: 2
386
+ Files validated: 2
387
+ Snippets checked: 3
388
+ Snippets skipped: 0
389
+ Snippets unavailable: 0
390
+ Titles checked: 1
391
+ Issues found: 1
392
+ Issues found in 1 file(s) (1 validation issue(s))
393
+ Failing files:
394
+ bad.yaml
395
+ ```
396
+
397
+ - **Input files** counts supplied paths, including files that cannot be read.
398
+ - **Files validated** counts files with at least one mapping submitted to the
399
+ validator. An empty mapping counts; an empty list has no instances and does not.
400
+ A mixed list containing valid mappings and malformed entries counts once and
401
+ still fails. Entry diagnostics use zero-based indexes.
402
+ - **Snippets checked** counts executed comparisons against reference content,
403
+ including matches and mismatches. Multiple excerpt slots each count separately.
404
+ - **Snippets skipped** counts snippet/reference pairs bypassed by `skip_prefixes`.
405
+ - **Snippets unavailable** counts attempted snippet validations where the reference
406
+ could not be fetched or had no content. These are not completed comparisons.
407
+ - **Titles checked** counts actual title comparisons, including those performed
408
+ alongside snippets. Missing titles and skipped/unavailable references do not count.
409
+ - **Issues found** counts emitted validation issues. Blank excerpts may produce an
410
+ issue without any comparison; file read/parse or data-format errors appear in the
411
+ failing-file list without adding a fabricated validation issue.
412
+
413
+ Absent excerpts and excerpts without a usable reference perform no comparison.
414
+ Counters come from execution and reset for each LinkML validation call; the CLI
415
+ accumulates all instances and files. The public plugin exposes `snippets_checked`,
416
+ `snippets_skipped`, `snippets_unavailable`, and `titles_checked` after validation.
417
+ When using lazy `process()` or `iter_results()` APIs, exhaust the result iterator
418
+ before reading final counters. Unconsumed or partially consumed iterators do not
419
+ provide final counts; `Validator.validate()` consumes the iterator for you.
420
+
421
+ Title-only skips and unavailable titles have no separate summary counters: they
422
+ leave all check counters at zero. A skipped title emits no issue; an unavailable
423
+ standalone title emits an issue. Existing title behavior is preserved: a reference
424
+ missing its title is an issue for standalone title validation, but a title supplied
425
+ alongside an excerpt is not compared or reported when the reference has no title.
426
+
427
+ A zero-snippet run prints `No snippet comparisons were performed.` This includes
428
+ empty input collections and title-only runs. Exit behavior is unchanged: no issues
429
+ or file errors means exit 0, even when zero snippets were checked; issues or file
430
+ errors mean exit 1. Failed file paths are listed at the end, including unreadable
431
+ files, while later files continue to be processed.
404
432
 
405
433
  ---
406
434
 
@@ -585,16 +613,16 @@ Repair Report
585
613
  ============================================================
586
614
 
587
615
  HIGH CONFIDENCE FIXES (auto-applicable):
588
- PMID:12345678 at evidence[0]:
616
+ PMID:12345678 at evidence[0].supporting_text:
589
617
  Character normalization fix
590
618
  'CO2 levels...' → 'CO₂ levels...'
591
619
 
592
620
  SUGGESTED FIXES (review recommended):
593
- PMID:23456789 at evidence[1]:
621
+ PMID:23456789 at evidence[1].supporting_text:
594
622
  Inserted ellipsis between non-contiguous parts
595
623
 
596
624
  RECOMMENDED REMOVALS (low confidence):
597
- PMID:34567890 at evidence[2]:
625
+ PMID:34567890 at evidence[2].supporting_text:
598
626
  Similarity: 8%
599
627
  Snippet: 'Fabricated text...'
600
628
 
@@ -622,6 +650,10 @@ validation:
622
650
  geo: GEO
623
651
  NCBIGeo: GEO
624
652
 
653
+ # Minimum non-whitespace characters an excerpt must quote.
654
+ # 0 disables the check; empty excerpts are rejected regardless.
655
+ min_excerpt_length: 0
656
+
625
657
  repair:
626
658
  # Confidence thresholds
627
659
  auto_fix_threshold: 0.95
@@ -719,6 +751,31 @@ Successfully cached PMID:16888623
719
751
  Content length: 1523 characters
720
752
  ```
721
753
 
754
+ ### Exit Codes
755
+
756
+ - `0` - The public validation cache holds a current entry for the reference
757
+ - `1` - It does not
758
+
759
+ Exit `0` does not imply a download: a reference the current extractor has
760
+ already cached may be reported as cached without contacting the source.
761
+
762
+ Two cases satisfy the promise loosely. Content marked as non-open full text is
763
+ written to the private cache, which validation deliberately never reads, so that
764
+ run exits `0` without leaving anything in the public cache. Legacy `.txt` entries
765
+ are exempt from the extractor version check and so always report as current.
766
+
767
+ Conversely, a reference that could not be re-fetched is a failure even when a
768
+ cache entry for it exists. Validation falls back to an entry written by an older
769
+ extractor rather than reporting the reference as missing, but this command exists
770
+ to populate the cache, so leaving it without a current entry is reported as
771
+ failure and a script gating on the exit status does not go green through an
772
+ outage. The same applies when no source handles the identifier at all.
773
+
774
+ ```
775
+ Fetching PMID:16888623...
776
+ Failed to cache PMID:16888623: it could not be re-fetched, so an out-of-date cache entry was served. The cache still holds no current entry for it.
777
+ ```
778
+
722
779
  ---
723
780
 
724
781
  ## cache lookup
@@ -0,0 +1,69 @@
1
+ # Splitting supporting text in Python
2
+
3
+ The public import is:
4
+
5
+ ```python
6
+ from linkml_reference_validator.matching import split_supporting_text
7
+
8
+ parts = split_supporting_text("protein [important] functions ... in cells")
9
+ assert parts == ["protein functions", "in cells"]
10
+ ```
11
+
12
+ This pure helper needs no validator, fetcher, cache, or network access. The
13
+ validator's existing `_split_query` method delegates to it, so offline snippet
14
+ reports and validation share the same splitting implementation.
15
+
16
+ ## Contract
17
+
18
+ `split_supporting_text(text, literal_bracket_patterns=()) -> list[str]`
19
+
20
+ - Replace editorial bracketed text with a space.
21
+ - Split on runs of **two or more ASCII periods**, including `..`, `...`, and
22
+ `....`. A single period and the Unicode ellipsis `…` remain literal.
23
+ - Collapse whitespace within each part, trim it, and discard empty parts.
24
+ - Return parts in input order; empty text or only editorial notes/separators
25
+ produces `[]`.
26
+
27
+ The helper performs splitting only; it does not normalize case or punctuation,
28
+ check a reference, or enforce validation policies such as minimum excerpt length.
29
+ An empty list contains no quoted evidence and should not be treated as a verified
30
+ snippet.
31
+
32
+ Bracket parsing retains the existing non-nested, single-line regex behavior:
33
+ `[` matches through the next `]` on the same line. Unmatched brackets remain.
34
+ Periods inside preserved brackets still act as separators.
35
+
36
+ ## Literal scientific notation and configuration
37
+
38
+ The default is an empty pattern sequence, matching
39
+ `ReferenceValidationConfig.literal_bracket_patterns`. **No scientific notation is
40
+ preserved automatically.** Each supplied regex is searched against the content
41
+ inside brackets, without the brackets themselves. Any match preserves the whole
42
+ bracketed text; otherwise it is removed. Supplied patterns are the complete set
43
+ of exceptions, with no implicit patterns added.
44
+
45
+ ```python
46
+ from linkml_reference_validator.models import ReferenceValidationConfig
47
+
48
+ config = ReferenceValidationConfig(literal_bracket_patterns=[r"\d", r"[()+]"])
49
+ parts = split_supporting_text(
50
+ "binds [2Fe-2S] [important] ... exports [poly(A)+] RNA",
51
+ literal_bracket_patterns=config.literal_bracket_patterns,
52
+ )
53
+ assert parts == ["binds [2Fe-2S]", "exports [poly(A)+] RNA"]
54
+ ```
55
+
56
+ For repeated calls, pass a sequence of compiled string regexes to reuse them.
57
+ Compiled flags are preserved; invalid regex strings raise `re.error`.
58
+
59
+ ```python
60
+ import re
61
+
62
+ patterns = tuple(re.compile(p) for p in config.literal_bracket_patterns)
63
+ assert split_supporting_text("[2Fe-2S] ... [poly(A)+]", patterns) == [
64
+ "[2Fe-2S]", "[poly(A)+]"
65
+ ]
66
+ ```
67
+
68
+ See [Editorial Conventions](../concepts/editorial-conventions.md) for authoring
69
+ examples.
@@ -385,6 +385,10 @@ validation:
385
385
  # Cache directory (relative to config file or absolute)
386
386
  cache_dir: ./references_cache
387
387
 
388
+ # Reject excerpts quoting fewer than this many non-whitespace
389
+ # characters (0 disables; empty excerpts are always rejected)
390
+ min_excerpt_length: 0
391
+
388
392
  # Custom reference prefix mappings
389
393
  reference_prefix_map:
390
394
  geo: GEO