proofpath 0.0.1__tar.gz → 0.2.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 (163) hide show
  1. proofpath-0.2.0/.env.example +4 -0
  2. proofpath-0.2.0/.gitattributes +3 -0
  3. proofpath-0.2.0/CHANGELOG.md +284 -0
  4. proofpath-0.2.0/PKG-INFO +275 -0
  5. proofpath-0.2.0/README.md +219 -0
  6. proofpath-0.2.0/docs/eval/2026-09-11-coverage.md +110 -0
  7. proofpath-0.2.0/docs/eval/2026-09-11-ghosts.md +77 -0
  8. proofpath-0.2.0/docs/eval/2026-09-11-pairing.md +340 -0
  9. proofpath-0.2.0/docs/eval/2026-09-11-scifact-dev.md +90 -0
  10. proofpath-0.2.0/docs/eval/2026-09-12-ghosts.md +222 -0
  11. proofpath-0.2.0/docs/eval/2026-09-12-pairing-author-year.md +238 -0
  12. proofpath-0.2.0/docs/eval/2026-09-12-scifact-dev.md +59 -0
  13. proofpath-0.2.0/docs/eval/2026-09-12-tiers.md +95 -0
  14. proofpath-0.2.0/docs/eval/2026-09-12-v0.1-live.md +767 -0
  15. proofpath-0.2.0/docs/eval/2026-09-15-v0.2-live.md +264 -0
  16. proofpath-0.2.0/docs/research/2026-09-11-free-llm-api-tiers.md +66 -0
  17. proofpath-0.2.0/docs/superpowers/OPEN-ITEMS.md +404 -0
  18. {proofpath-0.0.1 → proofpath-0.2.0}/docs/superpowers/plans/2026-09-10-proofpath-implementation-plan.md +99 -11
  19. proofpath-0.2.0/docs/superpowers/plans/2026-09-12-phases-9-10-plan.md +414 -0
  20. proofpath-0.2.0/docs/superpowers/specs/2026-09-10-proofpath-design.md +944 -0
  21. {proofpath-0.0.1 → proofpath-0.2.0}/pyproject.toml +32 -5
  22. proofpath-0.2.0/scripts/eval_coverage.py +443 -0
  23. proofpath-0.2.0/scripts/eval_ghosts.py +157 -0
  24. proofpath-0.2.0/scripts/eval_pairing.py +662 -0
  25. proofpath-0.2.0/scripts/eval_scifact.py +403 -0
  26. proofpath-0.2.0/scripts/zero_network_check.py +81 -0
  27. {proofpath-0.0.1 → proofpath-0.2.0}/src/proofpath/__init__.py +1 -1
  28. proofpath-0.2.0/src/proofpath/browser.py +335 -0
  29. proofpath-0.2.0/src/proofpath/cache.py +762 -0
  30. proofpath-0.2.0/src/proofpath/claims.py +652 -0
  31. proofpath-0.2.0/src/proofpath/cli.py +695 -0
  32. proofpath-0.2.0/src/proofpath/commands.py +337 -0
  33. proofpath-0.2.0/src/proofpath/config.py +216 -0
  34. proofpath-0.2.0/src/proofpath/device.py +60 -0
  35. proofpath-0.2.0/src/proofpath/document.py +155 -0
  36. proofpath-0.2.0/src/proofpath/entailment.py +167 -0
  37. proofpath-0.2.0/src/proofpath/eval/__init__.py +1 -0
  38. proofpath-0.2.0/src/proofpath/eval/metrics.py +111 -0
  39. proofpath-0.2.0/src/proofpath/eval/scifact.py +130 -0
  40. proofpath-0.2.0/src/proofpath/events.py +91 -0
  41. proofpath-0.2.0/src/proofpath/fetch.py +670 -0
  42. proofpath-0.2.0/src/proofpath/ingest.py +771 -0
  43. proofpath-0.2.0/src/proofpath/judge.py +149 -0
  44. proofpath-0.2.0/src/proofpath/models.py +49 -0
  45. proofpath-0.2.0/src/proofpath/numerics.py +309 -0
  46. proofpath-0.2.0/src/proofpath/oa.py +492 -0
  47. proofpath-0.2.0/src/proofpath/paths.py +40 -0
  48. proofpath-0.2.0/src/proofpath/pipeline.py +153 -0
  49. proofpath-0.2.0/src/proofpath/polite.py +189 -0
  50. proofpath-0.2.0/src/proofpath/report.py +734 -0
  51. proofpath-0.2.0/src/proofpath/resolve.py +960 -0
  52. proofpath-0.2.0/src/proofpath/retrieval.py +200 -0
  53. proofpath-0.2.0/src/proofpath/sarif.py +240 -0
  54. proofpath-0.2.0/src/proofpath/tui/__init__.py +7 -0
  55. proofpath-0.2.0/src/proofpath/tui/app.py +1813 -0
  56. proofpath-0.2.0/src/proofpath/tui/banner.py +89 -0
  57. proofpath-0.2.0/src/proofpath/tui/commands.py +119 -0
  58. proofpath-0.2.0/src/proofpath/tui/runs.py +459 -0
  59. proofpath-0.2.0/src/proofpath/ui.py +305 -0
  60. proofpath-0.2.0/src/proofpath/verify.py +1443 -0
  61. proofpath-0.2.0/tests/__init__.py +0 -0
  62. proofpath-0.2.0/tests/conftest.py +23 -0
  63. proofpath-0.2.0/tests/data/draft-author-year.md +25 -0
  64. proofpath-0.2.0/tests/data/draft-live.md +41 -0
  65. proofpath-0.2.0/tests/data/ghost_set.jsonl +274 -0
  66. proofpath-0.2.0/tests/data/pairing_author_year.jsonl +55 -0
  67. proofpath-0.2.0/tests/data/pairing_set.jsonl +61 -0
  68. proofpath-0.2.0/tests/data/sarif-schema-2.1.0.json +2882 -0
  69. proofpath-0.2.0/tests/fakes.py +96 -0
  70. proofpath-0.2.0/tests/fixtures/oa/crossref_work.json +47 -0
  71. proofpath-0.2.0/tests/fixtures/oa/crossref_work_no_abstract.json +16 -0
  72. proofpath-0.2.0/tests/fixtures/oa/europepmc_fulltext.xml +22 -0
  73. proofpath-0.2.0/tests/fixtures/oa/europepmc_search.json +19 -0
  74. proofpath-0.2.0/tests/fixtures/oa/europepmc_search_empty.json +1 -0
  75. proofpath-0.2.0/tests/fixtures/oa/europepmc_search_not_in_epmc.json +18 -0
  76. proofpath-0.2.0/tests/fixtures/oa/openalex_work.json +19 -0
  77. proofpath-0.2.0/tests/fixtures/oa/s2_paper.json +15 -0
  78. proofpath-0.2.0/tests/fixtures/oa/s2_paper_no_abstract.json +6 -0
  79. proofpath-0.2.0/tests/fixtures/oa/unpaywall.json +12 -0
  80. proofpath-0.2.0/tests/fixtures/oa/unpaywall_closed.json +1 -0
  81. proofpath-0.2.0/tests/fixtures/oa/unpaywall_landing_only.json +12 -0
  82. proofpath-0.2.0/tests/fixtures/resolve/arxiv_id_roberta.xml +51 -0
  83. proofpath-0.2.0/tests/fixtures/resolve/arxiv_title_fabricated.xml +10 -0
  84. proofpath-0.2.0/tests/fixtures/resolve/arxiv_title_roberta.xml +51 -0
  85. proofpath-0.2.0/tests/fixtures/resolve/crossref_alphafold.json +1 -0
  86. proofpath-0.2.0/tests/fixtures/resolve/crossref_fabricated.json +1 -0
  87. proofpath-0.2.0/tests/fixtures/resolve/crossref_numpy.json +37 -0
  88. proofpath-0.2.0/tests/fixtures/resolve/crossref_rag.json +1 -0
  89. proofpath-0.2.0/tests/fixtures/resolve/crossref_retracted_wakefield.json +1 -0
  90. proofpath-0.2.0/tests/fixtures/resolve/crossref_roberta.json +1 -0
  91. proofpath-0.2.0/tests/fixtures/resolve/crossref_work_alphafold.json +1 -0
  92. proofpath-0.2.0/tests/fixtures/resolve/crossref_work_numpy.json +34 -0
  93. proofpath-0.2.0/tests/fixtures/resolve/openalex_alphafold.json +1 -0
  94. proofpath-0.2.0/tests/fixtures/resolve/openalex_fabricated.json +1 -0
  95. proofpath-0.2.0/tests/fixtures/resolve/openalex_rag.json +1 -0
  96. proofpath-0.2.0/tests/fixtures/resolve/openalex_retracted_wakefield.json +1 -0
  97. proofpath-0.2.0/tests/fixtures/resolve/openalex_roberta.json +1 -0
  98. proofpath-0.2.0/tests/fixtures/resolve/openlibrary_fabricated.json +1 -0
  99. proofpath-0.2.0/tests/fixtures/resolve/openlibrary_using_language.json +1 -0
  100. proofpath-0.2.0/tests/fixtures/resolve/s2_alphafold.json +1 -0
  101. proofpath-0.2.0/tests/fixtures/resolve/s2_fabricated.json +1 -0
  102. proofpath-0.2.0/tests/fixtures/resolve/s2_rag.json +1 -0
  103. proofpath-0.2.0/tests/fixtures/resolve/s2_roberta.json +1 -0
  104. proofpath-0.2.0/tests/test_browser.py +500 -0
  105. proofpath-0.2.0/tests/test_cache.py +654 -0
  106. proofpath-0.2.0/tests/test_cache_cli.py +115 -0
  107. proofpath-0.2.0/tests/test_check_cli.py +830 -0
  108. proofpath-0.2.0/tests/test_claims.py +729 -0
  109. proofpath-0.2.0/tests/test_cli.py +87 -0
  110. proofpath-0.2.0/tests/test_commands.py +411 -0
  111. proofpath-0.2.0/tests/test_config.py +112 -0
  112. proofpath-0.2.0/tests/test_config_cli.py +143 -0
  113. proofpath-0.2.0/tests/test_device.py +39 -0
  114. proofpath-0.2.0/tests/test_document.py +179 -0
  115. proofpath-0.2.0/tests/test_entailment.py +95 -0
  116. proofpath-0.2.0/tests/test_eval_coverage.py +292 -0
  117. proofpath-0.2.0/tests/test_eval_pairing.py +560 -0
  118. proofpath-0.2.0/tests/test_eval_scifact.py +183 -0
  119. proofpath-0.2.0/tests/test_fetch.py +923 -0
  120. proofpath-0.2.0/tests/test_fetch_cli.py +400 -0
  121. proofpath-0.2.0/tests/test_ingest.py +1121 -0
  122. proofpath-0.2.0/tests/test_judge.py +106 -0
  123. proofpath-0.2.0/tests/test_metrics.py +46 -0
  124. proofpath-0.2.0/tests/test_models.py +35 -0
  125. proofpath-0.2.0/tests/test_numerics.py +190 -0
  126. proofpath-0.2.0/tests/test_oa.py +652 -0
  127. proofpath-0.2.0/tests/test_pipeline.py +165 -0
  128. proofpath-0.2.0/tests/test_polite.py +237 -0
  129. proofpath-0.2.0/tests/test_report.py +1252 -0
  130. proofpath-0.2.0/tests/test_resolve.py +1186 -0
  131. proofpath-0.2.0/tests/test_resolve_cli.py +263 -0
  132. proofpath-0.2.0/tests/test_retrieval.py +206 -0
  133. proofpath-0.2.0/tests/test_sarif.py +380 -0
  134. proofpath-0.2.0/tests/test_scifact.py +77 -0
  135. proofpath-0.2.0/tests/test_tui_app.py +1464 -0
  136. proofpath-0.2.0/tests/test_tui_banner.py +117 -0
  137. proofpath-0.2.0/tests/test_tui_commands.py +141 -0
  138. proofpath-0.2.0/tests/test_tui_runs.py +522 -0
  139. proofpath-0.2.0/tests/test_ui.py +593 -0
  140. proofpath-0.2.0/tests/test_verify.py +1520 -0
  141. {proofpath-0.0.1 → proofpath-0.2.0}/uv.lock +351 -14
  142. proofpath-0.0.1/CHANGELOG.md +0 -23
  143. proofpath-0.0.1/PKG-INFO +0 -112
  144. proofpath-0.0.1/README.md +0 -43
  145. proofpath-0.0.1/docs/superpowers/OPEN-ITEMS.md +0 -120
  146. proofpath-0.0.1/docs/superpowers/specs/2026-09-10-proofpath-design.md +0 -587
  147. proofpath-0.0.1/src/proofpath/cli.py +0 -69
  148. proofpath-0.0.1/tests/test_cli.py +0 -23
  149. {proofpath-0.0.1 → proofpath-0.2.0}/.editorconfig +0 -0
  150. {proofpath-0.0.1 → proofpath-0.2.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  151. {proofpath-0.0.1 → proofpath-0.2.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  152. {proofpath-0.0.1 → proofpath-0.2.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  153. {proofpath-0.0.1 → proofpath-0.2.0}/.github/ISSUE_TEMPLATE/wrong_verdict.yml +0 -0
  154. {proofpath-0.0.1 → proofpath-0.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  155. {proofpath-0.0.1 → proofpath-0.2.0}/.github/workflows/ci.yml +0 -0
  156. {proofpath-0.0.1 → proofpath-0.2.0}/.github/workflows/release.yml +0 -0
  157. {proofpath-0.0.1 → proofpath-0.2.0}/.gitignore +0 -0
  158. {proofpath-0.0.1 → proofpath-0.2.0}/CLAUDE.md +0 -0
  159. {proofpath-0.0.1 → proofpath-0.2.0}/CONTRIBUTING.md +0 -0
  160. {proofpath-0.0.1 → proofpath-0.2.0}/LICENSE +0 -0
  161. {proofpath-0.0.1 → proofpath-0.2.0}/SECURITY.md +0 -0
  162. {proofpath-0.0.1 → proofpath-0.2.0}/src/proofpath/py.typed +0 -0
  163. {proofpath-0.0.1 → proofpath-0.2.0}/tests/test_version.py +0 -0
@@ -0,0 +1,4 @@
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=
@@ -0,0 +1,3 @@
1
+ # Test fixtures whose bytes are pinned by a digest must not be CRLF-converted.
2
+ tests/data/*.json text eol=lf
3
+ tests/data/*.jsonl text eol=lf
@@ -0,0 +1,284 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.2.0] - 2026-09-15
10
+
11
+ The interactive front-end, author-year citations, SARIF output and a cache for the
12
+ two lookups that made a warm re-run slow. Bare `proofpath` now opens the TUI; the
13
+ one-shot verbs are unchanged for CI. Live checks in `docs/eval/2026-09-15-v0.2-live.md`.
14
+
15
+ ### Added
16
+ - **TUI** (spec §13.1): bare `proofpath` opens a `textual` session — a four-line banner
17
+ with the pet, one prompt bar, and a scrolling log of run blocks. Every CLI verb is a
18
+ slash command (`/check`, `/resolve`, `/fetch`, `/config`, `/cache`; plus `/allow`,
19
+ `/cancel #n`, `/help`, `/quit`), a bare verb waits for its argument (`Esc` leaves
20
+ it), and a line that is not a command is something to check. Runs are scheduled
21
+ concurrently through one `Scheduler` (network stages in parallel under a shared
22
+ politeness limiter, the NLI model one run at a time), each run is **cancellable**
23
+ mid-flight and keeps what it had decided, the coverage footer never scrolls away,
24
+ and the §7.1 permission prompt is drawn **inline under the stage that hit the wall**
25
+ with `[allow once] [always] [no] [never]` buttons (or `/allow …`). Every log line
26
+ works by mouse and by keyboard: run headers fold, stage rows hide their summary,
27
+ findings open the full quoted passage, `⧉`/`c` copies it, a finding's reference is
28
+ an OSC 8 link to its source. The pet's eyes blink, watch a run and react to its
29
+ result — only with colour, never under `--no-color` or `-q`.
30
+ - **Author-year citations** (spec §9, §17): `(Smith et al., 2020)`, `Smith (2020)`,
31
+ `(Smith, 2020; Jones, 2019)`, `2020a`/`2020b` collisions, `ibid.` and `op. cit.`
32
+ back-references, and mixed `(Smith, 2020; [12])` all pair with their bibliography
33
+ entry. Hand-built set of 55 passages, 83 expectations, rate **0.940**
34
+ (`docs/eval/2026-09-12-pairing-author-year.md`); the numeric set is unchanged at
35
+ 108/109. A marker no entry matches is reported as `UNRESOLVED MARKER`, never guessed.
36
+ - **`check --format sarif`** (spec §13.2): the run as a SARIF 2.1.0 log for VS Code or
37
+ any SARIF 2.1.0 viewer — one result per finding on its line, the quoted passage in
38
+ the message of every asserting result, the exact honesty state in `properties`, and
39
+ the run's coverage in `runs[0].properties`. `--out FILE` writes the same document.
40
+ Validated against the schemastore schema, vendored in
41
+ `tests/data/sarif-schema-2.1.0.json`; not yet exercised against GitHub code scanning.
42
+ - **Resolution and retraction cache** (cache schema v3): `resolutions` (30-day TTL,
43
+ keyed by the marker-free folded entry) and `retractions` (30 days for a notice, 7
44
+ for its absence). Neither an `UNVERIFIED (provider unavailable)` nor a retraction
45
+ check every provider failed is ever stored. A warm re-run of the seven-reference
46
+ draft: **1.35 s** wall clock, 0 network calls (v0.1: 15.8 s). `proofpath cache`
47
+ counts both tables.
48
+ - The §7.1 aggregate line `skipped N source(s) because the browser was not permitted`
49
+ is printed by `check` too (terminal and markdown), never dropped by `-q`.
50
+ - `browser_binary_present()`: the consent gate checks for a chromium build under
51
+ `PLAYWRIGHT_BROWSERS_PATH` or the platform default, so a half-installed environment
52
+ runs the idempotent installer instead of failing inside the fetch; the consent log
53
+ gains `browser binary: found|missing`.
54
+ - `commands.py`: the shared wiring behind every mirrored verb (`resolve_reference`,
55
+ `fetch_target`, `config_*`, `cache_*`) returns result objects and never prints, so
56
+ `cli.py` and the TUI can only differ in how a result is drawn.
57
+ - Bibliography fallback for PDFs without a `References` heading (paged formats):
58
+ the last contiguous run of numbered paragraphs is read as the list. AlphaFold:
59
+ 0 → 17 references found, 97 markers now reported as unresolved instead of invisible.
60
+ - Resolver rescues: `First Last and First Last` author lists resolve instead of landing
61
+ in `AMBIGUOUS`; an arXiv id whose record agrees on author and year is accepted as
62
+ `RESOLVED (low confidence)` the way a DOI already was. Ghost set 274 rows: false-ghost
63
+ **0.0 %**, ghost recall 99.1 % (`docs/eval/2026-09-12-ghosts.md`).
64
+ - `tests/data/draft-author-year.md`, a committed author-year draft with an offline
65
+ smoke test; live: 6 citations paired, 0 unresolved.
66
+
67
+ ### Changed
68
+ - Cache schema **v3** (migrated in place from v1/v2; versions compared as integers).
69
+ - `Claims.unsupported` now means "a style this version cannot pair" and is empty by
70
+ construction; `UNSUPPORTED CITATION STYLE` is left for footnote-only and
71
+ superscript-letter styles. Markers that pair with nothing are `unresolved`.
72
+ - `resolve.looks_unindexed` reads only the initials-path author patterns: a full-name
73
+ list (`First Last and First Last`) no longer counts as "this entry printed an
74
+ author list", so a title-first book keeps its title (see Known issues for the price).
75
+ - `--format sarif` on `resolve` or `fetch` now says `applies to check only` instead of
76
+ naming a future version.
77
+ - Stage row `Claims` reads `N citations, M unresolved` (was `M unsupported`).
78
+
79
+ ### Fixed
80
+ - A provider answering with a non-JSON body (a bot wall or maintenance page under a
81
+ 200) escaped `resolve.py` as a raw `JSONDecodeError` traceback. Every provider body
82
+ is decoded in one place and that failure is a `ProviderError`, so `resolve`, `fetch`
83
+ and `check` report `UNVERIFIED (provider unavailable)` and never read it as evidence
84
+ the work does not exist.
85
+ - The §7.1 prompt said `blocked this request (HTTP 200)` for the empty-body bot wall;
86
+ it now says `answered without readable text (HTTP 200)`.
87
+ - `resolve` on the CLI leaked one HTTP client per invocation; `commands.resolve_reference`
88
+ owns and closes it.
89
+ - A retraction check every provider failed was recorded as "not retracted". It now
90
+ raises, is reported as `retraction check unavailable` and counted in the stage
91
+ summary (`1 unavailable`), and nothing is cached, so the next run asks again
92
+ (product rule 2). Found alongside it: the fetching stage replaced a source's notes
93
+ instead of appending, which would have dropped that very note.
94
+ - Versions in the cache file compared as strings (`"10" < "9"`); compared as integers now.
95
+
96
+ ### Known issues
97
+ - `api_calls` in the footer counts LLM calls only (none yet), so a cold run that spent
98
+ 30 s on Crossref and Semantic Scholar still prints `0 API calls`; the provider lookups
99
+ are shown on the stage lines instead.
100
+ - A cached resolution reprints the notes it was stored with (for example an `openalex
101
+ unavailable (HTTP 429)` from the day it was resolved) as if they were current.
102
+ - `proofpath check -` names its SARIF artifact `-`; give the draft a file name when the
103
+ log is meant for a viewer.
104
+ - Author-year pairing matches the first author and the exact year; `(Lindqvist, 2019)`
105
+ against "Okafor, C. and Lindqvist, S." and a year off by one are reported unresolved,
106
+ and two surnames sharing a last word (`Berg` / `van der Berg`) are reported ambiguous
107
+ rather than guessed. `Smith 2020` with no comma is not a marker; `(WHO, 2020)` does
108
+ not pair with "World Health Organization".
109
+ - An unnumbered two-column bibliography is cut at line breaks (RoBERTa: 103 entries
110
+ for ~50), so author-year pairing over such a list resolves few items. The largest
111
+ open item for author-year pairing.
112
+ - The headless bibliography fallback takes only the *last* run of numbered paragraphs
113
+ (AlphaFold: 17 of 84 entries) and drops the prose printed before a block's first entry.
114
+ - A fabricated `First Last and First Last. Title. Venue.` with no year between the
115
+ names and the title reaches `UNVERIFIED (not in bibliographic indexes)`, not `GHOST`
116
+ (the price of the rescue above; rule 3 outranks recall). A proceedings-*volume*
117
+ record can accept a fabricated paper cited into that volume as `RESOLVED (low
118
+ confidence)` — the ghost set's one fabricated acceptance (0.9 %).
119
+ - A cold run of a long bibliography is still serial through the providers (129
120
+ references: minutes); only the TUI's concurrent scheduler runs them in parallel.
121
+ - In the TUI the `loading models …` note is drawn after the `Verifying` row it precedes
122
+ (it is emitted inside that stage).
123
+ - A `#n` run reference in the TUI is a click target that folds its block, not a
124
+ hyperlink (spec §13.1); a run number has no address to open.
125
+ - A provider body that is valid JSON but not an object (a bare list or string) still
126
+ escapes `resolve` as an error, exit 2, rather than being reported as `UNVERIFIED
127
+ (provider unavailable)`.
128
+ - `/quit` in the TUI waits for an in-flight mirrored `/fetch` to finish before the
129
+ app exits; a `/check` run is cancelled, a `/fetch` is not.
130
+
131
+ ## [0.1.0] - 2026-09-12
132
+
133
+ First working release: `proofpath check` verifies a document's citations end to end
134
+ and writes a report that states its own coverage.
135
+
136
+ ### Added
137
+ - `proofpath check TARGET` as the v0.1 surface: compiler-style diagnostics, a
138
+ markdown `report.md`, `--format json`, `-q`, and exit codes `0` / `1` / `2`.
139
+ - Confidence tiers calibrated on SciFact dev rather than chosen by hand
140
+ (`decide=0.45`, `medium=0.457948`, `high=0.99933`;
141
+ `docs/eval/2026-09-12-tiers.md`), shipped as `pipeline.DEFAULT_THRESHOLDS`.
142
+ - Nine live user-like runs recorded in `docs/eval/2026-09-12-v0.1-live.md`, and
143
+ `scripts/zero_network_check.py`, which guards both HTTP clients and re-verifies a
144
+ document: with the network gone the fetch ladder makes no attempt at all, because
145
+ resolution produces no identifiers to fetch with.
146
+ - Config and permissions module: `config.toml` under the platform config dir,
147
+ `proofpath config` / `proofpath config set permissions.<key>`, and the rule that an
148
+ `ask` permission without a TTY resolves to `deny` and is reported (spec §7.1).
149
+ - Core types (`Verdict` cannot be `SUPPORTED`/`REFUTED` without a passage), device
150
+ selection (CUDA → CoreML → CPU), sentence retrieval over `fastembed` embeddings
151
+ scanned with numpy, ONNX NLI entailment on `cross-encoder/nli-deberta-v3-base`, and the aggregation
152
+ pipeline.
153
+ - Reference resolution (spec §8): Crossref + Semantic Scholar first, then arXiv,
154
+ Open Library and OpenAlex before any ghost call; identity decided only by
155
+ field agreement against the raw string; DOI / arXiv id resolved directly;
156
+ retraction check via Crossref's Retraction Watch data and OpenAlex; new state
157
+ `UNVERIFIED (not in bibliographic indexes)`; per-host throttling, `Retry-After`,
158
+ OpenAlex daily-budget handling. `proofpath resolve "<reference>"` on the CLI.
159
+ Hand-built ghost set (106 real, 100 fabricated, 20 mutated) and
160
+ `scripts/eval_ghosts.py`: false-ghost rate 0 %.
161
+ - Persistent cache: one plain SQLite file (`sources`, `raw_text` with 7-day TTL,
162
+ `chunks` with float32 embeddings, `verdicts`), a schema `CHECK` that refuses an
163
+ asserted verdict without a passage, and `proofpath cache` / `cache path` / `ls` /
164
+ `show` / `clear [--expired]`.
165
+ - Numeric claim layer (spec §10): percentages, factors and unit counts with
166
+ direction are compared before NLI; an unambiguous contradiction is refuted by
167
+ rule with both figures named (`Verdict.reason`). Conservative by design: one
168
+ comparable figure on each side, change never against level.
169
+ - Judge settings (`[judge]` in config, Groq default) with `proofpath config check`
170
+ and `proofpath config set judge.<key>`; API key resolved from the environment or
171
+ `.env`, never stored or printed. `.env.example` added.
172
+ - SciFact loader pinned to the AI2 tarball by sha256, evaluation metrics, and
173
+ `scripts/eval_scifact.py`. First measured result: dev accuracy 0.606 vs 0.406
174
+ trivial baseline (`docs/eval/2026-09-11-scifact-dev.md`).
175
+
176
+ - Fetch ladder (spec §7): `httpx` → `curl_cffi` TLS impersonation → browser engine
177
+ behind the §7.1 consent prompt → Wayback Machine; `robots.txt` via `protego`;
178
+ content type from headers; per-host throttling and backoff shared in `polite.py`;
179
+ fetched text cached with the 7-day TTL. Distinct honesty states for blocked,
180
+ blocked-by-robots, browser-not-permitted, unreachable, provider-unavailable and
181
+ network-denied — never collapsed.
182
+ - Consent gate for the ~280 MB browser engine: asks at most once per run, never
183
+ without a TTY, `always`/`never` persist to config, installs with `pip` (or `uv`)
184
+ and `scrapling install`, and reports how many sources were skipped.
185
+ - Open-access chain: Semantic Scholar → Crossref TDM links → Unpaywall (only with a
186
+ contact address) → Europe PMC → arXiv → landing page → abstract (OpenAlex last);
187
+ abstract-only results labelled `LOW CONFIDENCE (abstract only)`. DataCite arXiv
188
+ DOIs resolve straight to the arXiv PDF. `proofpath fetch <url|doi|arXiv id>`.
189
+ - `scripts/eval_coverage.py`: measured 72 % full text / 18 % abstract / 10 % none
190
+ on 50 DOIs (`docs/eval/2026-09-11-coverage.md`).
191
+
192
+ - Document ingest and claim extraction (spec §9): `document.py` value types with
193
+ page/line locators; `ingest.py` for PDF (pymupdf blocks, per-page line numbers,
194
+ running header/footer removal, superscript citations), docx (paragraphs and tables),
195
+ markdown and plain text, with the bibliography kept as raw strings, every
196
+ unparseable page reported as a `PageError` and a page holding an image and no text
197
+ reported as a scan rather than passed on as an empty page; `claims.py` pairs numeric
198
+ markers (`[12]`, `[12,15]`, `[12-15]`) with their sentence, applies the
199
+ `PARAGRAPH-SCOPED` rule, reports author-year markers as `UNSUPPORTED CITATION STYLE`
200
+ and any number the bibliography does not print as unresolved. Hand-built pairing set
201
+ (61 passages, rate 0.98) and `scripts/eval_pairing.py`; four real documents (three
202
+ PDFs and one extracted text) measured in `docs/eval/2026-09-11-pairing.md`.
203
+
204
+ - `proofpath check` (spec §9, §13.2, §15): one `verify()` entry point built as
205
+ `prepare()` (parse, claims, resolve, retractions, fetch) and `decide_all()` (retrieval,
206
+ numeric rule, NLI, cached per source and claim); compiler-style diagnostics with the
207
+ quoted passage, the confidence tier and the exact honesty state; a coverage block in
208
+ every run and a "coverage is weak" line when a quarter or more of the sources could
209
+ not be read; `report.md` written by default, `--format json`, `-q`, `--out`, `check -`
210
+ for stdin, a real Ctrl-C that keeps what was decided (exit 2). A second run of the
211
+ same document re-decides nothing: chunks and verdicts come from the cache and neither
212
+ model scores again, although the models are still loaded and reference resolution and
213
+ the retraction check still go to the network. Cache schema v2: chunks carry the text
214
+ digest they were cut from and a source's verdicts are dropped when its text changes.
215
+ `permissions.network = deny` now also skips reference resolution and the retraction
216
+ check, each reported as not attempted.
217
+
218
+ ### Changed
219
+ - CLI surface (spec §13.3): `permissions` and `judge` groups replaced by `config`
220
+ (`config` / `show` / `path` / `set SECTION.KEY VALUE` / `check`); global
221
+ `--no-color` and `-q`; one `ui.py` layer over `rich` owns every colour and the
222
+ 10-column key/value layout; `resolve --format json`; "provider unavailable"
223
+ exits `1` (a finding), no longer `2`. A resolved but retracted reference and a
224
+ URL that was reached but yielded no text (`reached but no text extracted`) are
225
+ findings too (`1`). `skipped N source(s)` counts sources, not the URLs tried for
226
+ them; `--format json` adds `browser.skipped_urls`. `permissions.network` binds
227
+ the open-access providers as well as the ladder, and `ask` without a TTY is
228
+ `deny`, reported.
229
+ - Retrieval no longer depends on `sqlite-vec`: a numpy cosine scan is faster at
230
+ every measured scale and the plain SQLite file opens in any GUI.
231
+ - Spec: `PARAGRAPH-SCOPED` and `UNSUPPORTED CITATION STYLE` states, three-tier
232
+ confidence display, 7-day raw-text cache TTL, v0.1 limited to numeric citation
233
+ markers.
234
+
235
+ ### Fixed (found by the release's own live runs, `docs/eval/2026-09-12-v0.1-live.md`)
236
+ - A real reference whose author list carries a surname particle (`van der Walt`) was
237
+ called a `GHOST REFERENCE` — product rule 3. Reference resolution now understands
238
+ particles (including glued `al-`/`el-` forms), scores the title against every
239
+ title-like segment, and accepts a DOI whose record agrees on first author and year
240
+ as `RESOLVED (low confidence)` instead of a ghost. The ghost set grew to 258 rows
241
+ (`docs/eval/2026-09-12-ghosts.md`): false-ghost rate 0.0 %, fabricated recall 100 %.
242
+ - A fabricated reference in a numbered bibliography was reported as
243
+ `UNVERIFIED (not in bibliographic indexes)` instead of a ghost, because the printed
244
+ marker (`[7] `) blinded the "is this even a paper" check. The marker is stripped once
245
+ at the resolver's entry, guarded so it can never remove a year, an identifier or a
246
+ title that begins with a number.
247
+ - A markdown draft piped through `proofpath -q check -` lost its `## References`.
248
+ - The consent-gated browser step (fetch-ladder step 3) could not install itself
249
+ (`scrapling` has no `__main__`); it now runs the package's own CLI after `pip`/`uv`,
250
+ and a paywalled Cell landing page was read through it live (3,632 words).
251
+ - A piped run occasionally aborted with exit `134` from ONNX runtime teardown after
252
+ printing a complete report; the engine now releases both models explicitly and the
253
+ CLI flushes its streams before exiting (20 of 20 consecutive runs exit `1`).
254
+ - A document with citation markers but no detected bibliography printed a clean-looking
255
+ `0 %` coverage block; it now says `no bibliography was found; N citation markers could
256
+ not be checked`.
257
+
258
+ ### Known issues
259
+ - Reference resolution and the retraction check are not cached, so a warm re-run is
260
+ still a network run: a 68-reference PDF takes about 13 minutes cold and 3.7 minutes
261
+ cached, a 129-reference one 24 minutes cold. Concurrent resolution and a resolution
262
+ cache are v0.2 work.
263
+ - Two-author lists written `First Last and First Last` resolve to `AMBIGUOUS`, not
264
+ `RESOLVED`; an arXiv id whose record agrees on author and year is not yet rescued the
265
+ way a DOI is.
266
+ - `browser.is_installed()` checks that the packages import, not that a browser binary
267
+ exists; a half-installed environment skips the installer and fails inside the browser
268
+ fetch (reported as an unverified source, never a crash).
269
+
270
+ ## [0.0.1] - 2026-09-10
271
+
272
+ First release. The verification pipeline is not implemented; this reserves the name
273
+ and establishes the interface, packaging and CI that later phases build on.
274
+
275
+ ### Added
276
+ - Design specification with measured source-access data (spec §6), the fetch ladder
277
+ and its permission model (§7), and corrected reference resolution (§8).
278
+ - Phased implementation plan, ordered by risk retired rather than user-visible
279
+ progress.
280
+ - `proofpath` command. A bare invocation is a first-class entry point rather than a
281
+ help screen, which is where the TUI will attach. Exit codes are fixed: `0` clean,
282
+ `1` findings, `2` the run itself failed.
283
+ - Cross-platform CI on Linux, macOS and Windows, and PyPI publishing through trusted
284
+ publishing rather than a stored API token.
@@ -0,0 +1,275 @@
1
+ Metadata-Version: 2.5
2
+ Name: proofpath
3
+ Version: 0.2.0
4
+ Summary: Check whether the sources behind a claim actually say what the claim says.
5
+ Project-URL: Homepage, https://github.com/Yigtwxx/proofpath
6
+ Project-URL: Repository, https://github.com/Yigtwxx/proofpath
7
+ Project-URL: Issues, https://github.com/Yigtwxx/proofpath/issues
8
+ Project-URL: Changelog, https://github.com/Yigtwxx/proofpath/blob/main/CHANGELOG.md
9
+ Author: Yigit Erdogan
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: citation,cli,entailment,fact-checking,nli,rag,research-integrity,retrieval-augmented-generation
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering
22
+ Classifier: Topic :: Text Processing :: Linguistic
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: curl-cffi>=0.16.1
26
+ Requires-Dist: fastembed>=0.5
27
+ Requires-Dist: httpx>=0.28
28
+ Requires-Dist: huggingface-hub>=0.30
29
+ Requires-Dist: numpy>=1.26
30
+ Requires-Dist: onnxruntime>=1.20
31
+ Requires-Dist: orjson>=3.11
32
+ Requires-Dist: platformdirs>=4.3
33
+ Requires-Dist: protego>=0.3
34
+ Requires-Dist: pymupdf>=1.25
35
+ Requires-Dist: python-docx>=1.1
36
+ Requires-Dist: rich>=13
37
+ Requires-Dist: scrapling>=0.4.15
38
+ Requires-Dist: textual>=1.0
39
+ Requires-Dist: tokenizers>=0.20
40
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
41
+ Requires-Dist: typer>=0.15
42
+ Provides-Extra: browser
43
+ Requires-Dist: scrapling[fetchers]>=0.4.15; extra == 'browser'
44
+ Provides-Extra: dev
45
+ Requires-Dist: jsonschema>=4.23; extra == 'dev'
46
+ Requires-Dist: mypy>=1.14; extra == 'dev'
47
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
48
+ Requires-Dist: pytest-cov>=6.0; extra == 'dev'
49
+ Requires-Dist: pytest>=8.3; extra == 'dev'
50
+ Requires-Dist: respx>=0.22; extra == 'dev'
51
+ Requires-Dist: ruff>=0.9; extra == 'dev'
52
+ Provides-Extra: gpu
53
+ Requires-Dist: sentence-transformers>=3.3; extra == 'gpu'
54
+ Requires-Dist: torch>=2.5; extra == 'gpu'
55
+ Description-Content-Type: text/markdown
56
+
57
+ # proofpath
58
+
59
+ > Don't guess. Show the evidence.
60
+
61
+ `proofpath` checks whether the sources behind a claim actually say what the claim
62
+ says. Point it at a paper or a draft and it verifies every citation on three levels,
63
+ then shows you the passage behind each verdict.
64
+
65
+ 1. **Does the source exist?** — Crossref, Semantic Scholar, arXiv, Open Library, OpenAlex.
66
+ 2. **Is it still valid?** — Crossref's Retraction Watch data, and OpenAlex.
67
+ 3. **Does it support the claim?** — retrieval and entailment against the source text,
68
+ with a numeric rule that runs before the model.
69
+
70
+ Most tools stop at step 1. Step 3 is the point.
71
+
72
+ Runs locally and free: no API key, no Docker, no server. Windows, Linux, macOS.
73
+
74
+ ```bash
75
+ uv tool install proofpath
76
+ proofpath
77
+ ```
78
+
79
+ ```
80
+ ,_,
81
+ (o.o)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[PROOF]
82
+ " " proofpath v0.2.0 academic . online . coreml
83
+ paste a file path, a URL, or a claim. /help /config /quit
84
+ ```
85
+
86
+ Bare `proofpath` opens the terminal UI ([a recorded session](docs/eval/2026-09-15-v0.2-live.md)).
87
+ Paste a path and it runs; every one-shot verb is a slash command (`/check`, `/resolve`,
88
+ `/fetch`, `/config`, `/cache`), runs can be started while others are in flight and
89
+ stopped with `/cancel #n` — a stopped run keeps what it had decided — and the
90
+ coverage footer never scrolls away. Click (or press `enter` on) a finding to read the
91
+ whole quoted passage; `⧉` copies it; a finding's reference is a link to its source.
92
+ When a publisher blocks the plain fetch, the permission question is asked **inline,
93
+ under the stage that hit the wall**, with `[allow once] [always] [no] [never]`.
94
+
95
+ The same engine behind a pipe or in CI:
96
+
97
+ ```bash
98
+ proofpath check paper.pdf # report → report.md, exit 0/1/2
99
+ proofpath check draft.md --format json | jq '.coverage'
100
+ proofpath check draft.md --format sarif --out draft.sarif # any SARIF 2.1.0 viewer (VS Code's SARIF Viewer, …)
101
+ proofpath -q check - < draft.md # stdin; findings and coverage only
102
+ ```
103
+
104
+ `--format sarif` writes a SARIF 2.1.0 log: one result per finding on its line, the
105
+ quoted passage in the message, the exact honesty state in `properties`, and the run's
106
+ coverage in the run's properties, so a log opened without the terminal still says how
107
+ much was read. Exit codes are the interface: `0` clean, `1` findings (every
108
+ `UNVERIFIED` and `LOW CONFIDENCE` counts), `2` the run itself failed — no text
109
+ parsing needed to gate a job. Piped or in CI there is **no prompt**: an `ask`
110
+ permission is treated as `deny` and reported.
111
+
112
+ The install carries **no browser engine**. When a publisher blocks the plain fetch,
113
+ proofpath asks once before downloading one — about 280 MB, into its own environment
114
+ and the shared browser cache, never system-wide — and remembers the answer.
115
+ `proofpath config set permissions.install_browser never` stops it asking at all.
116
+
117
+ ## Honesty
118
+
119
+ **Most verdicts are "not enough information", and that is the honest answer.** On
120
+ SciFact dev only **135 of 340** claim–source pairs score above the decision threshold
121
+ at all; the other 60 % are `NEI` rather than guessed. Full text is openly reachable
122
+ for well under half of published citations, so a real document loses more on top.
123
+
124
+ **Every report states its own coverage** (and adds a warning line when it is thin):
125
+
126
+ ```
127
+ 7 refs: 1 ghost, 2 unsupported, 4 ok
128
+ fulltext 72%
129
+ abstract 14%
130
+ unverified 14%
131
+ ```
132
+
133
+ When a quarter or more of the sources could not be read, a further line says so:
134
+ `coverage is weak: unread sources may hold more, so this is a lower bound`. When the
135
+ browser step was not permitted, a line counts the sources it cost:
136
+ `skipped N source(s) because the browser was not permitted`.
137
+
138
+ **No verdict without its passage.** `SUPPORTED` and `REFUTED` cannot exist without
139
+ the quoted sentence they rest on — the cache schema itself refuses to store one.
140
+
141
+ **The confidence tiers are measured, not chosen** — read off a sweep on SciFact dev
142
+ ([details](docs/eval/2026-09-12-tiers.md)): `decide = 0.45`, `medium = 0.457948`,
143
+ `high = 0.99933`. Two caveats belong next to those numbers. `high` was fitted on the
144
+ same split it is reported on: an in-sample point estimate over **21 verdicts** (at
145
+ least 18 correct — roughly 0.65–0.95 at 95 % confidence), so read it as "the model
146
+ was near-certain here", not as a guarantee of 85 % precision. And `medium` lands
147
+ almost exactly on `decide`, so `low` is practically empty among asserted verdicts —
148
+ the display is effectively **two tiers**: near-certain, and asserted at all.
149
+
150
+ ## Citations it reads
151
+
152
+ Numeric markers — `[12]`, `[12,15]`, `[12-15]`, Nature-style superscripts — and, since
153
+ v0.2, **author-year**: `(Smith et al., 2020)`, `Smith (2020)`, `(Smith, 2020; Jones,
154
+ 2019)`, `2020a`/`2020b` collisions, `ibid.` and `op. cit.`, and a mixed
155
+ `(Smith, 2020; [12])`. Pairing rate on the hand-built author-year set: **0.940**
156
+ (83 expectations over 55 passages, [details](docs/eval/2026-09-12-pairing-author-year.md));
157
+ the numeric set is at 108 of 109. Still unpaired, and reported rather than guessed:
158
+ a surname that is not the entry's *first* author, a year off by one, two surnames
159
+ sharing a last word (`Berg` / `van der Berg`), `Smith 2020` with no comma, and an
160
+ initialism such as `(WHO, 2020)` against "World Health Organization". A marker no
161
+ entry matches is listed as an unresolved marker.
162
+
163
+ ## What the states mean
164
+
165
+ Absence of evidence is never reported as evidence of absence. Each of these is a
166
+ distinct, printed state (spec §15), never collapsed into a verdict:
167
+
168
+ | State | Cause |
169
+ |---|---|
170
+ | `LOW CONFIDENCE (abstract only)` | full text unavailable, abstract used |
171
+ | `UNVERIFIED (blocked)` | 403 or bot protection |
172
+ | `UNVERIFIED (blocked, robots.txt)` | the site's `robots.txt` disallows the fetch |
173
+ | `UNVERIFIED (blocked, browser not permitted)` | steps 1–2 blocked and the browser consent was denied or impossible |
174
+ | `UNVERIFIED (unreachable)` | dead link, Wayback miss |
175
+ | `UNVERIFIED (reached, no text extracted)` | 200 answered, nothing readable came back |
176
+ | `UNVERIFIED (network not permitted)` | `permissions.network = deny` |
177
+ | `UNVERIFIED (provider unavailable)` | API down, rate limited after backoff, or answering with a page instead of a record |
178
+ | `UNVERIFIED (not in bibliographic indexes)` | web page, blog, report — indexes do not cover it, so absence proves nothing |
179
+ | `AMBIGUOUS` | several plausible records, all listed |
180
+ | `NEI` | the source was read and neither supports nor contradicts |
181
+ | `PARAGRAPH-SCOPED` | the citation covers a paragraph; each sentence is judged separately |
182
+ | `UNSUPPORTED CITATION STYLE` | reserved; no detected style produces it in v0.2 |
183
+
184
+ An earlier build sometimes aborted with `134` after printing a complete report (ONNX
185
+ runtime teardown); fixed in v0.1 — 20 of 20 piped runs exit `1`
186
+ ([live runs](docs/eval/2026-09-12-v0.1-live.md)).
187
+
188
+ ## Known limitations
189
+
190
+ - **Superscript citations only when the PDF marks them as superscript**; a PDF that
191
+ draws them as ordinary digits loses them, and `km²` can be read as `[2]`.
192
+ - **Abstract fallback**: when only an abstract is reachable the verdict is labelled
193
+ `LOW CONFIDENCE (abstract only)`. Three sentences is not a source.
194
+ - **An unnumbered two-column bibliography is cut at line breaks** (RoBERTa's ACL
195
+ list: 103 entries for ~50, half of them with no author), so author-year pairing over
196
+ such a list resolves few items — 15 claims where a rejoined list would give 65. The
197
+ largest open item for author-year pairing.
198
+ - **A reference list without a `References` heading** is read by its shape in paged
199
+ formats — the last contiguous run of numbered paragraphs — which recovers only that
200
+ last block (AlphaFold: 17 of 84 entries) and drops the prose printed before a
201
+ block's first entry.
202
+ - **The 0 % false-ghost rate is measured on 274 hand-built references**, and rule 3
203
+ is paid for in recall: a fabricated `First Last and First Last. Title. Venue.` with
204
+ no year between the names and the title is reported as `not in bibliographic
205
+ indexes`, not as a ghost, and a proceedings-*volume* record can accept a fabricated
206
+ paper cited into that volume as `RESOLVED (low confidence)` — the set's one
207
+ fabricated acceptance (0.9 %) ([details](docs/eval/2026-09-12-ghosts.md)).
208
+ - **A cold run of a long bibliography is still serial** through the providers (129
209
+ references: minutes). Only the TUI runs several documents' network stages at once.
210
+ - **A refused or failed browser install is reported, not hidden**: the source is
211
+ `UNVERIFIED (blocked, browser not permitted)` with the install log, never silently
212
+ counted as unreachable.
213
+ - **Coverage is not perfectly reproducible**: two runs minutes apart can read a
214
+ different number of sources, depending on which providers answered.
215
+ - The LLM judge and `--summarize` arrive in **v0.3**.
216
+
217
+ ## Speed
218
+
219
+ Apple Silicon Mac, models already downloaded:
220
+
221
+ | document | first run | cached re-run |
222
+ |---|---|---|
223
+ | 1-page markdown draft, 7 references | 79.3 s (v0.1 cold) | **1.35 s** ([v0.2](docs/eval/2026-09-15-v0.2-live.md)) — 15.8 s in v0.1 |
224
+ | 19-page arXiv PDF, 68 references | 12 m 57 s | 3 m 42 s ([v0.1](docs/eval/2026-09-12-v0.1-live.md)) |
225
+
226
+ The **first ever** run also downloads about 250 MB of ONNX models. Since v0.2 a
227
+ cached re-run asks the network for nothing: reference resolution and the retraction
228
+ check are cached (resolutions 30 days; a retraction hit 30 days, a miss 7 days), the
229
+ fetched text for 7 days, and chunks and verdicts for as long as the text is unchanged. The models are still loaded, and any source whose text
230
+ has expired or was never read is fetched again — the PDF re-run above (v0.1) still
231
+ spent 22 s fetching and re-scored 3 of 102 claims.
232
+
233
+ ## Looking inside the cache
234
+
235
+ Everything proofpath fetches, embeds and decides lands in one plain SQLite file:
236
+
237
+ ```bash
238
+ proofpath cache # where it is, what it holds, how many lookups it remembers
239
+ proofpath cache ls # sources, chunk/verdict counts, text expiry
240
+ proofpath cache show <id> # one source's chunks and verdicts
241
+ proofpath cache clear --expired
242
+ ```
243
+
244
+ Open `proofpath cache path` in [DB Browser for SQLite](https://sqlitebrowser.org/),
245
+ TablePlus or DBeaver — plain tables, no extension. Raw publisher text expires after
246
+ 7 days; verdicts keep the passage they quote; a provider outage is never stored.
247
+ `proofpath resolve REF` and `proofpath fetch URL|DOI` run either half on its own.
248
+
249
+ ## Optional LLM judge — arrives in v0.3
250
+
251
+ Everything above runs locally. An LLM is used only at the end, as an opt-in second
252
+ opinion on low-confidence verdicts — it never sees a source document and cannot
253
+ change a verdict. The settings exist today (`proofpath config check` proves a key
254
+ works, default Groq); `check --judge` and `--summarize` are wired in v0.3. The key
255
+ comes from the environment or a `.env` file, never from config, and is never printed.
256
+
257
+ ## Measured
258
+
259
+ | What | Set | Result |
260
+ |---|---|---|
261
+ | Retrieval + entailment | SciFact dev, 340 pairs | 0.609 accuracy, 0.597 macro-F1, against a 0.406 trivial baseline ([details](docs/eval/2026-09-12-scifact-dev.md)) |
262
+ | Reference resolution | hand-built ghost set, 274 references | 0.0 % false-ghost, 99.1 % ghost recall ([details](docs/eval/2026-09-12-ghosts.md)) |
263
+ | 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 |
264
+ | Citation pairing, numeric | 61 hand-built passages | 0.99 ([details](docs/eval/2026-09-11-pairing.md)) |
265
+ | Citation pairing, author-year | 55 hand-built passages, 83 expectations | 0.940 ([details](docs/eval/2026-09-12-pairing-author-year.md)) |
266
+
267
+ Published SciFact results sit around 70–75 F1, not 95. Nothing is tuned on a test
268
+ split, and no number is quoted without the run that produced it.
269
+
270
+ - [Design specification](docs/superpowers/specs/2026-09-10-proofpath-design.md) — what it does and the measurements behind each decision
271
+ - [Open items](docs/superpowers/OPEN-ITEMS.md) — what is unresolved, and what has not been verified yet · [Changelog](CHANGELOG.md)
272
+
273
+ ## License
274
+
275
+ MIT