proofpath 0.2.1__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 (214) hide show
  1. proofpath-0.4.0/.env.example +9 -0
  2. {proofpath-0.2.1 → proofpath-0.4.0}/CHANGELOG.md +93 -0
  3. {proofpath-0.2.1 → proofpath-0.4.0}/PKG-INFO +97 -10
  4. {proofpath-0.2.1 → proofpath-0.4.0}/README.md +96 -9
  5. proofpath-0.4.0/docs/eval/2026-09-15-judge-live.md +95 -0
  6. proofpath-0.4.0/docs/eval/2026-09-16-averitec.md +95 -0
  7. {proofpath-0.2.1 → proofpath-0.4.0}/docs/superpowers/OPEN-ITEMS.md +90 -7
  8. {proofpath-0.2.1 → proofpath-0.4.0}/docs/superpowers/plans/2026-09-10-proofpath-implementation-plan.md +24 -0
  9. {proofpath-0.2.1 → proofpath-0.4.0}/docs/superpowers/specs/2026-09-10-proofpath-design.md +41 -5
  10. proofpath-0.4.0/scripts/eval_averitec.py +385 -0
  11. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/__init__.py +1 -1
  12. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/cache.py +91 -4
  13. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/claims.py +106 -2
  14. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/cli.py +86 -13
  15. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/document.py +10 -1
  16. proofpath-0.4.0/src/proofpath/eval/averitec.py +148 -0
  17. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/ingest.py +136 -1
  18. proofpath-0.4.0/src/proofpath/judge.py +781 -0
  19. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/polite.py +70 -4
  20. proofpath-0.4.0/src/proofpath/prompts/__init__.py +1 -0
  21. proofpath-0.4.0/src/proofpath/prompts/review.md +40 -0
  22. proofpath-0.4.0/src/proofpath/prompts/summarize.md +30 -0
  23. proofpath-0.4.0/src/proofpath/providers/__init__.py +318 -0
  24. proofpath-0.4.0/src/proofpath/providers/academic.py +117 -0
  25. proofpath-0.4.0/src/proofpath/providers/social.py +915 -0
  26. proofpath-0.4.0/src/proofpath/providers/web.py +120 -0
  27. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/report.py +272 -6
  28. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/resolve.py +18 -1
  29. proofpath-0.4.0/src/proofpath/secrets.py +115 -0
  30. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/app.py +104 -3
  31. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/runs.py +13 -0
  32. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/widgets/run_block.py +13 -0
  33. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/ui.py +72 -6
  34. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/verify.py +720 -250
  35. proofpath-0.4.0/tests/data/verify-report-golden.json +630 -0
  36. proofpath-0.4.0/tests/fixtures/social/bsky-blocked.json +11 -0
  37. proofpath-0.4.0/tests/fixtures/social/bsky-not-found.json +4 -0
  38. proofpath-0.4.0/tests/fixtures/social/bsky-quote-blocked.json +28 -0
  39. proofpath-0.4.0/tests/fixtures/social/bsky-resolve-handle.json +3 -0
  40. proofpath-0.4.0/tests/fixtures/social/bsky-thread-gone.json +7 -0
  41. proofpath-0.4.0/tests/fixtures/social/bsky-thread.json +70 -0
  42. proofpath-0.4.0/tests/fixtures/social/hn-comment.json +8 -0
  43. proofpath-0.4.0/tests/fixtures/social/hn-dead.json +9 -0
  44. proofpath-0.4.0/tests/fixtures/social/hn-deleted.json +6 -0
  45. proofpath-0.4.0/tests/fixtures/social/hn-missing.json +1 -0
  46. proofpath-0.4.0/tests/fixtures/social/hn-story.json +11 -0
  47. proofpath-0.4.0/tests/fixtures/social/mastodon-login-required.json +1 -0
  48. proofpath-0.4.0/tests/fixtures/social/mastodon-status.json +12 -0
  49. proofpath-0.4.0/tests/fixtures/social/reddit-comment.json +38 -0
  50. proofpath-0.4.0/tests/fixtures/social/reddit-link-post.json +26 -0
  51. proofpath-0.4.0/tests/fixtures/social/reddit-nested-comment.json +55 -0
  52. proofpath-0.4.0/tests/fixtures/social/reddit-post.json +26 -0
  53. proofpath-0.4.0/tests/fixtures/social/reddit-removed.json +27 -0
  54. proofpath-0.4.0/tests/fixtures/social/reddit-token.json +6 -0
  55. proofpath-0.4.0/tests/test_averitec.py +221 -0
  56. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_cache.py +148 -5
  57. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_check_cli.py +400 -8
  58. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_claims.py +122 -1
  59. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_document.py +7 -2
  60. proofpath-0.4.0/tests/test_eval_averitec.py +276 -0
  61. proofpath-0.4.0/tests/test_judge.py +959 -0
  62. proofpath-0.4.0/tests/test_providers.py +549 -0
  63. proofpath-0.4.0/tests/test_providers_social.py +1281 -0
  64. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_report.py +341 -1
  65. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_sarif.py +30 -2
  66. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_tui_app.py +161 -15
  67. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_tui_runs.py +28 -7
  68. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_ui.py +103 -0
  69. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_verify.py +563 -3
  70. proofpath-0.4.0/tests/test_verify_golden.py +239 -0
  71. proofpath-0.4.0/tests/test_verify_routing.py +255 -0
  72. proofpath-0.2.1/.env.example +0 -4
  73. proofpath-0.2.1/src/proofpath/judge.py +0 -149
  74. proofpath-0.2.1/tests/test_judge.py +0 -106
  75. {proofpath-0.2.1 → proofpath-0.4.0}/.editorconfig +0 -0
  76. {proofpath-0.2.1 → proofpath-0.4.0}/.gitattributes +0 -0
  77. {proofpath-0.2.1 → proofpath-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  78. {proofpath-0.2.1 → proofpath-0.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  79. {proofpath-0.2.1 → proofpath-0.4.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  80. {proofpath-0.2.1 → proofpath-0.4.0}/.github/ISSUE_TEMPLATE/wrong_verdict.yml +0 -0
  81. {proofpath-0.2.1 → proofpath-0.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  82. {proofpath-0.2.1 → proofpath-0.4.0}/.github/workflows/ci.yml +0 -0
  83. {proofpath-0.2.1 → proofpath-0.4.0}/.github/workflows/release.yml +0 -0
  84. {proofpath-0.2.1 → proofpath-0.4.0}/.gitignore +0 -0
  85. {proofpath-0.2.1 → proofpath-0.4.0}/CLAUDE.md +0 -0
  86. {proofpath-0.2.1 → proofpath-0.4.0}/CONTRIBUTING.md +0 -0
  87. {proofpath-0.2.1 → proofpath-0.4.0}/LICENSE +0 -0
  88. {proofpath-0.2.1 → proofpath-0.4.0}/SECURITY.md +0 -0
  89. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-11-coverage.md +0 -0
  90. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-11-ghosts.md +0 -0
  91. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-11-pairing.md +0 -0
  92. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-11-scifact-dev.md +0 -0
  93. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-12-ghosts.md +0 -0
  94. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-12-pairing-author-year.md +0 -0
  95. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-12-scifact-dev.md +0 -0
  96. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-12-tiers.md +0 -0
  97. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-12-v0.1-live.md +0 -0
  98. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-15-tui-v2-live.md +0 -0
  99. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/2026-09-15-v0.2-live.md +0 -0
  100. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/tui-v2-plain.svg +0 -0
  101. {proofpath-0.2.1 → proofpath-0.4.0}/docs/eval/tui-v2-rich.svg +0 -0
  102. {proofpath-0.2.1 → proofpath-0.4.0}/docs/research/2026-09-11-free-llm-api-tiers.md +0 -0
  103. {proofpath-0.2.1 → proofpath-0.4.0}/docs/superpowers/plans/2026-09-12-phases-9-10-plan.md +0 -0
  104. {proofpath-0.2.1 → proofpath-0.4.0}/docs/superpowers/specs/2026-09-15-tui-v2-design.md +0 -0
  105. {proofpath-0.2.1 → proofpath-0.4.0}/pyproject.toml +0 -0
  106. {proofpath-0.2.1 → proofpath-0.4.0}/scripts/eval_coverage.py +0 -0
  107. {proofpath-0.2.1 → proofpath-0.4.0}/scripts/eval_ghosts.py +0 -0
  108. {proofpath-0.2.1 → proofpath-0.4.0}/scripts/eval_pairing.py +0 -0
  109. {proofpath-0.2.1 → proofpath-0.4.0}/scripts/eval_scifact.py +0 -0
  110. {proofpath-0.2.1 → proofpath-0.4.0}/scripts/zero_network_check.py +0 -0
  111. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/browser.py +0 -0
  112. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/commands.py +0 -0
  113. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/config.py +0 -0
  114. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/device.py +0 -0
  115. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/entailment.py +0 -0
  116. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/eval/__init__.py +0 -0
  117. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/eval/metrics.py +0 -0
  118. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/eval/scifact.py +0 -0
  119. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/events.py +0 -0
  120. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/fetch.py +0 -0
  121. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/models.py +0 -0
  122. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/numerics.py +0 -0
  123. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/oa.py +0 -0
  124. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/paths.py +0 -0
  125. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/pipeline.py +0 -0
  126. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/py.typed +0 -0
  127. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/retrieval.py +0 -0
  128. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/sarif.py +0 -0
  129. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/__init__.py +0 -0
  130. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/banner.py +0 -0
  131. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/commands.py +0 -0
  132. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/pet.py +0 -0
  133. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/theme.py +0 -0
  134. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/verbs.py +0 -0
  135. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/widgets/__init__.py +0 -0
  136. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/widgets/_shared.py +0 -0
  137. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/widgets/banner.py +0 -0
  138. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/widgets/finding.py +0 -0
  139. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/widgets/footer.py +0 -0
  140. {proofpath-0.2.1 → proofpath-0.4.0}/src/proofpath/tui/widgets/prompt.py +0 -0
  141. {proofpath-0.2.1 → proofpath-0.4.0}/tests/__init__.py +0 -0
  142. {proofpath-0.2.1 → proofpath-0.4.0}/tests/conftest.py +0 -0
  143. {proofpath-0.2.1 → proofpath-0.4.0}/tests/data/draft-author-year.md +0 -0
  144. {proofpath-0.2.1 → proofpath-0.4.0}/tests/data/draft-live.md +0 -0
  145. {proofpath-0.2.1 → proofpath-0.4.0}/tests/data/ghost_set.jsonl +0 -0
  146. {proofpath-0.2.1 → proofpath-0.4.0}/tests/data/pairing_author_year.jsonl +0 -0
  147. {proofpath-0.2.1 → proofpath-0.4.0}/tests/data/pairing_set.jsonl +0 -0
  148. {proofpath-0.2.1 → proofpath-0.4.0}/tests/data/sarif-schema-2.1.0.json +0 -0
  149. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fakes.py +0 -0
  150. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/crossref_work.json +0 -0
  151. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/crossref_work_no_abstract.json +0 -0
  152. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/europepmc_fulltext.xml +0 -0
  153. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/europepmc_search.json +0 -0
  154. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/europepmc_search_empty.json +0 -0
  155. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/europepmc_search_not_in_epmc.json +0 -0
  156. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/openalex_work.json +0 -0
  157. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/s2_paper.json +0 -0
  158. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/s2_paper_no_abstract.json +0 -0
  159. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/unpaywall.json +0 -0
  160. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/unpaywall_closed.json +0 -0
  161. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/oa/unpaywall_landing_only.json +0 -0
  162. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/arxiv_id_roberta.xml +0 -0
  163. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/arxiv_title_fabricated.xml +0 -0
  164. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/arxiv_title_roberta.xml +0 -0
  165. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_alphafold.json +0 -0
  166. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_fabricated.json +0 -0
  167. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_numpy.json +0 -0
  168. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_rag.json +0 -0
  169. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_retracted_wakefield.json +0 -0
  170. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_roberta.json +0 -0
  171. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_work_alphafold.json +0 -0
  172. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/crossref_work_numpy.json +0 -0
  173. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/openalex_alphafold.json +0 -0
  174. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/openalex_fabricated.json +0 -0
  175. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/openalex_rag.json +0 -0
  176. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/openalex_retracted_wakefield.json +0 -0
  177. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/openalex_roberta.json +0 -0
  178. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/openlibrary_fabricated.json +0 -0
  179. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/openlibrary_using_language.json +0 -0
  180. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/s2_alphafold.json +0 -0
  181. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/s2_fabricated.json +0 -0
  182. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/s2_rag.json +0 -0
  183. {proofpath-0.2.1 → proofpath-0.4.0}/tests/fixtures/resolve/s2_roberta.json +0 -0
  184. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_browser.py +0 -0
  185. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_cache_cli.py +0 -0
  186. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_cli.py +0 -0
  187. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_commands.py +0 -0
  188. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_config.py +0 -0
  189. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_config_cli.py +0 -0
  190. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_device.py +0 -0
  191. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_entailment.py +0 -0
  192. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_eval_coverage.py +0 -0
  193. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_eval_pairing.py +0 -0
  194. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_eval_scifact.py +0 -0
  195. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_fetch.py +0 -0
  196. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_fetch_cli.py +0 -0
  197. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_ingest.py +0 -0
  198. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_metrics.py +0 -0
  199. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_models.py +0 -0
  200. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_numerics.py +0 -0
  201. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_oa.py +0 -0
  202. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_pipeline.py +0 -0
  203. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_polite.py +0 -0
  204. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_resolve.py +0 -0
  205. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_resolve_cli.py +0 -0
  206. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_retrieval.py +0 -0
  207. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_scifact.py +0 -0
  208. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_tui_banner.py +0 -0
  209. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_tui_commands.py +0 -0
  210. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_tui_pet.py +0 -0
  211. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_tui_rich.py +0 -0
  212. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_tui_theme.py +0 -0
  213. {proofpath-0.2.1 → proofpath-0.4.0}/tests/test_version.py +0 -0
  214. {proofpath-0.2.1 → 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,99 @@ 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
+
56
+ ## [0.3.0] - 2026-09-15
57
+
58
+ The judge layer: an opt-in LLM second opinion and an opt-in model-written summary.
59
+ The default run still makes zero LLM calls, and nothing the model says can change a
60
+ verdict. Live run on Groq in `docs/eval/2026-09-15-judge-live.md`.
61
+
62
+ ### Added
63
+ - **`check --judge`** (`judge.py`, `verify.py`). After the local verdicts are final, the
64
+ `low`-tier ones — never a numeric mismatch, never a claim without a quoted passage —
65
+ go to the model in batches of up to 20 items (about 7k tokens), each with its claim and
66
+ passage. The opinion (`SUPPORTED | REFUTED | NEI` plus a one-sentence rationale) is
67
+ attached beside the verdict: a `= judge (groq openai/gpt-oss-120b): …` line under a
68
+ finding it disagrees with, a `judge` column in the markdown `## Checked` table, and
69
+ `judge` fields in the JSON. The local `Verdict`, the finding kind and every state are
70
+ untouched (spec §11.1). Opinions are cached in the new `judgements` table (schema v4,
71
+ additive; wiped with the verdicts when a source's text changes), so a re-run asks
72
+ nothing, and `model_id` is untouched, so toggling `--judge` never invalidates a verdict.
73
+ - **`check --summarize`** and the TUI's **`/summarize`**: one extra call over the finished
74
+ markdown report, run after the report is final, off by default in both front-ends,
75
+ printed as `summary (model-written, groq openai/gpt-oss-120b) …` and as
76
+ `## Summary (model-written, …)` in the file. `--summarize` alone is exactly one call.
77
+ - **`JudgeClient`**: one adapter for Groq (default `openai/gpt-oss-120b`), Gemini and
78
+ Ollama over the OpenAI `chat/completions` shape; strict JSON-schema output with a
79
+ `json_object` fallback, `reasoning_effort=low` with a fallback for providers that
80
+ reject it, `Retry-After` on 429 (capped, accounted), exponential backoff on 5xx, then
81
+ `JudgeUnavailable`. The key comes from the environment or `.env`, never from config,
82
+ never appears in `repr`, errors or logs; provider bodies are never echoed. Prompts are
83
+ packaged template files (`proofpath/prompts/review.md`, `summarize.md`).
84
+ - **Cost on every surface**: the `Judging` and `Summarising` stage lines carry calls and
85
+ prompt/completion tokens; the footer counts the calls; `Report.judge_cost` and
86
+ `models["judge"]` land in the JSON.
87
+ - **An unanswered judge is reported, not hidden**: `judge unavailable after N calls
88
+ (HTTP 401 from …); local verdicts stand` in the stage line, the report header
89
+ (`judge status:` / `summary status:`), the JSON and an unsuppressed terminal line — a
90
+ `-q` or piped run cannot look like a judged-clean one. The SARIF log does not carry it.
91
+ The run never fails because of the judge.
92
+ - Gemini prints its data-use warning once per run (spec §11).
93
+
94
+ ### Changed
95
+ - `check --judge` / `--summarize` no longer exit with `arrives in v0.3`.
96
+ - Cache schema **v4** (`judgements`); a v1 file still migrates through the whole chain.
97
+ - The markdown `## Checked` table always carries a `judge` column; without `--judge` every
98
+ cell is `—`, so a v0.2 report and a v0.3 one differ by that column alone.
99
+ - `--format json` gained `summary`, `summary_model`, `judge_cost` and per-result `judge`
100
+ fields; every one of them is `null` on a default run.
101
+
9
102
  ## [0.2.1] - 2026-09-15
10
103
 
11
104
  The TUI's second look. No behaviour change: every state word, every honesty sentence,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: proofpath
3
- Version: 0.2.1
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 |
@@ -239,7 +240,12 @@ runtime teardown); fixed in v0.1 — 20 of 20 piped runs exit `1`
239
240
  counted as unreachable.
240
241
  - **Coverage is not perfectly reproducible**: two runs minutes apart can read a
241
242
  different number of sources, depending on which providers answered.
242
- - The LLM judge and `--summarize` arrive in **v0.3**.
243
+ - **The judge is a second opinion, not a second verdict.** `--judge` asks the model only
244
+ about the low-tier verdicts (1 of 10 on the live draft), and its answer is printed
245
+ beside the local verdict, never in place of it. `--summarize` is one extra call over
246
+ the finished report, labelled model-written; if the provider does not answer, the
247
+ markdown report, the JSON and the terminal say so (`judge status` / `summary status`
248
+ in the header). The SARIF log does not carry it — it is a findings document.
243
249
 
244
250
  ## Speed
245
251
 
@@ -273,13 +279,79 @@ TablePlus or DBeaver — plain tables, no extension. Raw publisher text expires
273
279
  7 days; verdicts keep the passage they quote; a provider outage is never stored.
274
280
  `proofpath resolve REF` and `proofpath fetch URL|DOI` run either half on its own.
275
281
 
276
- ## Optional LLM judge arrives in v0.3
282
+ ## Posts and the links inside them (v0.4)
277
283
 
278
- Everything above runs locally. An LLM is used only at the end, as an opt-in second
279
- opinion on low-confidence verdicts — it never sees a source document and cannot
280
- change a verdict. The settings exist today (`proofpath config check` proves a key
281
- works, default Groq); `check --judge` and `--summarize` are wired in v0.3. The key
282
- comes from the environment or a `.env` file, never from config, and is never printed.
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
+
308
+ ## Optional LLM judge (v0.3)
309
+
310
+ Everything above runs locally, and the default run makes **zero** LLM calls. Two flags
311
+ add an LLM at the end, and only there:
312
+
313
+ ```bash
314
+ proofpath check paper.pdf --judge # a second opinion on the low-confidence verdicts
315
+ proofpath check paper.pdf --summarize # one model-written paragraph over the finished report
316
+ proofpath config check # proves the key works before you spend a run on it
317
+ ```
318
+
319
+ **What `--judge` does.** After the local verdicts are final, the verdicts the models
320
+ were least sure about — the `low` tier, never a numeric mismatch and never a claim
321
+ without a quoted passage — go to the model in batches of up to 20 (about 7k tokens),
322
+ each with its claim and the passage it was checked against. The model answers from the
323
+ passage alone, and its label and one-sentence rationale are printed **beside** the local
324
+ verdict: `= judge (groq openai/gpt-oss-120b): NEI — …`. The local verdict, the finding
325
+ kind and the report's states never change. Opinions are cached with the verdict, so a
326
+ re-run asks nothing.
327
+
328
+ **What it cannot do.** It never sees a source document, so it cannot introduce a claim
329
+ or an evidence passage of its own; it cannot turn `NEI` into `SUPPORTED`; it cannot
330
+ hide a source that could not be read. If the provider is down, rate-limited or the key
331
+ is wrong, the run finishes on the local verdicts and says so in the stage line, the
332
+ report header (`judge status:`) and the JSON — `-q` cannot hide it.
333
+
334
+ **What `--summarize` does.** One final call turns the finished markdown report into 3–5
335
+ plain sentences a reader can act on. It runs after the report is complete, its only
336
+ input is that report, it is off by default in the CLI and the TUI (`/summarize` there),
337
+ and the output is labelled `(model-written, <provider> <model>)`. `--summarize` alone
338
+ is exactly one call; with `--judge` the escalation runs first.
339
+
340
+ **Cost.** The footer counts the calls and the stage line the tokens:
341
+ `Judging … 1 of 10 verdicts reviewed, 1 call, 613 prompt · 193 completion tokens` and
342
+ `Summarising … 98 words, 1 call, 1,608 prompt · 343 completion tokens`
343
+ on the live draft ([details](docs/eval/2026-09-15-judge-live.md)). Groq's free tier
344
+ allows roughly one call a minute. Only the low-tier verdicts are sent — 1 of 10 on that
345
+ draft — and up to 20 go in one call, so a long bibliography costs a handful of calls, not
346
+ one per citation.
347
+
348
+ **Providers.** Default is Groq `openai/gpt-oss-120b` (free without a card, no training
349
+ on submitted data). `proofpath config set judge.provider gemini` switches to Gemini —
350
+ note that Google trains on free-tier prompts outside the EEA/UK/CH, and proofpath prints
351
+ that warning once per run. `judge.provider ollama` runs fully offline. Gemini and Ollama are
352
+ fixture-tested and were not exercised live in v0.3.0. All three speak
353
+ the OpenAI `chat/completions` shape. The key comes from `GROQ_API_KEY` / `GEMINI_API_KEY`
354
+ in the environment or a `.env` file, never from config, and is never printed.
283
355
 
284
356
  ## Measured
285
357
 
@@ -290,6 +362,21 @@ comes from the environment or a `.env` file, never from config, and is never pri
290
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 |
291
363
  | Citation pairing, numeric | 61 hand-built passages | 0.99 ([details](docs/eval/2026-09-11-pairing.md)) |
292
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.
293
380
 
294
381
  Published SciFact results sit around 70–75 F1, not 95. Nothing is tuned on a test
295
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 |
@@ -183,7 +184,12 @@ runtime teardown); fixed in v0.1 — 20 of 20 piped runs exit `1`
183
184
  counted as unreachable.
184
185
  - **Coverage is not perfectly reproducible**: two runs minutes apart can read a
185
186
  different number of sources, depending on which providers answered.
186
- - The LLM judge and `--summarize` arrive in **v0.3**.
187
+ - **The judge is a second opinion, not a second verdict.** `--judge` asks the model only
188
+ about the low-tier verdicts (1 of 10 on the live draft), and its answer is printed
189
+ beside the local verdict, never in place of it. `--summarize` is one extra call over
190
+ the finished report, labelled model-written; if the provider does not answer, the
191
+ markdown report, the JSON and the terminal say so (`judge status` / `summary status`
192
+ in the header). The SARIF log does not carry it — it is a findings document.
187
193
 
188
194
  ## Speed
189
195
 
@@ -217,13 +223,79 @@ TablePlus or DBeaver — plain tables, no extension. Raw publisher text expires
217
223
  7 days; verdicts keep the passage they quote; a provider outage is never stored.
218
224
  `proofpath resolve REF` and `proofpath fetch URL|DOI` run either half on its own.
219
225
 
220
- ## Optional LLM judge arrives in v0.3
226
+ ## Posts and the links inside them (v0.4)
221
227
 
222
- Everything above runs locally. An LLM is used only at the end, as an opt-in second
223
- opinion on low-confidence verdicts — it never sees a source document and cannot
224
- change a verdict. The settings exist today (`proofpath config check` proves a key
225
- works, default Groq); `check --judge` and `--summarize` are wired in v0.3. The key
226
- comes from the environment or a `.env` file, never from config, and is never printed.
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
+
252
+ ## Optional LLM judge (v0.3)
253
+
254
+ Everything above runs locally, and the default run makes **zero** LLM calls. Two flags
255
+ add an LLM at the end, and only there:
256
+
257
+ ```bash
258
+ proofpath check paper.pdf --judge # a second opinion on the low-confidence verdicts
259
+ proofpath check paper.pdf --summarize # one model-written paragraph over the finished report
260
+ proofpath config check # proves the key works before you spend a run on it
261
+ ```
262
+
263
+ **What `--judge` does.** After the local verdicts are final, the verdicts the models
264
+ were least sure about — the `low` tier, never a numeric mismatch and never a claim
265
+ without a quoted passage — go to the model in batches of up to 20 (about 7k tokens),
266
+ each with its claim and the passage it was checked against. The model answers from the
267
+ passage alone, and its label and one-sentence rationale are printed **beside** the local
268
+ verdict: `= judge (groq openai/gpt-oss-120b): NEI — …`. The local verdict, the finding
269
+ kind and the report's states never change. Opinions are cached with the verdict, so a
270
+ re-run asks nothing.
271
+
272
+ **What it cannot do.** It never sees a source document, so it cannot introduce a claim
273
+ or an evidence passage of its own; it cannot turn `NEI` into `SUPPORTED`; it cannot
274
+ hide a source that could not be read. If the provider is down, rate-limited or the key
275
+ is wrong, the run finishes on the local verdicts and says so in the stage line, the
276
+ report header (`judge status:`) and the JSON — `-q` cannot hide it.
277
+
278
+ **What `--summarize` does.** One final call turns the finished markdown report into 3–5
279
+ plain sentences a reader can act on. It runs after the report is complete, its only
280
+ input is that report, it is off by default in the CLI and the TUI (`/summarize` there),
281
+ and the output is labelled `(model-written, <provider> <model>)`. `--summarize` alone
282
+ is exactly one call; with `--judge` the escalation runs first.
283
+
284
+ **Cost.** The footer counts the calls and the stage line the tokens:
285
+ `Judging … 1 of 10 verdicts reviewed, 1 call, 613 prompt · 193 completion tokens` and
286
+ `Summarising … 98 words, 1 call, 1,608 prompt · 343 completion tokens`
287
+ on the live draft ([details](docs/eval/2026-09-15-judge-live.md)). Groq's free tier
288
+ allows roughly one call a minute. Only the low-tier verdicts are sent — 1 of 10 on that
289
+ draft — and up to 20 go in one call, so a long bibliography costs a handful of calls, not
290
+ one per citation.
291
+
292
+ **Providers.** Default is Groq `openai/gpt-oss-120b` (free without a card, no training
293
+ on submitted data). `proofpath config set judge.provider gemini` switches to Gemini —
294
+ note that Google trains on free-tier prompts outside the EEA/UK/CH, and proofpath prints
295
+ that warning once per run. `judge.provider ollama` runs fully offline. Gemini and Ollama are
296
+ fixture-tested and were not exercised live in v0.3.0. All three speak
297
+ the OpenAI `chat/completions` shape. The key comes from `GROQ_API_KEY` / `GEMINI_API_KEY`
298
+ in the environment or a `.env` file, never from config, and is never printed.
227
299
 
228
300
  ## Measured
229
301
 
@@ -234,6 +306,21 @@ comes from the environment or a `.env` file, never from config, and is never pri
234
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 |
235
307
  | Citation pairing, numeric | 61 hand-built passages | 0.99 ([details](docs/eval/2026-09-11-pairing.md)) |
236
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.
237
324
 
238
325
  Published SciFact results sit around 70–75 F1, not 95. Nothing is tuned on a test
239
326
  split, and no number is quoted without the run that produced it.
@@ -0,0 +1,95 @@
1
+ # Judge layer live runs — 2026-09-15 (v0.3.0)
2
+
3
+ `proofpath check tests/data/draft-live.md --judge --summarize` on Groq
4
+ (`openai/gpt-oss-120b`, key from `.env`), Apple Silicon, models already downloaded.
5
+ Three runs: the first against the pre-fix build (it found the defect), then a cold run
6
+ (`--no-cache`) and a warm run against the shipped build. Stage lines are copied from
7
+ the terminal; the key never appears in any output.
8
+
9
+ ## Run 1 — pre-fix build: the summary came back empty
10
+
11
+ ```
12
+ Judging groq openai/gpt-oss-120b 1 of 10 verdicts reviewed, 1 call, 661 prompt · 211 completion tokens 0.9s
13
+ Summarising groq openai/gpt-oss-120b summary unavailable after 0 calls (no completion in the 200 response from https://api.groq.com/openai/v1/chat/completions (finish_reason=length)); local verdicts stand 1.0s
14
+ ```
15
+
16
+ `openai/gpt-oss-120b` is a reasoning model: with the planned `max_tokens=400` the
17
+ completion budget was spent on reasoning and the visible content was empty, and the
18
+ written `report.md` said nothing about the summary at all (the 9.3 reviewer had found
19
+ the same gap from the code). Both are fixed in the shipped build: requests carry
20
+ `reasoning_effort=low` (dropped on a 400 from providers that reject it), the review
21
+ budget is 4096 tokens and the summary budget 1500, and an unanswered summary is
22
+ reported on the stage line, the `summary` CLI line and a `- summary status:` header
23
+ line in the file. The 1,888-test suite was green before this run; the live run found it.
24
+
25
+ ## Run 2 — cold (`--no-cache`), shipped build
26
+
27
+ ```
28
+ Parsing text 1 pages, 7 refs 0.0s
29
+ Claims rules 7 citations, 0 unresolved 0.0s
30
+ Resolving Crossref, Semantic Scholar 6 ok, 0 amb, 1 ghost 9.3s
31
+ Retractions Retraction Watch none 2.5s
32
+ Fetching arXiv, Semantic Scholar 5 full text, 1 abstract, 0 unverified 23.8s
33
+ Verifying coreml 10 claims: 1 supported, 2 not supported, 7 NEI 44.5s
34
+ Judging groq openai/gpt-oss-120b 1 of 10 verdicts reviewed, 1 call, 613 prompt · 193 completion tokens 1.1s
35
+ Summarising groq openai/gpt-oss-120b 98 words, 1 call, 1,608 prompt · 343 completion tokens 1.0s
36
+ … written · 2 API calls · 82.1s
37
+ judge 2,221 prompt · 536 completion tokens
38
+ ```
39
+
40
+ The escalation set was one verdict — the `low`-tier `NOT SUPPORTED` on the SciPy
41
+ sentence — and the judge's opinion was printed beside it, the local verdict untouched:
42
+
43
+ ```
44
+ error[not-supported]: claim is not supported by the cited source (confidence: low)
45
+ = judge (groq openai/gpt-oss-120b): NEI — Passage states "SciPy provides fundamental
46
+ algorithms for scientific computing" but does not mention SciPy building on anything.
47
+ ```
48
+
49
+ The numeric mismatch (`high`, rule-decided) and the ghost reference were not sent, as
50
+ specified. The summary, verbatim:
51
+
52
+ > The report checked seven cited references, and it found two claims not supported and
53
+ > one ghost reference; it does not give a total number of claims that were examined. The
54
+ > most serious problem is the high‑confidence "NOT SUPPORTED" error on line 6, where the
55
+ > claim about training time for the Transformer contradicts the source. The coverage
56
+ > analysis shows that only 72 % of the material was verified against full text, 14 % was
57
+ > limited to abstracts, and the remaining 14 % could not be read, so the counts above
58
+ > apply only to the portion that was actually examined.
59
+
60
+ It repeats the coverage caveat as instructed and invents no finding. Two things it
61
+ gets slightly wrong, which is why it is labelled model-written and sits under the
62
+ computed report: it calls the line-6 numeric mismatch a "NOT SUPPORTED" error (the
63
+ report says `numeric-mismatch: claim contradicts the cited source`), and "it does not
64
+ give a total number of claims" is true of the markdown file (the stage table is not
65
+ printed there) but not of the run (`10 claims`).
66
+
67
+ ## Run 3 — warm, shipped build
68
+
69
+ ```
70
+ Resolving cache 6 ok, 0 amb, 1 ghost 0.0s
71
+ Fetching cache 5 full text, 1 abstract, 0 unverified 0.0s
72
+ Verifying coreml 10 claims: 1 supported, 2 not supported, 7 NEI, 10 cached 0.8s
73
+ Judging groq openai/gpt-oss-120b 1 of 10 verdicts reviewed, 0 calls, 0 prompt · 0 completion tokens 0.0s
74
+ Summarising groq openai/gpt-oss-120b 106 words, 1 call, 1,615 prompt · 263 completion tokens 0.9s
75
+ … written · 1 API calls · 1.8s
76
+ judge 1,615 prompt · 263 completion tokens
77
+ ```
78
+
79
+ The judgement came back from the schema-v4 `judgements` table (0 calls); the summary
80
+ is always one fresh call. A plain `check` of the same file (no flags) reports
81
+ `api_calls: 0` and `summary: null`.
82
+
83
+ ## What this does and does not show
84
+
85
+ - The judge layer works end to end on Groq's free tier: batching, strict-JSON output,
86
+ cost accounting, caching, attribution, and the report unchanged by the model.
87
+ - One document, one low-tier verdict, one provider. Nothing here measures the judge's
88
+ accuracy; the escalation band is narrow by design (OPEN-ITEMS 14.1), so a paper with
89
+ 118 citations would send a handful of verdicts, not dozens.
90
+ - Gemini and Ollama were not exercised live; they share the adapter and are covered by
91
+ the `respx` tests only.
92
+ - These runs were made with the batch cap at 7,000 prompt tokens. The whole-phase review
93
+ lowered it to 3,500 so that a full batch plus its 4,096-token answer budget stays under
94
+ Groq's 8K-per-minute tier; with one escalated item the runs above never approached
95
+ either figure, so nothing here measures the cap.
@@ -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
+