proofpath 0.2.0__tar.gz → 0.2.1__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 (174) hide show
  1. {proofpath-0.2.0 → proofpath-0.2.1}/CHANGELOG.md +41 -0
  2. {proofpath-0.2.0 → proofpath-0.2.1}/PKG-INFO +38 -11
  3. {proofpath-0.2.0 → proofpath-0.2.1}/README.md +37 -10
  4. proofpath-0.2.1/docs/eval/2026-09-15-tui-v2-live.md +267 -0
  5. proofpath-0.2.1/docs/eval/tui-v2-plain.svg +279 -0
  6. proofpath-0.2.1/docs/eval/tui-v2-rich.svg +280 -0
  7. {proofpath-0.2.0 → proofpath-0.2.1}/docs/superpowers/OPEN-ITEMS.md +26 -1
  8. {proofpath-0.2.0 → proofpath-0.2.1}/docs/superpowers/specs/2026-09-10-proofpath-design.md +19 -0
  9. proofpath-0.2.1/docs/superpowers/specs/2026-09-15-tui-v2-design.md +142 -0
  10. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/__init__.py +1 -1
  11. proofpath-0.2.1/src/proofpath/tui/app.py +681 -0
  12. proofpath-0.2.1/src/proofpath/tui/pet.py +309 -0
  13. proofpath-0.2.1/src/proofpath/tui/theme.py +259 -0
  14. proofpath-0.2.1/src/proofpath/tui/verbs.py +328 -0
  15. proofpath-0.2.1/src/proofpath/tui/widgets/__init__.py +47 -0
  16. proofpath-0.2.1/src/proofpath/tui/widgets/_shared.py +96 -0
  17. proofpath-0.2.1/src/proofpath/tui/widgets/banner.py +194 -0
  18. proofpath-0.2.1/src/proofpath/tui/widgets/finding.py +341 -0
  19. proofpath-0.2.1/src/proofpath/tui/widgets/footer.py +162 -0
  20. proofpath-0.2.1/src/proofpath/tui/widgets/prompt.py +109 -0
  21. proofpath-0.2.1/src/proofpath/tui/widgets/run_block.py +565 -0
  22. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/ui.py +13 -2
  23. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_tui_app.py +21 -5
  24. proofpath-0.2.1/tests/test_tui_pet.py +483 -0
  25. proofpath-0.2.1/tests/test_tui_rich.py +679 -0
  26. proofpath-0.2.1/tests/test_tui_theme.py +319 -0
  27. proofpath-0.2.0/src/proofpath/tui/app.py +0 -1813
  28. {proofpath-0.2.0 → proofpath-0.2.1}/.editorconfig +0 -0
  29. {proofpath-0.2.0 → proofpath-0.2.1}/.env.example +0 -0
  30. {proofpath-0.2.0 → proofpath-0.2.1}/.gitattributes +0 -0
  31. {proofpath-0.2.0 → proofpath-0.2.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  32. {proofpath-0.2.0 → proofpath-0.2.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  33. {proofpath-0.2.0 → proofpath-0.2.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  34. {proofpath-0.2.0 → proofpath-0.2.1}/.github/ISSUE_TEMPLATE/wrong_verdict.yml +0 -0
  35. {proofpath-0.2.0 → proofpath-0.2.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  36. {proofpath-0.2.0 → proofpath-0.2.1}/.github/workflows/ci.yml +0 -0
  37. {proofpath-0.2.0 → proofpath-0.2.1}/.github/workflows/release.yml +0 -0
  38. {proofpath-0.2.0 → proofpath-0.2.1}/.gitignore +0 -0
  39. {proofpath-0.2.0 → proofpath-0.2.1}/CLAUDE.md +0 -0
  40. {proofpath-0.2.0 → proofpath-0.2.1}/CONTRIBUTING.md +0 -0
  41. {proofpath-0.2.0 → proofpath-0.2.1}/LICENSE +0 -0
  42. {proofpath-0.2.0 → proofpath-0.2.1}/SECURITY.md +0 -0
  43. {proofpath-0.2.0 → proofpath-0.2.1}/docs/eval/2026-09-11-coverage.md +0 -0
  44. {proofpath-0.2.0 → proofpath-0.2.1}/docs/eval/2026-09-11-ghosts.md +0 -0
  45. {proofpath-0.2.0 → proofpath-0.2.1}/docs/eval/2026-09-11-pairing.md +0 -0
  46. {proofpath-0.2.0 → proofpath-0.2.1}/docs/eval/2026-09-11-scifact-dev.md +0 -0
  47. {proofpath-0.2.0 → proofpath-0.2.1}/docs/eval/2026-09-12-ghosts.md +0 -0
  48. {proofpath-0.2.0 → proofpath-0.2.1}/docs/eval/2026-09-12-pairing-author-year.md +0 -0
  49. {proofpath-0.2.0 → proofpath-0.2.1}/docs/eval/2026-09-12-scifact-dev.md +0 -0
  50. {proofpath-0.2.0 → proofpath-0.2.1}/docs/eval/2026-09-12-tiers.md +0 -0
  51. {proofpath-0.2.0 → proofpath-0.2.1}/docs/eval/2026-09-12-v0.1-live.md +0 -0
  52. {proofpath-0.2.0 → proofpath-0.2.1}/docs/eval/2026-09-15-v0.2-live.md +0 -0
  53. {proofpath-0.2.0 → proofpath-0.2.1}/docs/research/2026-09-11-free-llm-api-tiers.md +0 -0
  54. {proofpath-0.2.0 → proofpath-0.2.1}/docs/superpowers/plans/2026-09-10-proofpath-implementation-plan.md +0 -0
  55. {proofpath-0.2.0 → proofpath-0.2.1}/docs/superpowers/plans/2026-09-12-phases-9-10-plan.md +0 -0
  56. {proofpath-0.2.0 → proofpath-0.2.1}/pyproject.toml +0 -0
  57. {proofpath-0.2.0 → proofpath-0.2.1}/scripts/eval_coverage.py +0 -0
  58. {proofpath-0.2.0 → proofpath-0.2.1}/scripts/eval_ghosts.py +0 -0
  59. {proofpath-0.2.0 → proofpath-0.2.1}/scripts/eval_pairing.py +0 -0
  60. {proofpath-0.2.0 → proofpath-0.2.1}/scripts/eval_scifact.py +0 -0
  61. {proofpath-0.2.0 → proofpath-0.2.1}/scripts/zero_network_check.py +0 -0
  62. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/browser.py +0 -0
  63. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/cache.py +0 -0
  64. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/claims.py +0 -0
  65. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/cli.py +0 -0
  66. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/commands.py +0 -0
  67. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/config.py +0 -0
  68. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/device.py +0 -0
  69. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/document.py +0 -0
  70. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/entailment.py +0 -0
  71. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/eval/__init__.py +0 -0
  72. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/eval/metrics.py +0 -0
  73. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/eval/scifact.py +0 -0
  74. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/events.py +0 -0
  75. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/fetch.py +0 -0
  76. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/ingest.py +0 -0
  77. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/judge.py +0 -0
  78. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/models.py +0 -0
  79. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/numerics.py +0 -0
  80. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/oa.py +0 -0
  81. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/paths.py +0 -0
  82. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/pipeline.py +0 -0
  83. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/polite.py +0 -0
  84. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/py.typed +0 -0
  85. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/report.py +0 -0
  86. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/resolve.py +0 -0
  87. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/retrieval.py +0 -0
  88. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/sarif.py +0 -0
  89. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/tui/__init__.py +0 -0
  90. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/tui/banner.py +0 -0
  91. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/tui/commands.py +0 -0
  92. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/tui/runs.py +0 -0
  93. {proofpath-0.2.0 → proofpath-0.2.1}/src/proofpath/verify.py +0 -0
  94. {proofpath-0.2.0 → proofpath-0.2.1}/tests/__init__.py +0 -0
  95. {proofpath-0.2.0 → proofpath-0.2.1}/tests/conftest.py +0 -0
  96. {proofpath-0.2.0 → proofpath-0.2.1}/tests/data/draft-author-year.md +0 -0
  97. {proofpath-0.2.0 → proofpath-0.2.1}/tests/data/draft-live.md +0 -0
  98. {proofpath-0.2.0 → proofpath-0.2.1}/tests/data/ghost_set.jsonl +0 -0
  99. {proofpath-0.2.0 → proofpath-0.2.1}/tests/data/pairing_author_year.jsonl +0 -0
  100. {proofpath-0.2.0 → proofpath-0.2.1}/tests/data/pairing_set.jsonl +0 -0
  101. {proofpath-0.2.0 → proofpath-0.2.1}/tests/data/sarif-schema-2.1.0.json +0 -0
  102. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fakes.py +0 -0
  103. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/crossref_work.json +0 -0
  104. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/crossref_work_no_abstract.json +0 -0
  105. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/europepmc_fulltext.xml +0 -0
  106. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/europepmc_search.json +0 -0
  107. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/europepmc_search_empty.json +0 -0
  108. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/europepmc_search_not_in_epmc.json +0 -0
  109. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/openalex_work.json +0 -0
  110. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/s2_paper.json +0 -0
  111. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/s2_paper_no_abstract.json +0 -0
  112. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/unpaywall.json +0 -0
  113. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/unpaywall_closed.json +0 -0
  114. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/oa/unpaywall_landing_only.json +0 -0
  115. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/arxiv_id_roberta.xml +0 -0
  116. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/arxiv_title_fabricated.xml +0 -0
  117. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/arxiv_title_roberta.xml +0 -0
  118. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/crossref_alphafold.json +0 -0
  119. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/crossref_fabricated.json +0 -0
  120. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/crossref_numpy.json +0 -0
  121. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/crossref_rag.json +0 -0
  122. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/crossref_retracted_wakefield.json +0 -0
  123. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/crossref_roberta.json +0 -0
  124. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/crossref_work_alphafold.json +0 -0
  125. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/crossref_work_numpy.json +0 -0
  126. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/openalex_alphafold.json +0 -0
  127. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/openalex_fabricated.json +0 -0
  128. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/openalex_rag.json +0 -0
  129. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/openalex_retracted_wakefield.json +0 -0
  130. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/openalex_roberta.json +0 -0
  131. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/openlibrary_fabricated.json +0 -0
  132. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/openlibrary_using_language.json +0 -0
  133. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/s2_alphafold.json +0 -0
  134. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/s2_fabricated.json +0 -0
  135. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/s2_rag.json +0 -0
  136. {proofpath-0.2.0 → proofpath-0.2.1}/tests/fixtures/resolve/s2_roberta.json +0 -0
  137. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_browser.py +0 -0
  138. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_cache.py +0 -0
  139. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_cache_cli.py +0 -0
  140. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_check_cli.py +0 -0
  141. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_claims.py +0 -0
  142. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_cli.py +0 -0
  143. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_commands.py +0 -0
  144. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_config.py +0 -0
  145. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_config_cli.py +0 -0
  146. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_device.py +0 -0
  147. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_document.py +0 -0
  148. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_entailment.py +0 -0
  149. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_eval_coverage.py +0 -0
  150. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_eval_pairing.py +0 -0
  151. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_eval_scifact.py +0 -0
  152. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_fetch.py +0 -0
  153. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_fetch_cli.py +0 -0
  154. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_ingest.py +0 -0
  155. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_judge.py +0 -0
  156. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_metrics.py +0 -0
  157. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_models.py +0 -0
  158. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_numerics.py +0 -0
  159. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_oa.py +0 -0
  160. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_pipeline.py +0 -0
  161. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_polite.py +0 -0
  162. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_report.py +0 -0
  163. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_resolve.py +0 -0
  164. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_resolve_cli.py +0 -0
  165. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_retrieval.py +0 -0
  166. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_sarif.py +0 -0
  167. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_scifact.py +0 -0
  168. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_tui_banner.py +0 -0
  169. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_tui_commands.py +0 -0
  170. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_tui_runs.py +0 -0
  171. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_ui.py +0 -0
  172. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_verify.py +0 -0
  173. {proofpath-0.2.0 → proofpath-0.2.1}/tests/test_version.py +0 -0
  174. {proofpath-0.2.0 → proofpath-0.2.1}/uv.lock +0 -0
@@ -6,6 +6,47 @@ All notable changes to this project are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.1] - 2026-09-15
10
+
11
+ The TUI's second look. No behaviour change: every state word, every honesty sentence,
12
+ the exit codes, the scheduler and the one-shot CLI are exactly v0.2.0's. Design in
13
+ `docs/superpowers/specs/2026-09-15-tui-v2-design.md`; the by-hand session in both
14
+ themes, with SVG screenshots, in `docs/eval/2026-09-15-tui-v2-live.md`.
15
+
16
+ ### Changed
17
+ - **Two themes, one truth** (`tui/theme.py`). `rich` draws box borders, Unicode glyphs
18
+ and truecolor tones when the terminal gives evidence of them (`COLORTERM`, Windows
19
+ Terminal, iTerm2, kitty, WezTerm, Ghostty, VS Code, Terminal.app); `plain` is a pure-ASCII, ANSI-16 look
20
+ (v0.2.0's `⏺ ✗ ⚠ › ⧉` become `* x ! > [copy]`, and findings now print their `= note:`
21
+ lines as the CLI does), chosen under `NO_COLOR`, `--no-color`, `-q`,
22
+ `TERM=dumb`, legacy conhost, a CJK locale, or any session without truecolor evidence (an SSH
23
+ or tmux session that strips `COLORTERM` gets `plain`). `PROOFPATH_THEME=rich|plain` overrides
24
+ detection. The meaning colours stay `ui.py`'s tables; a theme changes how a meaning
25
+ looks, never what a word means. Spec §13.1's "pure ASCII" rule for the pet now binds
26
+ `plain` only.
27
+ - **The pet** (`tui/pet.py`): `rich` draws a seven-line ferret with a real tail running
28
+ to the `[PROOF]` stamp; `plain` keeps the three-line one unchanged. Same eyes, same
29
+ blink, same reactions; the tail wags in `rich` while a run works.
30
+ - **Run panels** (`tui/widgets/run_block.py`): in `rich` each run is a rounded panel in
31
+ its accent, the command and the state word on the top border, the coverage on the
32
+ bottom one; the stages are a fixed-column table (symbol, name, summary with `·`
33
+ separators, attribution, elapsed) and the active stage carries a real `▰▱` progress
34
+ bar with `done/total`. A finished stage that left something unverified keeps the
35
+ `⏺` mark instead of a tick. Below 60 columns the borders go and the flat rows
36
+ return; a run that crossed the floor either way says its coverage exactly once.
37
+ - **Findings** (`tui/widgets/finding.py`): the state word is a badge, the location a
38
+ fixed cell, the tier right-aligned; the finding's notes and the claim (`you`) and
39
+ the passage (`source`) it was checked against are printed under it. A tier-less
40
+ badge keeps its row at 60–79 columns. `plain` prints the notes as the CLI's own
41
+ `= note:` rows and keeps `[copy]`.
42
+ - **Footer** (`tui/widgets/footer.py`): `rich` draws the coverage as a proportional
43
+ `█▓░` bar in the three meaning colours over the counts line; `plain` keeps the
44
+ `kv` lines. The prompt wears a one-line rounded border in the run's accent.
45
+ - **Structure** (OPEN-ITEMS 12.4): `tui/app.py` is split — `theme.py`, `pet.py`,
46
+ `verbs.py` and `tui/widgets/` (`run_block`, `finding`, `footer`, `prompt`,
47
+ `banner`, `_shared`) — and is now 681 lines of composition, scheduler wiring and
48
+ slash commands, no rendering.
49
+
9
50
  ## [0.2.0] - 2026-09-15
10
51
 
11
52
  The interactive front-end, author-year citations, SARIF output and a cache for the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: proofpath
3
- Version: 0.2.0
3
+ Version: 0.2.1
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
@@ -77,20 +77,47 @@ proofpath
77
77
  ```
78
78
 
79
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
80
+ ╭╮ ╭╮
81
+ ╭╯╰─╯╰────────────────────────────────────────────────────────────────────╮
82
+ ╸┤ o o ╰~~~~~~~~~~~~~[PROOF]
83
+ ╰─┬─┬────────────────────────────────────────────────────────────────┬─┬──╯
84
+ ˘ ˘ ˘ ˘
85
+ proofpath v0.2.1 academic . online . coreml
86
+ paste a file path, a URL, or a claim. /help /config /quit
84
87
  ```
85
88
 
86
- Bare `proofpath` opens the terminal UI ([a recorded session](docs/eval/2026-09-15-v0.2-live.md)).
89
+ Bare `proofpath` opens the terminal UI ([a recorded session in both themes](docs/eval/2026-09-15-tui-v2-live.md),
90
+ with [SVG screenshots](docs/eval/tui-v2-rich.svg)). Each run is one panel in its own
91
+ accent: the command on the top border and the run's state at its right, a fixed-column
92
+ stage table underneath (`✓` finished, `⏺` still running or finished with something
93
+ unverified, a real progress bar on the active stage, the provider that produced each
94
+ number at the right), a rule, then the findings — location, reference, the state word
95
+ as a badge, the tier — with the finding's notes and the claim (`you`) and the passage
96
+ (`source`) it was checked against under it. The bottom border carries the run's
97
+ coverage; the docked footer draws it as a bar and never scrolls away.
98
+
87
99
  Paste a path and it runs; every one-shot verb is a slash command (`/check`, `/resolve`,
88
100
  `/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]`.
101
+ stopped with `/cancel #n` — a stopped run keeps what it had decided. Click (or press
102
+ `enter` on) a finding to read the whole quoted passage; `⧉` copies it; a finding's
103
+ reference is a link to its source. When a publisher blocks the plain fetch, the
104
+ permission question is asked **inline, under the stage that hit the wall**, with
105
+ `[allow once] [always] [no] [never]`.
106
+
107
+ **Windows / `NO_COLOR`.** The look above is the `rich` theme, chosen when the terminal
108
+ gives evidence of truecolor (`COLORTERM`, Windows Terminal, iTerm2, kitty, WezTerm,
109
+ Ghostty, VS Code, Terminal.app). Under `NO_COLOR`, `--no-color`, `-q`, `TERM=dumb`,
110
+ legacy conhost or a CJK locale the `plain` theme draws the same runs as flat ASCII rows
111
+ in the terminal's own 16 colours — the state words, the coverage and every honesty
112
+ sentence are identical, only the drawing changes. `PROOFPATH_THEME=rich|plain` forces
113
+ either, for screenshots and bug reports.
114
+
115
+ ```
116
+ ,_,
117
+ (o.o)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[PROOF]
118
+ " " proofpath v0.2.1 academic . online . coreml
119
+ paste a file path, a URL, or a claim. /help /config /quit
120
+ ```
94
121
 
95
122
  The same engine behind a pipe or in CI:
96
123
 
@@ -21,20 +21,47 @@ proofpath
21
21
  ```
22
22
 
23
23
  ```
24
- ,_,
25
- (o.o)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[PROOF]
26
- " " proofpath v0.2.0 academic . online . coreml
27
- paste a file path, a URL, or a claim. /help /config /quit
24
+ ╭╮ ╭╮
25
+ ╭╯╰─╯╰────────────────────────────────────────────────────────────────────╮
26
+ ╸┤ o o ╰~~~~~~~~~~~~~[PROOF]
27
+ ╰─┬─┬────────────────────────────────────────────────────────────────┬─┬──╯
28
+ ˘ ˘ ˘ ˘
29
+ proofpath v0.2.1 academic . online . coreml
30
+ paste a file path, a URL, or a claim. /help /config /quit
28
31
  ```
29
32
 
30
- Bare `proofpath` opens the terminal UI ([a recorded session](docs/eval/2026-09-15-v0.2-live.md)).
33
+ Bare `proofpath` opens the terminal UI ([a recorded session in both themes](docs/eval/2026-09-15-tui-v2-live.md),
34
+ with [SVG screenshots](docs/eval/tui-v2-rich.svg)). Each run is one panel in its own
35
+ accent: the command on the top border and the run's state at its right, a fixed-column
36
+ stage table underneath (`✓` finished, `⏺` still running or finished with something
37
+ unverified, a real progress bar on the active stage, the provider that produced each
38
+ number at the right), a rule, then the findings — location, reference, the state word
39
+ as a badge, the tier — with the finding's notes and the claim (`you`) and the passage
40
+ (`source`) it was checked against under it. The bottom border carries the run's
41
+ coverage; the docked footer draws it as a bar and never scrolls away.
42
+
31
43
  Paste a path and it runs; every one-shot verb is a slash command (`/check`, `/resolve`,
32
44
  `/fetch`, `/config`, `/cache`), runs can be started while others are in flight and
33
- stopped with `/cancel #n` — a stopped run keeps what it had decided and the
34
- coverage footer never scrolls away. Click (or press `enter` on) a finding to read the
35
- whole quoted passage; `⧉` copies it; a finding's reference is a link to its source.
36
- When a publisher blocks the plain fetch, the permission question is asked **inline,
37
- under the stage that hit the wall**, with `[allow once] [always] [no] [never]`.
45
+ stopped with `/cancel #n` — a stopped run keeps what it had decided. Click (or press
46
+ `enter` on) a finding to read the whole quoted passage; `⧉` copies it; a finding's
47
+ reference is a link to its source. When a publisher blocks the plain fetch, the
48
+ permission question is asked **inline, under the stage that hit the wall**, with
49
+ `[allow once] [always] [no] [never]`.
50
+
51
+ **Windows / `NO_COLOR`.** The look above is the `rich` theme, chosen when the terminal
52
+ gives evidence of truecolor (`COLORTERM`, Windows Terminal, iTerm2, kitty, WezTerm,
53
+ Ghostty, VS Code, Terminal.app). Under `NO_COLOR`, `--no-color`, `-q`, `TERM=dumb`,
54
+ legacy conhost or a CJK locale the `plain` theme draws the same runs as flat ASCII rows
55
+ in the terminal's own 16 colours — the state words, the coverage and every honesty
56
+ sentence are identical, only the drawing changes. `PROOFPATH_THEME=rich|plain` forces
57
+ either, for screenshots and bug reports.
58
+
59
+ ```
60
+ ,_,
61
+ (o.o)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[PROOF]
62
+ " " proofpath v0.2.1 academic . online . coreml
63
+ paste a file path, a URL, or a claim. /help /config /quit
64
+ ```
38
65
 
39
66
  The same engine behind a pipe or in CI:
40
67
 
@@ -0,0 +1,267 @@
1
+ # TUI v2 live session — 2026-09-15
2
+
3
+ The by-hand session the v2 design's §7 asks for: the real `uv run proofpath` in both
4
+ themes, `PROOFPATH_THEME` forced, driven in a pseudo-terminal and read back as text.
5
+ Nothing here is a fixture — this is the real `App.run()`, the real `Scheduler`, the
6
+ real engine and the warm cache. Release evidence for v0.2.1, beside
7
+ `2026-09-15-v0.2-live.md` (the v0.2.0 session of the same draft).
8
+
9
+ - machine: Darwin arm64 (Apple Silicon), `CoreMLExecutionProvider` for NLI; the
10
+ session was driven from VS Code's terminal (`TERM_PROGRAM=vscode`, `COLORTERM=truecolor`)
11
+ - pty: Python `pty.fork`, **100 × 60**, `TERM=xterm-256color`, `COLORTERM=truecolor`,
12
+ `PROOFPATH_THEME=rich` then `=plain`; the screen rendered by the `pyte` terminal
13
+ emulator (`uv run --with pyte`, not added to the project). 60 rows rather than the
14
+ 40 of the v0.2.0 session so that the whole run block — title border, stage table,
15
+ every finding, coverage border — is on one screen; a 100 × 40 `rich` run was made
16
+ first and behaved the same, with the top of the panel scrolled away
17
+ - cache: warm — the same `tests/data/draft-live.md` as the v0.2.0 session, so every
18
+ network stage is attributed to `cache` and the footer says `0 API calls`
19
+ - version: `0.2.1` (the bump was made before the recordings)
20
+ - the sequence, both themes: `/check tests/data/draft-live.md` → wait for `7 refs` →
21
+ `/help` → `/check` alone → `Esc` → `/quit`. Exit status **0** both times.
22
+
23
+ The SVG screenshots `tui-v2-rich.svg` and `tui-v2-plain.svg` are a separate pair of
24
+ runs: `ProofpathApp(load_config(), ui.build(force_terminal=True), theme=…)` under
25
+ `app.run_test(size=(100, 54))` with the **real `Scheduler`** (no fake), the same
26
+ `/check`, polled until the run was `done`, then `app.save_screenshot()` (Textual's
27
+ `export_screenshot`). The same draft, the same cache, the same rows.
28
+
29
+ ---
30
+
31
+ ## 1. `PROOFPATH_THEME=rich`
32
+
33
+ The banner on start (seven lines: the ferret, its tail to the stamp, the two text
34
+ lines) and, below the empty log, the footer before any run and the bordered prompt:
35
+
36
+ ```
37
+ ╭╮ ╭╮
38
+ ╭╯╰─╯╰────────────────────────────────────────────────────────────────────╮
39
+ ╸┤ o o ╰~~~~~~~~~~~~~[PROOF]
40
+ ╰─┬─┬────────────────────────────────────────────────────────────────┬─┬──╯
41
+ ˘ ˘ ˘ ˘
42
+ proofpath v0.2.1 academic . online . coreml
43
+ paste a file path, a URL, or a claim. /help /config /quit
44
+
45
+
46
+ no run yet · coverage is reported per run
47
+
48
+
49
+ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮
50
+ │ › paste a file path, a URL, or a claim │
51
+ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯
52
+ ```
53
+
54
+ The screen once the run was `done` — the whole panel, the docked footer with the
55
+ coverage bar, the prompt back in the run's accent:
56
+
57
+ ```
58
+ ╭╮ ╭╮
59
+ ╭╯╰─╯╰────────────────────────────────────────────────────────────────────╮
60
+ ╸┤ O O ╰~~~~~~~~~~~~~[PROOF]
61
+ ╰─┬─┬────────────────────────────────────────────────────────────────┬─┬──╯
62
+ ˘ ˘ ˘ ˘
63
+ proofpath v0.2.1 academic . online . coreml
64
+ paste a file path, a URL, or a claim. /help /config /quit
65
+ ╭─ #1 /check tests/data/draft-live.md ─────────────────────────────────────────────────── done ─╮
66
+ │ ✓ Parsing 1 pages · 7 refs text 0.0s │
67
+ │ ✓ Claims 7 citations · 0 unresolved rules 0.0s │
68
+ │ ⏺ Resolving 6 ok · 0 amb · 1 ghost cache 0.0s │
69
+ │ ✓ Retractions none cache 0.0s │
70
+ │ ✓ Fetching 5 full text · 1 abstract · 0 unverified cache 0.0s │
71
+ │ ✓ Verifying 10 claims: 1 supported · 2 not supported · 7 NEI · 10 cached coreml 0.8s │
72
+ │ loading models … │
73
+ │ ────────────────────────────────────────────────────────────────────────────────────────────── │
74
+ │ ✗ L41 [7] Marchetti, L. R., Osei, K. & Lind… GHOST REFERENCE │
75
+ │ arxiv and openlibrary consulted before the ghost call │
76
+ │ openalex unavailable (HTTP 429, retry after 65567s); best-effort, not required │
77
+ │ · L40 [6] Pearl, J. & Mackenzie, D. The B… LOW CONFIDENCE (abstract only) │
78
+ │ cache: hit │
79
+ │ ✗ L6 [1] Vaswani, A., Shazeer, N., Parmar,… NOT SUPPORTED high │
80
+ │ numeric mismatch: claim says 21 days, source says 3.5 days │
81
+ │ you "On the WMT 2014 English-to-French translation task the Transformer reached…" │
82
+ │ source "On the WMT 2014 English-to-French translation task, our model establishes …" ⧉ │
83
+ │ · L11 [2] Jumper, J., Evans, R., Pritzel, A… NEI low │
84
+ │ · L11 [2] Jumper, J., Evans, R., Pritzel, A… NEI low │
85
+ │ · L17 [3] Harris, C. R., Millman, K. J., va… NEI low │
86
+ │ · L16 [4] Virtanen, P., Gommers, R., Olipha… NEI low │
87
+ │ · L17 [4] Virtanen, P., Gommers, R., Olipha… NEI low │
88
+ │ ✗ L18 [4] Virtanen, P., Gommers, R., Olipha… NOT SUPPORTED low │
89
+ │ you "SciPy builds on it with the fundamental algorithms for scientific computin…" │
90
+ │ source "Architecture and implementation choices Project scope SciPy provides funda…" ⧉ │
91
+ │ · L23 [6] Pearl, J. & Mackenzie, D. The Boo… NEI low │
92
+ │ · L24 [6] Pearl, J. & Mackenzie, D. The Boo… NEI low │
93
+ │ · L11 citation supports a paragraph, no… PARAGRAPH-SCOPED │
94
+ │ 2 sentences: 2 NEI │
95
+ │ · L16 citation supports a paragraph, no… PARAGRAPH-SCOPED │
96
+ │ 3 sentences: 2 NEI, 1 not supported │
97
+ │ · L23 citation supports a paragraph, no… PARAGRAPH-SCOPED │
98
+ │ 2 sentences: 2 NEI │
99
+ ╰─────────────────────────────────────────────────────────────────────────── coverage 72/14/14% ─╯
100
+
101
+
102
+ ██████████████████████▓▓▓▓░░░░ █ 72% full text · ▓ 14% abstract · ░ 14% unverified
103
+ 7 refs: 1 ghost, 2 unsupported, 4 ok
104
+ no report written · 0 API calls · 0.8s
105
+
106
+ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮
107
+ │ › paste a file path, a URL, or a claim │
108
+ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯
109
+ ```
110
+
111
+ Then, in order:
112
+
113
+ - `/help`: eleven note rows under the panel, one per verb with what it wants, then
114
+ `a line that is not a command is checked as a target`:
115
+
116
+ ```
117
+ /check paste a file path or URL
118
+ /resolve paste a reference
119
+ /fetch paste a URL, DOI or arXiv id
120
+ /config
121
+ /cache
122
+ /allow once | always | no | never
123
+ /summarize
124
+ /cancel #n
125
+ /help
126
+ /quit
127
+ a line that is not a command is checked as a target
128
+ ██████████████████████▓▓▓▓░░░░ █ 72% full text · ▓ 14% abstract · ░ 14% unverified
129
+ 7 refs: 1 ghost, 2 unsupported, 4 ok
130
+ no report written · 0 API calls · 0.8s
131
+
132
+ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮
133
+ │ › paste a file path, a URL, or a claim │
134
+ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯
135
+ ```
136
+
137
+ - `/check` alone: the bar entered the awaiting mode; its placeholder changed to
138
+ **`paste a file path or URL`** inside the bordered prompt:
139
+
140
+ ```
141
+ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮
142
+ │ › paste a file path or URL │
143
+ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯
144
+ ```
145
+
146
+ - `Esc`: the placeholder went back to `paste a file path, a URL, or a claim` — the
147
+ mode was left, nothing was run.
148
+ - `/quit`: the app left the alternate screen and the process exited **0**.
149
+
150
+ ## 2. `PROOFPATH_THEME=plain`
151
+
152
+ The banner (the v0.2.0 three-line ferret, unchanged) and the flat rows:
153
+
154
+ ```
155
+ ,_,
156
+ (o.o)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[PROOF]
157
+ " " proofpath v0.2.1 academic . online . coreml
158
+ paste a file path, a URL, or a claim. /help /config /quit
159
+
160
+
161
+ no run yet · coverage is reported per run
162
+
163
+
164
+ > paste a file path, a URL, or a claim
165
+ ```
166
+
167
+ ```
168
+ ,_,
169
+ (O.O)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[PROOF]
170
+ " " proofpath v0.2.1 academic . online . coreml
171
+ paste a file path, a URL, or a claim. /help /config /quit
172
+ #1 /check tests/data/draft-live.md done coverage 72/14/14%
173
+ + Parsing 0.0s
174
+ 1 pages, 7 refs text
175
+ + Claims 0.0s
176
+ 7 citations, 0 unresolved rules
177
+ * Resolving 0.0s
178
+ 6 ok, 0 amb, 1 ghost cache
179
+ + Retractions 0.0s
180
+ none cache
181
+ + Fetching 0.0s
182
+ 5 full text, 1 abstract, 0 unverified cache
183
+ + Verifying 0.8s
184
+ 10 claims: 1 supported, 2 not supported, 7 NEI, 10 cached coreml
185
+ loading models …
186
+ --------------------------------------------------------------------------------------------------
187
+ x L41 [7] Marchetti, L. R., Osei, K. & Lind… GHOST REFERENCE
188
+ = note: arxiv and openlibrary consulted before the ghost call
189
+ = note: openalex unavailable (HTTP 429, retry after 65567s); best-effort, not required
190
+ - L40 [6] Pearl, J. & Mackenzie, D. The Boo… LOW CONFIDENCE (abstract only)
191
+ = note: cache: hit
192
+ x L6 [1] Vaswani, A., Shazeer, N., Parmar,… NOT SUPPORTED high
193
+ "On the WMT 2014 English-to-French translation task, our model establishes a new s…" [copy]
194
+ = note: numeric mismatch: claim says 21 days, source says 3.5 days
195
+ - L11 [2] Jumper, J., Evans, R., Pritzel, A… NEI low
196
+ - L11 [2] Jumper, J., Evans, R., Pritzel, A… NEI low
197
+ - L17 [3] Harris, C. R., Millman, K. J., va… NEI low
198
+ - L16 [4] Virtanen, P., Gommers, R., Olipha… NEI low
199
+ - L17 [4] Virtanen, P., Gommers, R., Olipha… NEI low
200
+ x L18 [4] Virtanen, P., Gommers, R., Olipha… NOT SUPPORTED low
201
+ "Architecture and implementation choices Project scope SciPy provides fundamental …" [copy]
202
+ - L23 [6] Pearl, J. & Mackenzie, D. The Boo… NEI low
203
+ - L24 [6] Pearl, J. & Mackenzie, D. The Boo… NEI low
204
+ - L11 citation supports a paragraph, no… PARAGRAPH-SCOPED
205
+ = note: 2 sentences: 2 NEI
206
+ - L16 citation supports a paragraph, no… PARAGRAPH-SCOPED
207
+ = note: 3 sentences: 2 NEI, 1 not supported
208
+ - L23 citation supports a paragraph, no… PARAGRAPH-SCOPED
209
+ = note: 2 sentences: 2 NEI
210
+ coverage 72/14/14%
211
+
212
+
213
+ 7 refs: 1 ghost, 2 unsupported, 4 ok coverage 72/14/14%
214
+ no report written · 0 API calls · 0.8s
215
+
216
+ > paste a file path, a URL, or a claim
217
+ ```
218
+
219
+ Then `/help` (the same eleven rows), `/check` alone (the prompt row became
220
+ `> paste a file path or URL`), `Esc` (back to the default), `/quit` (exit **0**).
221
+
222
+ ## 3. What was observed
223
+
224
+ - **Same truth in both themes.** The stage summaries, the fourteen findings, their
225
+ states and tiers, the two ghost notes, the `numeric mismatch` note, the passages and
226
+ `coverage 72/14/14%` are the v0.2.0 session's, word for word; only the drawing
227
+ differs. `RICH` writes the summaries with `·` where `PLAIN` keeps the engine's
228
+ commas (`6 ok · 0 amb · 1 ghost` / `6 ok, 0 amb, 1 ghost`).
229
+ - **The honesty flag.** `Resolving` ended with `1 ghost`, so its row keeps the active
230
+ mark — `⏺` in `RICH`, `*` in `PLAIN` — while the other five stages get `✓` / `+`.
231
+ A glance down the table finds the stage that left something unverified.
232
+ - **OPEN-ITEMS 12.6 is closed on screen**: the finding rows print the marker once
233
+ (`[7] Marchetti, L. R., …`), in both themes.
234
+ - **OPEN-ITEMS 12.7 still shows**: `loading models …` is drawn under the `Verifying`
235
+ row in both themes. True event order; unchanged by design in v2.
236
+ - **Badges and elision.** `LOW CONFIDENCE (abstract only)` is the longest badge; its
237
+ label elided to `[6] Pearl, J. & Mackenzie, D. The B…` so the badge and the empty
238
+ tier cell keep their columns. Every badge ends on one column at 100 columns.
239
+ - **`you` / `source`.** The two `NOT SUPPORTED` findings print the claim and the
240
+ passage it was checked against, both elided to the row, the copy glyph `⧉` after the
241
+ passage; `PLAIN` prints the passage with `[copy]` and the notes as `= note:` rows.
242
+ - **Coverage said once.** In `RICH` the coverage is on the panel's bottom border and
243
+ in the footer's bar; there is no `coverage …` row inside the panel (the T5 fix keeps
244
+ the row mounted but hidden while the block is a panel). In `PLAIN` the header's tail
245
+ and the block's own row both say it, as v0.2.0 did.
246
+ - **The eyes.** `o o` idle, `O O` after a run that ended with findings — the flash
247
+ was still up when the `done` screen was read (`(O.O)` in `PLAIN`).
248
+ - **Warm cache.** Every network stage `cache`, `0 API calls`, `0.8s` in Verifying.
249
+
250
+ ### How `⏺` renders
251
+
252
+ `pyte` lays cells out with `wcwidth`, so the captures above cannot show how a font
253
+ draws a glyph; they only show that every width table agrees `⏺` (U+23FA) is one cell
254
+ (`unicodedata` East Asian Width `N`, `wcwidth` 1, Rich `cell_len` 1 — and the suite's
255
+ `test_a_rich_render_uses_only_single_cell_glyphs` pins Rich's answer). What a real
256
+ terminal does was measured instead in Chromium's font fallback on this Mac, which is
257
+ what VS Code's terminal (xterm.js) draws with: a `Menlo, monospace` span at 28 px
258
+ gives `a` 18.9 px, `⏺` **19.6 px** (1.04 cells, a text glyph in the foreground
259
+ colour, not the colour emoji), `●` 18.9 px, `✓ ✗ ⚠` 18.9 px, and `⧉` **24.3 px**
260
+ (1.29 cells). So on this machine `⏺` is not drawn as a wide emoji, and the switch to
261
+ `●` that the T5 brief conditions on that was **not made**. The glyph is `Emoji=Yes`
262
+ (text-default presentation), and terminals that apply emoji presentation to such
263
+ characters — Apple Terminal with some fonts, Windows Terminal — may draw it two cells
264
+ wide in the emoji's own red; if a report says so, `Glyphs.stage_active` and
265
+ `stage_flag` in `tui/theme.py` become `●` (OPEN-ITEMS 13.2). `⧉` overflows its cell
266
+ by a third in the same fallback; it is followed by a space and the border, so nothing
267
+ is covered.