proofpath 0.3.0__tar.gz → 0.4.0__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 (212) hide show
  1. proofpath-0.4.0/.env.example +9 -0
  2. {proofpath-0.3.0 → proofpath-0.4.0}/CHANGELOG.md +47 -0
  3. {proofpath-0.3.0 → proofpath-0.4.0}/PKG-INFO +45 -3
  4. {proofpath-0.3.0 → proofpath-0.4.0}/README.md +44 -2
  5. proofpath-0.4.0/docs/eval/2026-09-16-averitec.md +95 -0
  6. {proofpath-0.3.0 → proofpath-0.4.0}/docs/superpowers/OPEN-ITEMS.md +44 -0
  7. {proofpath-0.3.0 → proofpath-0.4.0}/docs/superpowers/plans/2026-09-10-proofpath-implementation-plan.md +11 -0
  8. {proofpath-0.3.0 → proofpath-0.4.0}/docs/superpowers/specs/2026-09-10-proofpath-design.md +19 -1
  9. proofpath-0.4.0/scripts/eval_averitec.py +385 -0
  10. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/__init__.py +1 -1
  11. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/claims.py +106 -2
  12. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/cli.py +26 -7
  13. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/document.py +10 -1
  14. proofpath-0.4.0/src/proofpath/eval/averitec.py +148 -0
  15. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/ingest.py +136 -1
  16. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/judge.py +22 -68
  17. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/polite.py +70 -4
  18. proofpath-0.4.0/src/proofpath/providers/__init__.py +318 -0
  19. proofpath-0.4.0/src/proofpath/providers/academic.py +117 -0
  20. proofpath-0.4.0/src/proofpath/providers/social.py +915 -0
  21. proofpath-0.4.0/src/proofpath/providers/web.py +120 -0
  22. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/report.py +82 -0
  23. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/resolve.py +18 -1
  24. proofpath-0.4.0/src/proofpath/secrets.py +115 -0
  25. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/ui.py +37 -2
  26. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/verify.py +426 -247
  27. proofpath-0.4.0/tests/data/verify-report-golden.json +630 -0
  28. proofpath-0.4.0/tests/fixtures/social/bsky-blocked.json +11 -0
  29. proofpath-0.4.0/tests/fixtures/social/bsky-not-found.json +4 -0
  30. proofpath-0.4.0/tests/fixtures/social/bsky-quote-blocked.json +28 -0
  31. proofpath-0.4.0/tests/fixtures/social/bsky-resolve-handle.json +3 -0
  32. proofpath-0.4.0/tests/fixtures/social/bsky-thread-gone.json +7 -0
  33. proofpath-0.4.0/tests/fixtures/social/bsky-thread.json +70 -0
  34. proofpath-0.4.0/tests/fixtures/social/hn-comment.json +8 -0
  35. proofpath-0.4.0/tests/fixtures/social/hn-dead.json +9 -0
  36. proofpath-0.4.0/tests/fixtures/social/hn-deleted.json +6 -0
  37. proofpath-0.4.0/tests/fixtures/social/hn-missing.json +1 -0
  38. proofpath-0.4.0/tests/fixtures/social/hn-story.json +11 -0
  39. proofpath-0.4.0/tests/fixtures/social/mastodon-login-required.json +1 -0
  40. proofpath-0.4.0/tests/fixtures/social/mastodon-status.json +12 -0
  41. proofpath-0.4.0/tests/fixtures/social/reddit-comment.json +38 -0
  42. proofpath-0.4.0/tests/fixtures/social/reddit-link-post.json +26 -0
  43. proofpath-0.4.0/tests/fixtures/social/reddit-nested-comment.json +55 -0
  44. proofpath-0.4.0/tests/fixtures/social/reddit-post.json +26 -0
  45. proofpath-0.4.0/tests/fixtures/social/reddit-removed.json +27 -0
  46. proofpath-0.4.0/tests/fixtures/social/reddit-token.json +6 -0
  47. proofpath-0.4.0/tests/test_averitec.py +221 -0
  48. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_claims.py +122 -1
  49. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_document.py +7 -2
  50. proofpath-0.4.0/tests/test_eval_averitec.py +276 -0
  51. proofpath-0.4.0/tests/test_providers.py +549 -0
  52. proofpath-0.4.0/tests/test_providers_social.py +1281 -0
  53. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_report.py +113 -0
  54. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_tui_app.py +46 -10
  55. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_tui_runs.py +28 -7
  56. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_ui.py +43 -0
  57. proofpath-0.4.0/tests/test_verify_golden.py +239 -0
  58. proofpath-0.4.0/tests/test_verify_routing.py +255 -0
  59. proofpath-0.3.0/.env.example +0 -4
  60. {proofpath-0.3.0 → proofpath-0.4.0}/.editorconfig +0 -0
  61. {proofpath-0.3.0 → proofpath-0.4.0}/.gitattributes +0 -0
  62. {proofpath-0.3.0 → proofpath-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  63. {proofpath-0.3.0 → proofpath-0.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  64. {proofpath-0.3.0 → proofpath-0.4.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  65. {proofpath-0.3.0 → proofpath-0.4.0}/.github/ISSUE_TEMPLATE/wrong_verdict.yml +0 -0
  66. {proofpath-0.3.0 → proofpath-0.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  67. {proofpath-0.3.0 → proofpath-0.4.0}/.github/workflows/ci.yml +0 -0
  68. {proofpath-0.3.0 → proofpath-0.4.0}/.github/workflows/release.yml +0 -0
  69. {proofpath-0.3.0 → proofpath-0.4.0}/.gitignore +0 -0
  70. {proofpath-0.3.0 → proofpath-0.4.0}/CLAUDE.md +0 -0
  71. {proofpath-0.3.0 → proofpath-0.4.0}/CONTRIBUTING.md +0 -0
  72. {proofpath-0.3.0 → proofpath-0.4.0}/LICENSE +0 -0
  73. {proofpath-0.3.0 → proofpath-0.4.0}/SECURITY.md +0 -0
  74. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-11-coverage.md +0 -0
  75. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-11-ghosts.md +0 -0
  76. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-11-pairing.md +0 -0
  77. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-11-scifact-dev.md +0 -0
  78. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-12-ghosts.md +0 -0
  79. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-12-pairing-author-year.md +0 -0
  80. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-12-scifact-dev.md +0 -0
  81. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-12-tiers.md +0 -0
  82. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-12-v0.1-live.md +0 -0
  83. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-15-judge-live.md +0 -0
  84. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-15-tui-v2-live.md +0 -0
  85. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/2026-09-15-v0.2-live.md +0 -0
  86. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/tui-v2-plain.svg +0 -0
  87. {proofpath-0.3.0 → proofpath-0.4.0}/docs/eval/tui-v2-rich.svg +0 -0
  88. {proofpath-0.3.0 → proofpath-0.4.0}/docs/research/2026-09-11-free-llm-api-tiers.md +0 -0
  89. {proofpath-0.3.0 → proofpath-0.4.0}/docs/superpowers/plans/2026-09-12-phases-9-10-plan.md +0 -0
  90. {proofpath-0.3.0 → proofpath-0.4.0}/docs/superpowers/specs/2026-09-15-tui-v2-design.md +0 -0
  91. {proofpath-0.3.0 → proofpath-0.4.0}/pyproject.toml +0 -0
  92. {proofpath-0.3.0 → proofpath-0.4.0}/scripts/eval_coverage.py +0 -0
  93. {proofpath-0.3.0 → proofpath-0.4.0}/scripts/eval_ghosts.py +0 -0
  94. {proofpath-0.3.0 → proofpath-0.4.0}/scripts/eval_pairing.py +0 -0
  95. {proofpath-0.3.0 → proofpath-0.4.0}/scripts/eval_scifact.py +0 -0
  96. {proofpath-0.3.0 → proofpath-0.4.0}/scripts/zero_network_check.py +0 -0
  97. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/browser.py +0 -0
  98. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/cache.py +0 -0
  99. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/commands.py +0 -0
  100. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/config.py +0 -0
  101. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/device.py +0 -0
  102. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/entailment.py +0 -0
  103. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/eval/__init__.py +0 -0
  104. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/eval/metrics.py +0 -0
  105. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/eval/scifact.py +0 -0
  106. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/events.py +0 -0
  107. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/fetch.py +0 -0
  108. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/models.py +0 -0
  109. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/numerics.py +0 -0
  110. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/oa.py +0 -0
  111. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/paths.py +0 -0
  112. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/pipeline.py +0 -0
  113. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/prompts/__init__.py +0 -0
  114. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/prompts/review.md +0 -0
  115. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/prompts/summarize.md +0 -0
  116. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/py.typed +0 -0
  117. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/retrieval.py +0 -0
  118. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/sarif.py +0 -0
  119. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/__init__.py +0 -0
  120. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/app.py +0 -0
  121. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/banner.py +0 -0
  122. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/commands.py +0 -0
  123. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/pet.py +0 -0
  124. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/runs.py +0 -0
  125. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/theme.py +0 -0
  126. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/verbs.py +0 -0
  127. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/widgets/__init__.py +0 -0
  128. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/widgets/_shared.py +0 -0
  129. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/widgets/banner.py +0 -0
  130. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/widgets/finding.py +0 -0
  131. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/widgets/footer.py +0 -0
  132. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/widgets/prompt.py +0 -0
  133. {proofpath-0.3.0 → proofpath-0.4.0}/src/proofpath/tui/widgets/run_block.py +0 -0
  134. {proofpath-0.3.0 → proofpath-0.4.0}/tests/__init__.py +0 -0
  135. {proofpath-0.3.0 → proofpath-0.4.0}/tests/conftest.py +0 -0
  136. {proofpath-0.3.0 → proofpath-0.4.0}/tests/data/draft-author-year.md +0 -0
  137. {proofpath-0.3.0 → proofpath-0.4.0}/tests/data/draft-live.md +0 -0
  138. {proofpath-0.3.0 → proofpath-0.4.0}/tests/data/ghost_set.jsonl +0 -0
  139. {proofpath-0.3.0 → proofpath-0.4.0}/tests/data/pairing_author_year.jsonl +0 -0
  140. {proofpath-0.3.0 → proofpath-0.4.0}/tests/data/pairing_set.jsonl +0 -0
  141. {proofpath-0.3.0 → proofpath-0.4.0}/tests/data/sarif-schema-2.1.0.json +0 -0
  142. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fakes.py +0 -0
  143. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/crossref_work.json +0 -0
  144. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/crossref_work_no_abstract.json +0 -0
  145. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/europepmc_fulltext.xml +0 -0
  146. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/europepmc_search.json +0 -0
  147. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/europepmc_search_empty.json +0 -0
  148. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/europepmc_search_not_in_epmc.json +0 -0
  149. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/openalex_work.json +0 -0
  150. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/s2_paper.json +0 -0
  151. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/s2_paper_no_abstract.json +0 -0
  152. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/unpaywall.json +0 -0
  153. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/unpaywall_closed.json +0 -0
  154. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/oa/unpaywall_landing_only.json +0 -0
  155. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/arxiv_id_roberta.xml +0 -0
  156. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/arxiv_title_fabricated.xml +0 -0
  157. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/arxiv_title_roberta.xml +0 -0
  158. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_alphafold.json +0 -0
  159. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_fabricated.json +0 -0
  160. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_numpy.json +0 -0
  161. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_rag.json +0 -0
  162. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_retracted_wakefield.json +0 -0
  163. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_roberta.json +0 -0
  164. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_work_alphafold.json +0 -0
  165. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_work_numpy.json +0 -0
  166. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/openalex_alphafold.json +0 -0
  167. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/openalex_fabricated.json +0 -0
  168. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/openalex_rag.json +0 -0
  169. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/openalex_retracted_wakefield.json +0 -0
  170. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/openalex_roberta.json +0 -0
  171. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/openlibrary_fabricated.json +0 -0
  172. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/openlibrary_using_language.json +0 -0
  173. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/s2_alphafold.json +0 -0
  174. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/s2_fabricated.json +0 -0
  175. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/s2_rag.json +0 -0
  176. {proofpath-0.3.0 → proofpath-0.4.0}/tests/fixtures/resolve/s2_roberta.json +0 -0
  177. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_browser.py +0 -0
  178. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_cache.py +0 -0
  179. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_cache_cli.py +0 -0
  180. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_check_cli.py +0 -0
  181. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_cli.py +0 -0
  182. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_commands.py +0 -0
  183. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_config.py +0 -0
  184. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_config_cli.py +0 -0
  185. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_device.py +0 -0
  186. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_entailment.py +0 -0
  187. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_eval_coverage.py +0 -0
  188. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_eval_pairing.py +0 -0
  189. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_eval_scifact.py +0 -0
  190. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_fetch.py +0 -0
  191. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_fetch_cli.py +0 -0
  192. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_ingest.py +0 -0
  193. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_judge.py +0 -0
  194. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_metrics.py +0 -0
  195. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_models.py +0 -0
  196. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_numerics.py +0 -0
  197. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_oa.py +0 -0
  198. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_pipeline.py +0 -0
  199. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_polite.py +0 -0
  200. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_resolve.py +0 -0
  201. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_resolve_cli.py +0 -0
  202. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_retrieval.py +0 -0
  203. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_sarif.py +0 -0
  204. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_scifact.py +0 -0
  205. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_tui_banner.py +0 -0
  206. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_tui_commands.py +0 -0
  207. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_tui_pet.py +0 -0
  208. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_tui_rich.py +0 -0
  209. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_tui_theme.py +0 -0
  210. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_verify.py +0 -0
  211. {proofpath-0.3.0 → proofpath-0.4.0}/tests/test_version.py +0 -0
  212. {proofpath-0.3.0 → proofpath-0.4.0}/uv.lock +0 -0
@@ -0,0 +1,9 @@
1
+ # Copy to .env (gitignored) and fill in. Only the optional LLM judge needs a key;
2
+ # everything else runs offline. See `proofpath judge` for where this file is read.
3
+ GROQ_API_KEY=
4
+ # GEMINI_API_KEY=
5
+ # A free Reddit app (https://www.reddit.com/prefs/apps, type "script"): its id.
6
+ REDDIT_CLIENT_ID=
7
+ # The same app's secret. Without both, a cited Reddit post is reported as
8
+ # UNVERIFIED (credentials missing) rather than silently skipped.
9
+ REDDIT_CLIENT_SECRET=
@@ -6,6 +6,53 @@ All notable changes to this project are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.0] - 2026-09-16
10
+
11
+ Sources that are not papers: a post's links, and the coverage block that finally says why
12
+ a source could not be read. Also the first end-to-end measurement on real web claims, which
13
+ says plainly that this is not yet the thing to point at a news story
14
+ (`docs/eval/2026-09-16-averitec.md`).
15
+
16
+ ### Added
17
+ - **`check --url`** reads a post and verifies **the links inside it**, never the post's own
18
+ words (spec §6.2). Bluesky (`public.api.bsky.app`) and Hacker News (the Firebase API) are
19
+ first-class and need no account; Reddit reads with a free app you register yourself
20
+ (`REDDIT_CLIENT_ID` / `REDDIT_CLIENT_SECRET` in `.env`); Mastodon is best effort per
21
+ instance; X cannot be read and says so, asking you to paste the text. Pasted text with
22
+ links in it is verified the same way.
23
+ - **`UNVERIFIED (credentials missing)`** (spec §15): a platform that reads only with a
24
+ credential this machine does not have is its own state, never "unreachable". No request is
25
+ made, both variable names are printed, and the secret itself never reaches a log, an error
26
+ or a `repr`.
27
+ - **The coverage block now prints a line per reason** on the terminal and in the markdown
28
+ report, not only in SARIF. `blocked: 3`, `credentials missing: 1`, `unreachable: 2` — a
29
+ source with no recorded reason is listed rather than dropped, so the lines account for
30
+ every unverified source. Product rule 6 on its main surface. The TUI footer does not carry
31
+ them yet.
32
+ - **A `providers/` package** (`academic`, `web`, `social`) behind the `EvidenceProvider`
33
+ protocol of spec §5.2, so a new source family no longer means touching the core. The
34
+ academic and web paths came through byte-identical, pinned by a golden report.
35
+ - **`docs/eval/2026-09-16-averitec.md`**: 100 AVeriTeC dev claims through the whole product.
36
+
37
+ ### Fixed
38
+ - A bare DOI or arXiv **URL** is resolved as a record again instead of being fetched as a
39
+ web page, so it keeps its retraction check and its open-access full text.
40
+ - A `doi:` source is read through the open-access chain and retraction-checked by the
41
+ academic provider whoever resolved the entry — a warm cache could previously route both
42
+ to the web ladder, where a missing retraction notice was reported as "no notice".
43
+ - The `Resolving` stage no longer names Crossref and Semantic Scholar on a run that asked
44
+ neither.
45
+ - A document that cites by linking is never paired as if it printed a numbered bibliography,
46
+ and vice versa: a PDF with a bibliography and no detected marker could previously pair a
47
+ body sentence to a reference it never cited.
48
+ - `check --url` consults the network permission before reading anything; a denied run makes
49
+ no request at all.
50
+
51
+ ### Measured
52
+ - AVeriTeC dev, 100 claims: **0.270 3-way accuracy against a 0.708 majority baseline**
53
+ (4-way 0.240). A third of the claims had no readable source; on the rest the score is
54
+ 0.361. Every `Supported` claim was missed. Nothing was tuned after the measurement.
55
+
9
56
  ## [0.3.0] - 2026-09-15
10
57
 
11
58
  The judge layer: an opt-in LLM second opinion and an opt-in model-written summary.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: proofpath
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Check whether the sources behind a claim actually say what the claim says.
5
5
  Project-URL: Homepage, https://github.com/Yigtwxx/proofpath
6
6
  Project-URL: Repository, https://github.com/Yigtwxx/proofpath
@@ -82,7 +82,7 @@ proofpath
82
82
  ╸┤ o o ╰~~~~~~~~~~~~~[PROOF]
83
83
  ╰─┬─┬────────────────────────────────────────────────────────────────┬─┬──╯
84
84
  ˘ ˘ ˘ ˘
85
- proofpath v0.2.1 academic . online . coreml
85
+ proofpath v0.4.0 academic . online . coreml
86
86
  paste a file path, a URL, or a claim. /help /config /quit
87
87
  ```
88
88
 
@@ -115,7 +115,7 @@ either, for screenshots and bug reports.
115
115
  ```
116
116
  ,_,
117
117
  (o.o)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[PROOF]
118
- " " proofpath v0.2.1 academic . online . coreml
118
+ " " proofpath v0.4.0 academic . online . coreml
119
119
  paste a file path, a URL, or a claim. /help /config /quit
120
120
  ```
121
121
 
@@ -202,6 +202,7 @@ distinct, printed state (spec §15), never collapsed into a verdict:
202
202
  | `UNVERIFIED (reached, no text extracted)` | 200 answered, nothing readable came back |
203
203
  | `UNVERIFIED (network not permitted)` | `permissions.network = deny` |
204
204
  | `UNVERIFIED (provider unavailable)` | API down, rate limited after backoff, or answering with a page instead of a record |
205
+ | `UNVERIFIED (credentials missing)` | the platform reads only with a credential this machine has none of — Reddit's free app, `REDDIT_CLIENT_ID` and `REDDIT_CLIENT_SECRET`. Nobody was asked, so it is not "unreachable"; the coverage block names both variables |
205
206
  | `UNVERIFIED (not in bibliographic indexes)` | web page, blog, report — indexes do not cover it, so absence proves nothing |
206
207
  | `AMBIGUOUS` | several plausible records, all listed |
207
208
  | `NEI` | the source was read and neither supports nor contradicts |
@@ -278,6 +279,32 @@ TablePlus or DBeaver — plain tables, no extension. Raw publisher text expires
278
279
  7 days; verdicts keep the passage they quote; a provider outage is never stored.
279
280
  `proofpath resolve REF` and `proofpath fetch URL|DOI` run either half on its own.
280
281
 
282
+ ## Posts and the links inside them (v0.4)
283
+
284
+ A social post is not a source. What proofpath checks is whether the **links inside it**
285
+ back what it says:
286
+
287
+ ```bash
288
+ proofpath check --url https://bsky.app/profile/bsky.app/post/3movpwtbjgs2d
289
+ proofpath check --url https://news.ycombinator.com/item?id=8863
290
+ proofpath check - # paste the text of a post that cannot be read
291
+ ```
292
+
293
+ Every sentence of the post is checked against the pages its links point to, and the post's
294
+ own words are never allowed to stand as their own evidence.
295
+
296
+ | Platform | How it is read |
297
+ |---|---|
298
+ | Bluesky | `public.api.bsky.app`, no account, first-class. Link cards, rich-text links and one level of quoted post |
299
+ | Hacker News | the official Firebase API, no account, first-class. A story's URL and the links in a comment |
300
+ | Reddit | with a **free app you register yourself**: put `REDDIT_CLIENT_ID` and `REDDIT_CLIENT_SECRET` in `.env`. Without them the run says `UNVERIFIED (credentials missing)` and names both variables — it never quietly skips the post |
301
+ | Mastodon | best effort, per instance. Many instances now require a login for the public API, and that answer is reported as `UNVERIFIED (blocked)`, not as a missing post |
302
+ | X / Twitter | cannot be read at all. `check --url` says so and asks you to paste the text; the links inside it are then verified normally |
303
+
304
+ The Reddit path is built against Reddit's documented shapes and covered by fixtures, but it
305
+ has never run against Reddit on this machine — nobody here has an app to register. Bluesky,
306
+ Hacker News and Mastodon were each read live before release.
307
+
281
308
  ## Optional LLM judge (v0.3)
282
309
 
283
310
  Everything above runs locally, and the default run makes **zero** LLM calls. Two flags
@@ -335,6 +362,21 @@ in the environment or a `.env` file, never from config, and is never printed.
335
362
  | Source access | 50 DOIs | 72 % full text, 18 % abstract only, 10 % nothing ([details](docs/eval/2026-09-11-coverage.md)) — a real biomedical paper in the live runs reached 33 % full text |
336
363
  | Citation pairing, numeric | 61 hand-built passages | 0.99 ([details](docs/eval/2026-09-11-pairing.md)) |
337
364
  | Citation pairing, author-year | 55 hand-built passages, 83 expectations | 0.940 ([details](docs/eval/2026-09-12-pairing-author-year.md)) |
365
+ | **End to end on real web claims** | AVeriTeC dev, 100 claims | **0.270 3-way accuracy against a 0.708 majority baseline — worse than always guessing "refuted"** ([details](docs/eval/2026-09-16-averitec.md)) |
366
+
367
+ **The AVeriTeC row is the one to read before trusting this tool on a news claim.** A third
368
+ of those claims had no readable source at all: 32 of the source URLs needed the browser
369
+ step, 29 were unreachable, 14 were refused by `robots.txt`. On the claims that *did* have a
370
+ readable source the score is 0.361 — still below the baseline, and every one of the 19
371
+ `Supported` claims was missed. The retrieval and entailment models were calibrated on
372
+ scientific abstracts, and a fact-check page is a different object: long, discursive, and
373
+ usually quoting the claim it debunks. Nothing was tuned after that measurement, and no
374
+ blocked URL was dropped from it.
375
+
376
+ What proofpath is good at is the academic path the other rows measure: finding out whether a
377
+ cited paper exists, whether it was retracted, and whether its text says what the sentence
378
+ citing it claims. Pointed at a news claim on the open web, it is currently a coverage
379
+ report with a weak verdict attached.
338
380
 
339
381
  Published SciFact results sit around 70–75 F1, not 95. Nothing is tuned on a test
340
382
  split, and no number is quoted without the run that produced it.
@@ -26,7 +26,7 @@ proofpath
26
26
  ╸┤ o o ╰~~~~~~~~~~~~~[PROOF]
27
27
  ╰─┬─┬────────────────────────────────────────────────────────────────┬─┬──╯
28
28
  ˘ ˘ ˘ ˘
29
- proofpath v0.2.1 academic . online . coreml
29
+ proofpath v0.4.0 academic . online . coreml
30
30
  paste a file path, a URL, or a claim. /help /config /quit
31
31
  ```
32
32
 
@@ -59,7 +59,7 @@ either, for screenshots and bug reports.
59
59
  ```
60
60
  ,_,
61
61
  (o.o)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[PROOF]
62
- " " proofpath v0.2.1 academic . online . coreml
62
+ " " proofpath v0.4.0 academic . online . coreml
63
63
  paste a file path, a URL, or a claim. /help /config /quit
64
64
  ```
65
65
 
@@ -146,6 +146,7 @@ distinct, printed state (spec §15), never collapsed into a verdict:
146
146
  | `UNVERIFIED (reached, no text extracted)` | 200 answered, nothing readable came back |
147
147
  | `UNVERIFIED (network not permitted)` | `permissions.network = deny` |
148
148
  | `UNVERIFIED (provider unavailable)` | API down, rate limited after backoff, or answering with a page instead of a record |
149
+ | `UNVERIFIED (credentials missing)` | the platform reads only with a credential this machine has none of — Reddit's free app, `REDDIT_CLIENT_ID` and `REDDIT_CLIENT_SECRET`. Nobody was asked, so it is not "unreachable"; the coverage block names both variables |
149
150
  | `UNVERIFIED (not in bibliographic indexes)` | web page, blog, report — indexes do not cover it, so absence proves nothing |
150
151
  | `AMBIGUOUS` | several plausible records, all listed |
151
152
  | `NEI` | the source was read and neither supports nor contradicts |
@@ -222,6 +223,32 @@ TablePlus or DBeaver — plain tables, no extension. Raw publisher text expires
222
223
  7 days; verdicts keep the passage they quote; a provider outage is never stored.
223
224
  `proofpath resolve REF` and `proofpath fetch URL|DOI` run either half on its own.
224
225
 
226
+ ## Posts and the links inside them (v0.4)
227
+
228
+ A social post is not a source. What proofpath checks is whether the **links inside it**
229
+ back what it says:
230
+
231
+ ```bash
232
+ proofpath check --url https://bsky.app/profile/bsky.app/post/3movpwtbjgs2d
233
+ proofpath check --url https://news.ycombinator.com/item?id=8863
234
+ proofpath check - # paste the text of a post that cannot be read
235
+ ```
236
+
237
+ Every sentence of the post is checked against the pages its links point to, and the post's
238
+ own words are never allowed to stand as their own evidence.
239
+
240
+ | Platform | How it is read |
241
+ |---|---|
242
+ | Bluesky | `public.api.bsky.app`, no account, first-class. Link cards, rich-text links and one level of quoted post |
243
+ | Hacker News | the official Firebase API, no account, first-class. A story's URL and the links in a comment |
244
+ | Reddit | with a **free app you register yourself**: put `REDDIT_CLIENT_ID` and `REDDIT_CLIENT_SECRET` in `.env`. Without them the run says `UNVERIFIED (credentials missing)` and names both variables — it never quietly skips the post |
245
+ | Mastodon | best effort, per instance. Many instances now require a login for the public API, and that answer is reported as `UNVERIFIED (blocked)`, not as a missing post |
246
+ | X / Twitter | cannot be read at all. `check --url` says so and asks you to paste the text; the links inside it are then verified normally |
247
+
248
+ The Reddit path is built against Reddit's documented shapes and covered by fixtures, but it
249
+ has never run against Reddit on this machine — nobody here has an app to register. Bluesky,
250
+ Hacker News and Mastodon were each read live before release.
251
+
225
252
  ## Optional LLM judge (v0.3)
226
253
 
227
254
  Everything above runs locally, and the default run makes **zero** LLM calls. Two flags
@@ -279,6 +306,21 @@ in the environment or a `.env` file, never from config, and is never printed.
279
306
  | Source access | 50 DOIs | 72 % full text, 18 % abstract only, 10 % nothing ([details](docs/eval/2026-09-11-coverage.md)) — a real biomedical paper in the live runs reached 33 % full text |
280
307
  | Citation pairing, numeric | 61 hand-built passages | 0.99 ([details](docs/eval/2026-09-11-pairing.md)) |
281
308
  | Citation pairing, author-year | 55 hand-built passages, 83 expectations | 0.940 ([details](docs/eval/2026-09-12-pairing-author-year.md)) |
309
+ | **End to end on real web claims** | AVeriTeC dev, 100 claims | **0.270 3-way accuracy against a 0.708 majority baseline — worse than always guessing "refuted"** ([details](docs/eval/2026-09-16-averitec.md)) |
310
+
311
+ **The AVeriTeC row is the one to read before trusting this tool on a news claim.** A third
312
+ of those claims had no readable source at all: 32 of the source URLs needed the browser
313
+ step, 29 were unreachable, 14 were refused by `robots.txt`. On the claims that *did* have a
314
+ readable source the score is 0.361 — still below the baseline, and every one of the 19
315
+ `Supported` claims was missed. The retrieval and entailment models were calibrated on
316
+ scientific abstracts, and a fact-check page is a different object: long, discursive, and
317
+ usually quoting the claim it debunks. Nothing was tuned after that measurement, and no
318
+ blocked URL was dropped from it.
319
+
320
+ What proofpath is good at is the academic path the other rows measure: finding out whether a
321
+ cited paper exists, whether it was retracted, and whether its text says what the sentence
322
+ citing it claims. Pointed at a news claim on the open web, it is currently a coverage
323
+ report with a weak verdict attached.
282
324
 
283
325
  Published SciFact results sit around 70–75 F1, not 95. Nothing is tuned on a test
284
326
  split, and no number is quoted without the run that produced it.
@@ -0,0 +1,95 @@
1
+ # AVeriTeC dev — 2026-09-16
2
+
3
+ - claims: 100 (limit=100)
4
+ - dataset: `https://raw.githubusercontent.com/MichSchli/AVeriTeC/main/data/dev.json` (sha256 499793726b4a…)
5
+ - one run of the whole product: real claims, real source pages, real fetch ladder.
6
+
7
+ ## Headline
8
+
9
+ 3-way accuracy **0.270** vs majority baseline 0.708, over 89 of 100 claims.
10
+
11
+ The 3-way number excludes the Conflicting Evidence/Cherrypicking rows, which proofpath has no verdict for; counting them as wrong gives a 4-way accuracy of 0.240.
12
+
13
+ ## Per label
14
+
15
+ | label | n | correct | accuracy |
16
+ |---|---|---|---|
17
+ | Supported | 19 | 0 | 0.000 |
18
+ | Refuted | 63 | 20 | 0.317 |
19
+ | Not Enough Evidence | 7 | 4 | 0.571 |
20
+ | Conflicting Evidence/Cherrypicking | 11 | 0 | 0.000 |
21
+
22
+ ## Source coverage
23
+
24
+ | state | count |
25
+ |---|---|
26
+ | ok | 120 |
27
+ | UNVERIFIED (blocked, browser not permitted) | 32 |
28
+ | UNVERIFIED (unreachable) | 29 |
29
+ | UNVERIFIED (blocked, robots.txt) | 14 |
30
+ | not a url | 9 |
31
+ | UNVERIFIED (reached, no text extracted) | 3 |
32
+ | UNVERIFIED (provider unavailable) | 2 |
33
+
34
+ 76 of 200 source URLs are web.archive.org snapshots (38.0 %).
35
+
36
+ ## What the numbers say
37
+
38
+ **proofpath is worse than always guessing "Refuted" on this set.** The majority baseline is
39
+ 0.708 because 63 of the 89 answerable dev claims are refutations; proofpath scores 0.270.
40
+ That is the headline and it is not softened anywhere else in this repository.
41
+
42
+ Where the loss comes from, from the per-claim rows:
43
+
44
+ | | count |
45
+ |---|---|
46
+ | claims where **no** source could be read | 33 of 100 |
47
+ | claims with at least one readable source | 67 |
48
+ | 3-way accuracy on those 67 (61 answerable) | **0.361** |
49
+ | claims answered `NEI` | 31 |
50
+ | claims answered nothing at all (no source read) | 33 |
51
+ | claims answered `SUPPORTED` | 7 |
52
+ | claims answered `REFUTED` | 29 |
53
+
54
+ Two separate problems, and the coverage one is the larger:
55
+
56
+ 1. **A third of the claims had nothing to read.** 32 source URLs needed the browser step
57
+ (this run passed `--no-browser`), 29 were unreachable, 14 were refused by `robots.txt`.
58
+ AVeriTeC's evidence is news sites, fact-check pages and archived snapshots — the exact
59
+ corner of the web that a polite, browserless fetcher is worst at. A claim with no
60
+ readable source is reported as such and counts as `NEI`, which is honest and still wrong
61
+ against the gold label 26 of the 28 times such a claim was scorable at all. Counting every
62
+ claim the run answered `NEI` — the 31 it decided and the 33 it could not read — 56 are
63
+ scorable and 52 of those are wrong.
64
+ 2. **Even with a source in hand, entailment over a news page is much harder than over an
65
+ abstract.** On the claims that did have a readable source the score is 0.361, still below
66
+ the baseline. Every one of the 19 `Supported` claims was missed: seven came back `NEI`,
67
+ seven had nothing to read, five were called `REFUTED`. The retrieval and NLI models were
68
+ calibrated on SciFact — scientific abstracts, one claim per abstract — and a fact-check
69
+ page is a different object: long, discursive, and often quoting the claim it debunks.
70
+
71
+ The `Refuted` column is the only one that works (20 of 63), and some of that is the prior
72
+ doing the work rather than the evidence.
73
+
74
+ ## What this does not say
75
+
76
+ - It is not a statement about proofpath's academic path. SciFact dev (abstracts, 340 pairs)
77
+ scores 0.609 with a 0.406 baseline; the ghost set scores 0.0 % false ghosts. Those measure
78
+ different things and are measured separately.
79
+ - It is not tuned away. No threshold was moved after seeing this, no URL was dropped for
80
+ being blocked, and the coverage table above prints every state rather than the ones that
81
+ flatter the run.
82
+ - 38 % of the source URLs are `web.archive.org` snapshots, so a good part of what was read
83
+ is an archived copy of the page the dataset's annotators saw, not today's page.
84
+
85
+ ## How it was run
86
+
87
+ `uv run python scripts/eval_averitec.py --limit 100 --no-browser --sleep 1.0 --resume`
88
+ on the v0.4.0 development build, 2026-09-16, Apple Silicon, cache warm from nothing.
89
+ Per-claim rows are in `~/Library/Caches/proofpath/datasets/averitec_results.json`; the run
90
+ is resumable, so the same command reproduces it without re-fetching. The dataset is pinned
91
+ by sha256 and the loader refuses a mismatch.
92
+
93
+ Allowing the browser step would raise coverage and probably the score; it was left off so
94
+ the number describes the default install, which is what a reader gets.
95
+
@@ -461,8 +461,52 @@ Findings from inspecting the installed base environment, and the decisions they
461
461
  | 14.11 | The unanswered judge is absent from the SARIF log | `_run_properties` carries `models` and `apiCalls` but no `judge status` / `summary status`, so a `--format sarif` log of a run whose provider was down reads like one with nothing to escalate. SARIF is a findings document, and the markdown, JSON and terminal all say it; still, a viewer-only workflow does not see it |
462
462
  | 14.12 | Groq's free tier is metered on prompt **plus** requested answer | the whole-phase review caught `TOKEN_CAP = 7000` left over from the 1,024-token answer budget: with the 4,096-token budget of 9.3 a full batch would ask for ~11.1k against an 8K/minute tier. The cap is now 3,500. Nothing measured this live — the live escalation set was one item |
463
463
 
464
+ ### Carried into Phase 10
465
+
466
+ | # | Item | Note |
467
+ |---|---|---|
468
+ | 14.21 | `Document.kind` now mixes origin with citation style | the five original values say where a document came from (`pdf`, `docx`, `markdown`, `text`, `post`); `linked`, added so `claims.extract` could stay a pure function of the document, says how it cites — a paste is `text` in origin and the field no longer records that. The clean shape is a second field (`cites: "marker" | "link"`), blocked today only because `Report.document` is serialised field by field into the frozen golden payload, so any new field rewrites it. Visible to a user only as `document.kind` in `--format json` |
469
+ | 14.16 | A post is never cached | the fetch cache lives inside `Fetcher`, which the social provider bypasses, so a post is re-read on every run and `--no-cache` changes nothing for one. Nothing stale is served (the chunk cache is pinned to the text's digest), but the `Fetching` line can never attribute a post to `cache` |
470
+ | 14.17 | A post's text is graded `fulltext` at any length | `text_kind` answers "how much of the source was read", and an API-returned post is complete, so `fulltext` is the honest word — but a three-word post cited by a bibliography then counts as a full-text source in the coverage block. Distinguishing "short source, read whole" from "long document read whole" is a `Coverage` change, not a grading one |
471
+ | 14.18 | A directly constructed `Engine` builds a `PoliteClient` it never closes | `Engine.default` is clean (it passes the client already on `_closers`); a hand-built one allocates an `httpx` transport per engine. No socket opens until a post is read, so nothing leaks functionally |
472
+ | 14.19 | Pasted text with no links gets no "nothing to verify" note | the stage-1 note is gated on the post path, and a paste carrying no addresses never becomes `kind="linked"`, so it prints zero findings — which is what a clean run looks like. Spec §6.2 makes this the designated path for an unreadable post, so it is the one place the gap matters |
473
+ | 14.20 | `check --url` flattens two §15 states into one exit code | an unreachable post and an unavailable provider both exit 2; the hint distinguishes them, the exit code does not |
474
+ | 14.15 | The resolution cache is shared across routing changes | `cache.put_resolution` stores whatever the entry's provider answered under the entry's raw string, so a row written by one release is read back by another whose routing differs. Both of Task 10.1's warm-cache defects (a `doi:` source read by the web provider; the same source skipping the retraction check) reached the code through exactly this door, and it will open again whenever a provider's routing moves |
475
+
464
476
  ### Next session
465
477
 
466
478
  1. **Phase 10** (`providers/` refactor, Bluesky/HN, Reddit/Mastodon/X, AVeriTeC) runs
467
479
  from `plans/2026-09-12-phases-9-10-plan.md`; Task 10.4a (AVeriTeC loader and scorer)
468
480
  is already implemented and reviewed, unstaged behind the v0.3.0 tag.
481
+
482
+ ## 15. Phase 10 — v0.4.0, 2026-09-16
483
+
484
+ - Shipped: the `providers/` package (10.1; academic and web moved behind the §5.2 protocol
485
+ with the report byte-identical, pinned by a golden payload written before the refactor),
486
+ Bluesky and Hacker News with `check --url` and pasted-text links (10.2), Reddit/Mastodon/X
487
+ and the per-reason coverage block (10.3), and the AVeriTeC harness (10.4).
488
+ - **The headline measurement is below baseline and is reported as such everywhere**:
489
+ 0.270 3-way against a 0.708 majority baseline on 100 AVeriTeC dev claims
490
+ (`docs/eval/2026-09-16-averitec.md`). A third of the claims had no readable source; on the
491
+ rest the score is 0.361; every `Supported` claim was missed. Nothing was tuned after the
492
+ run and no blocked URL was dropped from it.
493
+ - Reviewers caught, before the tag: a bare DOI/arXiv URL losing its record and its retraction
494
+ check; a warm-cache `doi:` source read up the web ladder with the open-access chain never
495
+ asked, and retraction-checked by a provider that always answers "no notice"; the `Resolving`
496
+ line naming providers nobody called; a PDF's body sentences paired to a bibliography entry
497
+ they never cited; `check --url` fetching before the network permission was read. Five
498
+ product-rule failures the 1,900-test suite did not catch on its own.
499
+
500
+ ### New open items
501
+
502
+ | # | Item | Note |
503
+ |---|---|---|
504
+ | 15.1 | proofpath is below baseline on real-world web claims | the two causes are separable: a third of the claims had nothing readable (the browser step is off by default, `robots.txt` is honoured, news sites block), and the entailment models were calibrated on scientific abstracts. Neither is fixed by tuning a threshold. A browser-enabled run was not measured |
505
+ | 15.2 | The Reddit path has never run against Reddit | fixtures reproduce the documented shapes; nobody on this machine has an app to register. The first real-credential run should check the comment-listing shape first |
506
+ | 15.3 | `/r/<sub>/s/<hash>` share links are unrecognised | they degrade to the web ladder, so nothing is invented; the shape is simply not parsed |
507
+ | 15.4 | The TUI footer ignores `Footer.reasons` | the per-reason coverage lines reach the terminal and the markdown report but not the TUI, so a TUI run whose sources were half blocked still shows three numbers and no reason |
508
+ | 15.7 | A Mastodon status is read outside the fetch ladder | `check --url https://<any host>/@a/123` issues a request to that host's `/api/v1/statuses/123` directly: no `robots.txt` check and no entry in `polite.MIN_INTERVAL`. The user typed the address, so it is not a rule breach, but it is the one read path that skips the politeness machinery |
509
+ | 15.8 | `target_document`'s `network_allowed` defaults to `True` | every in-tree caller passes the resolved permission, so the default is unreachable today; it is a footgun for the next caller and should be a required keyword |
510
+ | 15.9 | The Reddit token exchange follows redirects with basic auth attached | httpx re-applies the credential along a redirect chain. The URL is a fixed Reddit constant, so it needs Reddit itself to be the attacker, but `follow_redirects=False` on that one call costs nothing |
511
+ | 15.5 | `NO_REASON_RECORDED` is a user-visible state outside §15 | it is a guard that should never print; if it can print, it belongs in the table |
512
+ | 15.6 | `NOT_READ_HERE` is provably unreachable through `verify` | kept deliberately for a sixth platform, like `checker_for`'s `arxiv:` row |
@@ -331,6 +331,17 @@ For any unreadable post, the user pastes the text and `proofpath` verifies the
331
331
 
332
332
  **Done when:** measured on AVeriTeC, and no platform integration requires a paid API.
333
333
 
334
+ **Status (2026-09-16): released as v0.4.0.** Tasks 10.1–10.4 (`.superpowers/sdd/phase10/`,
335
+ briefs in `2026-09-12-phases-9-10-plan.md`): the `providers/` package with the academic and
336
+ web families refactored behind it byte-identically (golden report pinned before the change
337
+ and unchanged after); Bluesky and Hacker News with `check --url` and pasted-text links;
338
+ Reddit (user's own OAuth app, fixtures only — nobody here has an app), Mastodon (live read)
339
+ and X (paste only); the coverage block printing a line per reason; and the AVeriTeC harness.
340
+ No platform integration requires a paid API. **The measurement is below baseline:** 0.270
341
+ 3-way against 0.708 on 100 dev claims (`docs/eval/2026-09-16-averitec.md`). It is reported
342
+ in the README's own Measured table, in the spec §14 row and in the changelog, unsoftened,
343
+ because the alternative is a tool that looks better than it is.
344
+
334
345
  ---
335
346
 
336
347
  ## What is deliberately not planned yet
@@ -821,7 +821,7 @@ stage so a regression can be located.
821
821
  |---|---|---|
822
822
  | SciFact (AI2 tarball, sha256 pinned; not the HF loader) | retrieval + entailment on scientific claims | label accuracy, macro-F1, rationale F1 |
823
823
  | FEVER | retrieval + entailment baseline | label accuracy |
824
- | AVeriTeC | real-world web claim verification | AVeriTeC score |
824
+ | AVeriTeC | real-world web claim verification | AVeriTeC score. **Measured 2026-09-16 on 100 dev claims: 0.270 3-way against a 0.708 majority baseline** (4-way 0.240) — below the baseline. A third of the claims had no readable source (browser step off, unreachable, `robots.txt`); on the rest, 0.361, and every `Supported` claim was missed. `docs/eval/2026-09-16-averitec.md` |
825
825
  | PubHealth | high-harm domain behaviour | label accuracy |
826
826
  | X Community Notes | social provider sanity check | agreement with human notes |
827
827
  | Hand-built ghost set | reference resolution (§8) | precision/recall on ghosts, **false-ghost rate** |
@@ -885,11 +885,13 @@ presented as evidence of absence.
885
885
  | `LOW CONFIDENCE (abstract only)` | full text unavailable, abstract used |
886
886
  | `UNVERIFIED (blocked)` | 403/bot protection, Scrapling absent or defeated |
887
887
  | `UNVERIFIED (unreachable)` | dead link, Wayback miss |
888
+ | `UNVERIFIED (no identifier to fetch)` | the reference resolved to a record that carries no DOI, arXiv id or address, so there is nothing to fetch |
888
889
  | `UNVERIFIED (reached, no text extracted)` | the page answered 200 but no text could be extracted (bot wall, image-only page) |
889
890
  | `UNVERIFIED (blocked, browser not permitted)` | steps 1–2 blocked and the §7.1 consent was denied, absent, or impossible without a TTY |
890
891
  | `UNVERIFIED (blocked, robots.txt)` | the site's `robots.txt` disallows the fetch; steps 3–4 are not attempted |
891
892
  | `UNVERIFIED (network not permitted)` | `permissions.network = deny`; nothing was fetched |
892
893
  | `UNVERIFIED (provider unavailable)` | API down or rate limited after backoff |
894
+ | `UNVERIFIED (credentials missing)` | the source is on a platform that reads only with a credential this machine does not have — today Reddit, whose free app is `REDDIT_CLIENT_ID` and `REDDIT_CLIENT_SECRET`. Nobody was asked, so it is neither unreachable nor blocked; the coverage block names the two variables (§6.2) |
893
895
  | `AMBIGUOUS` | multiple plausible reference candidates — all listed |
894
896
  | `NEI` | source read, but it neither supports nor contradicts |
895
897
  | `UNVERIFIED (not in bibliographic indexes)` | web page, blog, report, manual or organisation-authored document; indexes do not cover it, so absence proves nothing (§8.1) |
@@ -905,8 +907,16 @@ Every report ends with a coverage summary:
905
907
  verified against full text 62%
906
908
  abstract only 21%
907
909
  unverified 17%
910
+ blocked: 3
911
+ credentials missing: 1
908
912
  ```
909
913
 
914
+ Whenever anything is unverified, every reason is listed under the share it
915
+ explains — most common first, then by name — in the terminal and in the markdown
916
+ report alike. Three percentages with no reason beside them let a run whose sources
917
+ were half blocked read exactly like a thin one. `UNVERIFIED (credentials missing)`
918
+ also puts the names of the variables that would fix it under the block.
919
+
910
920
  A run where coverage is low is a run whose conclusions are weak, and the user is
911
921
  told so directly.
912
922
 
@@ -953,6 +963,14 @@ an unanswered judge reported on every surface but SARIF. Live run: `docs/eval/20
953
963
  **v0.4 — social provider.** Bluesky and Hacker News first, Reddit via user-supplied
954
964
  OAuth app (missing credentials are reported, never silently skipped), Mastodon
955
965
  best-effort, Community Notes dumps for X. Measured on AVeriTeC.
966
+ *Shipped 2026-09-16 as v0.4.0:* the `providers/` package (§5.2) with the academic and web
967
+ families moved behind it byte-identically; `check --url` reading a post and verifying the
968
+ links inside it; Bluesky, Hacker News, Reddit (user's own app), Mastodon best effort, X by
969
+ pasting; `UNVERIFIED (credentials missing)` as its own state; the coverage block printing a
970
+ line per reason on the terminal and in the markdown report (the TUI footer does not yet
971
+ carry them — OPEN-ITEMS 15.4). X Community Notes dumps were **not** built — the paste
972
+ path replaced them (OPEN-ITEMS 3.5). Measured: `docs/eval/2026-09-16-averitec.md`, and the
973
+ number is bad — see §14.
956
974
 
957
975
  **Later.** Turkish sources as a separate provider (TR Dizin / DergiPark class),
958
976
  `spiyweb` graph retrieval as an alternative backend, GROBID parser.