vouch-paper 0.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 (133) hide show
  1. vouch_paper-0.1/.gitattributes +3 -0
  2. vouch_paper-0.1/.github/workflows/docs.yml +43 -0
  3. vouch_paper-0.1/.github/workflows/publish.yml +38 -0
  4. vouch_paper-0.1/.gitignore +24 -0
  5. vouch_paper-0.1/.pre-commit-hooks.yaml +7 -0
  6. vouch_paper-0.1/LICENSE +21 -0
  7. vouch_paper-0.1/PKG-INFO +278 -0
  8. vouch_paper-0.1/README.md +233 -0
  9. vouch_paper-0.1/SPEC.md +1875 -0
  10. vouch_paper-0.1/docs/assets/images/README.md +33 -0
  11. vouch_paper-0.1/docs/assets/images/explore-ui-function-detail.png +0 -0
  12. vouch_paper-0.1/docs/assets/images/explore-ui-overview.png +0 -0
  13. vouch_paper-0.1/docs/assets/images/pdf-provenance-appendix.png +0 -0
  14. vouch_paper-0.1/docs/assets/images/pdf-provenance-link.png +0 -0
  15. vouch_paper-0.1/docs/assets/images/terminal-vouch-check-fail.png +0 -0
  16. vouch_paper-0.1/docs/assets/images/terminal-vouch-check-pass.png +0 -0
  17. vouch_paper-0.1/docs/cli/author.md +95 -0
  18. vouch_paper-0.1/docs/cli/check.md +81 -0
  19. vouch_paper-0.1/docs/cli/index.md +55 -0
  20. vouch_paper-0.1/docs/cli/inspect.md +135 -0
  21. vouch_paper-0.1/docs/cli/integrations.md +68 -0
  22. vouch_paper-0.1/docs/cli/setup.md +76 -0
  23. vouch_paper-0.1/docs/examples.md +61 -0
  24. vouch_paper-0.1/docs/guide/change-notification.md +78 -0
  25. vouch_paper-0.1/docs/guide/checks-and-lints.md +51 -0
  26. vouch_paper-0.1/docs/guide/concepts.md +82 -0
  27. vouch_paper-0.1/docs/guide/derived-values.md +162 -0
  28. vouch_paper-0.1/docs/guide/formatting.md +58 -0
  29. vouch_paper-0.1/docs/guide/freshness.md +93 -0
  30. vouch_paper-0.1/docs/guide/latex.md +138 -0
  31. vouch_paper-0.1/docs/guide/non-python-experiments.md +49 -0
  32. vouch_paper-0.1/docs/guide/project-layout.md +106 -0
  33. vouch_paper-0.1/docs/guide/provenance-csv.md +40 -0
  34. vouch_paper-0.1/docs/guide/recording.md +279 -0
  35. vouch_paper-0.1/docs/guide/workflow-integration.md +52 -0
  36. vouch_paper-0.1/docs/index.md +52 -0
  37. vouch_paper-0.1/docs/llm/claude-code.md +84 -0
  38. vouch_paper-0.1/docs/llm/index.md +90 -0
  39. vouch_paper-0.1/docs/llm/mcp-server.md +80 -0
  40. vouch_paper-0.1/docs/quickstart.md +81 -0
  41. vouch_paper-0.1/docs/requirements.txt +1 -0
  42. vouch_paper-0.1/docs/tutorial/01-record.md +70 -0
  43. vouch_paper-0.1/docs/tutorial/02-describe-and-find.md +52 -0
  44. vouch_paper-0.1/docs/tutorial/03-cite-and-build.md +69 -0
  45. vouch_paper-0.1/docs/tutorial/04-derived-and-tables.md +84 -0
  46. vouch_paper-0.1/docs/tutorial/05-check.md +61 -0
  47. vouch_paper-0.1/docs/tutorial/06-when-code-changes.md +89 -0
  48. vouch_paper-0.1/docs/tutorial/07-submitting-and-next.md +33 -0
  49. vouch_paper-0.1/docs/tutorial/index.md +75 -0
  50. vouch_paper-0.1/examples/minimal/models.py +36 -0
  51. vouch_paper-0.1/examples/minimal/paper/main.tex +56 -0
  52. vouch_paper-0.1/examples/minimal/train.py +32 -0
  53. vouch_paper-0.1/examples/minimal/vouch.toml +6 -0
  54. vouch_paper-0.1/examples/minimal/vouch_values.py +30 -0
  55. vouch_paper-0.1/examples/tutorial/README.md +451 -0
  56. vouch_paper-0.1/examples/tutorial/finished/experiment.py +102 -0
  57. vouch_paper-0.1/examples/tutorial/finished/paper/main.tex +69 -0
  58. vouch_paper-0.1/examples/tutorial/finished/vouch.toml +19 -0
  59. vouch_paper-0.1/examples/tutorial/finished/vouch_values.py +59 -0
  60. vouch_paper-0.1/examples/tutorial/start/experiment.py +97 -0
  61. vouch_paper-0.1/examples/tutorial/start/paper/main.tex +23 -0
  62. vouch_paper-0.1/examples/viewer-check/viewer-check.tex +28 -0
  63. vouch_paper-0.1/mkdocs.yml +89 -0
  64. vouch_paper-0.1/pyproject.toml +45 -0
  65. vouch_paper-0.1/src/vouch/__init__.py +82 -0
  66. vouch_paper-0.1/src/vouch/__main__.py +4 -0
  67. vouch_paper-0.1/src/vouch/_entry.py +48 -0
  68. vouch_paper-0.1/src/vouch/_version.py +24 -0
  69. vouch_paper-0.1/src/vouch/agents.py +214 -0
  70. vouch_paper-0.1/src/vouch/api.py +1096 -0
  71. vouch_paper-0.1/src/vouch/assist.py +561 -0
  72. vouch_paper-0.1/src/vouch/build.py +634 -0
  73. vouch_paper-0.1/src/vouch/catalog.py +187 -0
  74. vouch_paper-0.1/src/vouch/changes.py +381 -0
  75. vouch_paper-0.1/src/vouch/check.py +289 -0
  76. vouch_paper-0.1/src/vouch/cli.py +1315 -0
  77. vouch_paper-0.1/src/vouch/config.py +231 -0
  78. vouch_paper-0.1/src/vouch/console.py +41 -0
  79. vouch_paper-0.1/src/vouch/data/explore.html +409 -0
  80. vouch_paper-0.1/src/vouch/data/vouch.sty +352 -0
  81. vouch_paper-0.1/src/vouch/derived.py +856 -0
  82. vouch_paper-0.1/src/vouch/edithook.py +235 -0
  83. vouch_paper-0.1/src/vouch/exec.py +45 -0
  84. vouch_paper-0.1/src/vouch/explore.py +501 -0
  85. vouch_paper-0.1/src/vouch/figures.py +175 -0
  86. vouch_paper-0.1/src/vouch/fmt.py +87 -0
  87. vouch_paper-0.1/src/vouch/freshness.py +335 -0
  88. vouch_paper-0.1/src/vouch/hashing.py +120 -0
  89. vouch_paper-0.1/src/vouch/hooks.py +61 -0
  90. vouch_paper-0.1/src/vouch/index.py +400 -0
  91. vouch_paper-0.1/src/vouch/init.py +124 -0
  92. vouch_paper-0.1/src/vouch/issues.py +66 -0
  93. vouch_paper-0.1/src/vouch/mcp_server.py +315 -0
  94. vouch_paper-0.1/src/vouch/provenance.py +189 -0
  95. vouch_paper-0.1/src/vouch/render.py +318 -0
  96. vouch_paper-0.1/src/vouch/runner.py +296 -0
  97. vouch_paper-0.1/src/vouch/schema/v1.json +45 -0
  98. vouch_paper-0.1/src/vouch/store.py +201 -0
  99. vouch_paper-0.1/src/vouch/tex/__init__.py +1 -0
  100. vouch_paper-0.1/src/vouch/tex/annotate.py +76 -0
  101. vouch_paper-0.1/src/vouch/tex/emit.py +89 -0
  102. vouch_paper-0.1/src/vouch/tex/lint.py +391 -0
  103. vouch_paper-0.1/src/vouch/tex/scan.py +482 -0
  104. vouch_paper-0.1/src/vouch/tex/suggest.py +93 -0
  105. vouch_paper-0.1/src/vouch/tracing.py +481 -0
  106. vouch_paper-0.1/src/vouch/tracked.py +623 -0
  107. vouch_paper-0.1/src/vouch/units.py +223 -0
  108. vouch_paper-0.1/src/vouch/values.py +315 -0
  109. vouch_paper-0.1/src/vouch/valuesmod.py +42 -0
  110. vouch_paper-0.1/src/vouch/verdict.py +150 -0
  111. vouch_paper-0.1/tests/conftest.py +81 -0
  112. vouch_paper-0.1/tests/test_annotate.py +40 -0
  113. vouch_paper-0.1/tests/test_api.py +359 -0
  114. vouch_paper-0.1/tests/test_assist.py +391 -0
  115. vouch_paper-0.1/tests/test_auto_track.py +297 -0
  116. vouch_paper-0.1/tests/test_build.py +365 -0
  117. vouch_paper-0.1/tests/test_changes.py +127 -0
  118. vouch_paper-0.1/tests/test_check.py +221 -0
  119. vouch_paper-0.1/tests/test_derive.py +632 -0
  120. vouch_paper-0.1/tests/test_explore.py +166 -0
  121. vouch_paper-0.1/tests/test_fmt_config.py +92 -0
  122. vouch_paper-0.1/tests/test_freshness.py +142 -0
  123. vouch_paper-0.1/tests/test_lint.py +131 -0
  124. vouch_paper-0.1/tests/test_mcp.py +193 -0
  125. vouch_paper-0.1/tests/test_render.py +98 -0
  126. vouch_paper-0.1/tests/test_runner.py +154 -0
  127. vouch_paper-0.1/tests/test_scan.py +125 -0
  128. vouch_paper-0.1/tests/test_store.py +89 -0
  129. vouch_paper-0.1/tests/test_tracing.py +300 -0
  130. vouch_paper-0.1/tests/test_track.py +486 -0
  131. vouch_paper-0.1/tests/test_tutorial.py +182 -0
  132. vouch_paper-0.1/tests/test_units.py +151 -0
  133. vouch_paper-0.1/tests/test_values.py +120 -0
@@ -0,0 +1,3 @@
1
+ * text=auto eol=lf
2
+ *.pdf binary
3
+ *.png binary
@@ -0,0 +1,43 @@
1
+ name: docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "docs/**"
8
+ - "mkdocs.yml"
9
+ - ".github/workflows/docs.yml"
10
+ workflow_dispatch: {}
11
+
12
+ permissions:
13
+ contents: read
14
+ pages: write
15
+ id-token: write
16
+
17
+ concurrency:
18
+ group: pages
19
+ cancel-in-progress: false
20
+
21
+ jobs:
22
+ build:
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+ - uses: actions/setup-python@v5
27
+ with:
28
+ python-version: "3.12"
29
+ - run: pip install -r docs/requirements.txt
30
+ - run: mkdocs build --strict
31
+ - uses: actions/upload-pages-artifact@v3
32
+ with:
33
+ path: site
34
+
35
+ deploy:
36
+ needs: build
37
+ runs-on: ubuntu-latest
38
+ environment:
39
+ name: github-pages
40
+ url: ${{ steps.deployment.outputs.page_url }}
41
+ steps:
42
+ - id: deployment
43
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,38 @@
1
+ name: publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.12"
20
+ - run: pip install build
21
+ - run: python -m build
22
+ - uses: actions/upload-artifact@v4
23
+ with:
24
+ name: dist
25
+ path: dist/
26
+
27
+ publish:
28
+ needs: build
29
+ runs-on: ubuntu-latest
30
+ environment: pypi
31
+ permissions:
32
+ id-token: write
33
+ steps:
34
+ - uses: actions/download-artifact@v4
35
+ with:
36
+ name: dist
37
+ path: dist/
38
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,24 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ build/
6
+ dist/
7
+ site/
8
+ src/vouch/_version.py
9
+
10
+ # example outputs (regenerated by running the example)
11
+ examples/**/.vouch/
12
+ examples/**/vouch.sty
13
+ examples/**/vouch-values.tex
14
+ examples/**/vouch-provenance.csv
15
+ examples/**/vouch-tables/
16
+ examples/**/.gitattributes
17
+ examples/**/*.pdf
18
+ examples/**/*.aux
19
+ examples/**/*.log
20
+ examples/**/*.fls
21
+ examples/**/*.fdb_latexmk
22
+ examples/**/*.out
23
+ examples/**/*.upa
24
+ examples/**/*.upb
@@ -0,0 +1,7 @@
1
+ - id: vouch-check
2
+ name: vouch check (paper numbers vouched for by their runs)
3
+ description: Fails when a number the paper cites is unknown, stale, out of sync, or a claim no longer holds.
4
+ entry: vouch check --quiet
5
+ language: python
6
+ pass_filenames: false
7
+ always_run: true
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Felps
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,278 @@
1
+ Metadata-Version: 2.5
2
+ Name: vouch-paper
3
+ Version: 0.1
4
+ Summary: Every number in your paper, vouched for by the code that produced it.
5
+ Author: Daniel Felps
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Daniel Felps
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE
28
+ Classifier: License :: OSI Approved :: MIT License
29
+ Requires-Python: >=3.10
30
+ Requires-Dist: tomli>=1.1; python_version < '3.11'
31
+ Provides-Extra: dev
32
+ Requires-Dist: matplotlib; extra == 'dev'
33
+ Requires-Dist: mcp>=2; extra == 'dev'
34
+ Requires-Dist: pandas; extra == 'dev'
35
+ Requires-Dist: pytest>=7; extra == 'dev'
36
+ Provides-Extra: mcp
37
+ Provides-Extra: pandas
38
+ Requires-Dist: pandas; extra == 'pandas'
39
+ Provides-Extra: test
40
+ Requires-Dist: matplotlib; extra == 'test'
41
+ Requires-Dist: mcp>=2; extra == 'test'
42
+ Requires-Dist: pandas; extra == 'test'
43
+ Requires-Dist: pytest>=7; extra == 'test'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # vouch
47
+
48
+ Every number in your paper, vouched for by the code that produced it.
49
+
50
+ **Full documentation: https://dlfelps.github.io/vouch/**
51
+
52
+ Experiments **record** their results as they run. The paper **cites** them by key,
53
+ `\vouch{key}`, and never contains a typed number. `vouch check` proves that every
54
+ cited value exists, comes from a run whose code hasn't changed since, and that
55
+ every claim still holds. When a cited value changes, vouch shows you the sentences
56
+ to re-read. Any number traces back to the function, arguments, seeds, command and
57
+ commit that produced it, from the terminal, a local web page, or by clicking it in
58
+ the PDF.
59
+
60
+ It is built for people and for LLM agents writing papers: the correct number is
61
+ always cheaper to use than a guessed one.
62
+
63
+ **New here? Start with the [tutorial](examples/tutorial/README.md)** (also
64
+ browsable at https://dlfelps.github.io/vouch/tutorial/): a two-second experiment
65
+ and a paper draft, taken step by step to a PDF whose values, table and figure
66
+ all come from the code.
67
+
68
+ ```console
69
+ $ pip install -e . # from a clone; Python ≥ 3.10, no dependencies
70
+ $ vouch init # finds your paper, writes vouch.toml, copies vouch.sty
71
+ ```
72
+
73
+ The base install is everything, the MCP server included, with no dependencies
74
+ (only `tomli` on Python 3.10). Extras: `.[pandas]` to record pandas DataFrames;
75
+ `.[mcp]` installs nothing more (`vouch mcp` needs nothing); `.[test]` for the test
76
+ suite, which includes the official MCP SDK to test `vouch mcp` against, and
77
+ matplotlib for the tutorial.
78
+
79
+ ## 1. Record
80
+
81
+ Decorate the function that computes your results. Every call is recorded, keyed by
82
+ the function and its arguments, together with the call itself: arguments, seeds,
83
+ each seed's result, how long it took, and the exact code that ran.
84
+
85
+ ```python
86
+ import vouch
87
+
88
+ @vouch.track(over="seed") # calls that differ only in seed -> mean ± std
89
+ def evaluate(dataset: str, model: str, seed: int = 0) -> dict:
90
+ ...
91
+ return {"acc": acc, "loss": loss}
92
+
93
+ for model in ("resnet", "vit"):
94
+ for seed in range(5):
95
+ evaluate("cifar", model, seed=seed)
96
+ # -> evaluate.cifar.resnet.acc, evaluate.cifar.resnet.loss, evaluate.cifar.resnet.time, ...
97
+ ```
98
+
99
+ Run the script as usual: `python train.py`. Formats and descriptions for whole
100
+ families of keys go in `vouch.toml`:
101
+
102
+ ```toml
103
+ [metrics]
104
+ "*.acc" = { fmt = ".1pct", better = "higher", desc = "top-1 test accuracy" }
105
+ "*.loss" = { fmt = ".3f", better = "lower", desc = "test loss" }
106
+ ```
107
+
108
+ Other ways to record, when a decorator doesn't fit:
109
+
110
+ | | |
111
+ |---|---|
112
+ | No code changes at all | list the function in `vouch.toml` (`[[track]] function = "train.py::evaluate"`) and run `python -m vouch.exec train.py` |
113
+ | A single number | `vouch.record("cifar.n_test", 10000, desc="test images")` |
114
+ | A dict or table of results | `vouch.record_all(metrics, prefix="cifar.resnet")`, `vouch.table("main", rows, row_key="model")` |
115
+ | A claim | `vouch.claim("resnet_wins", vouch.gt(acc_r, acc_v), desc="ResNet beats ViT")` |
116
+ | Another language | `vouch run vit_jl --dep src/ -- julia train.jl` (the program writes its values to `$VOUCH_VALUES`) |
117
+ | Results that already exist | `vouch import results.json --run eval --producer eval.py` (marked *imported*) |
118
+
119
+ ## 2. Find the key
120
+
121
+ ```console
122
+ $ vouch explore --open # a local page: script -> function -> keys; one click copies \vouch{...}
123
+ $ vouch search vit accuracy cifar # ranked lookup from the terminal
124
+ $ vouch cite evaluate.cifar.vit.acc # the exact snippet, what it renders as, its subfields
125
+ \vouch{evaluate.cifar.vit.acc} → 90.6 ± 0.4% (fmt .1pct)
126
+ \vouch[.2pct]{evaluate.cifar.vit.acc} → 90.59 ± 0.44% (fmt .2pct)
127
+ top-1 test accuracy · higher is better · fresh · run experiments.train
128
+ subfields: .mean 90.6% · .std 0.4% · .n 5 · .ci95 90.0-91.1% · .min 90.2% · .max 91.1%
129
+ ```
130
+
131
+ `.vouch/CATALOG.md`, rewritten by every build, lists every citable key on one line
132
+ each: the file an agent reads first.
133
+
134
+ Long generated keys can get short names in `vouch_values.py`:
135
+ `vouch.alias("resnet", "evaluate.cifar.resnet")` makes `\vouch{resnet.acc}` work.
136
+
137
+ ## 3. Cite
138
+
139
+ ```latex
140
+ \usepackage{vouch}
141
+ ...
142
+ ResNet reaches \vouch{evaluate.cifar.resnet.acc} top-1 accuracy
143
+ (\vouch[.3f]{evaluate.cifar.resnet.acc.mean} as a fraction).
144
+ \vouchclaim{resnet_wins}{ResNet outperforms ViT.}
145
+ \begin{tabular}{lr} \toprule Model & Acc \\ \midrule \vouchtable{main} \bottomrule \end{tabular}
146
+ ```
147
+
148
+ Then build the generated values and compile as usual:
149
+
150
+ ```console
151
+ $ vouch build
152
+ $ latexmk -pdf paper/main.tex
153
+ ```
154
+
155
+ In the draft, every number is a link to a "Value provenance" appendix: what the
156
+ value is, the call that produced it (`evaluate(dataset=cifar, model=resnet) over
157
+ seed=0..4 (5 calls)`), each seed's result, the run, the command and the commit.
158
+ `\usepackage[final]{vouch}` removes all of it for submission.
159
+
160
+ ## 4. Numbers computed from numbers
161
+
162
+ Differences, ratios, best-of and derived tables live in `vouch_values.py`, never
163
+ in anyone's head. `vouch build` evaluates them and records exactly which values
164
+ each one read.
165
+
166
+ ```python
167
+ import vouch
168
+
169
+ @vouch.derive("cifar.gap", fmt=".1f", unit="points", desc="ResNet minus ViT, points")
170
+ def gap(v):
171
+ return 100 * (v["evaluate.cifar.resnet.acc.mean"] - v["evaluate.cifar.vit.acc.mean"])
172
+
173
+ @vouch.claim("cifar.big_gap", desc="ResNet leads by more than a point")
174
+ def big_gap(v):
175
+ return vouch.gt(v["cifar.gap"], 1) # "2.3 > 1 (margin 130%)"; thin margins warn
176
+ ```
177
+
178
+ `vouch compare A B` does the arithmetic for you (difference, ratio, a Welch t-test
179
+ for two means ± std) and prints the `derive` and `claim` that make it citable;
180
+ `--write` adds them to `vouch_values.py`.
181
+
182
+ A number the paper needs before any run has produced it is a placeholder, never a
183
+ guess:
184
+
185
+ ```python
186
+ vouch.expect("imagenet.vit.acc", desc="ViT top-1 on ImageNet", producer="python train.py --dataset imagenet")
187
+ ```
188
+
189
+ The PDF shows `[pending: imagenet.vit.acc]`, `vouch todo` lists what is owed, and
190
+ the placeholder resolves once a run records the key.
191
+
192
+ ## 5. Check
193
+
194
+ ```console
195
+ $ vouch check
196
+ vouch check: paper/main.tex · 16 citations · 1/1 cited runs fresh
197
+ ✓ OK
198
+ ```
199
+
200
+ `vouch check` never runs your code. It fails when:
201
+ - a cited key doesn't exist
202
+ - the code behind a cited run changed since the run (comments and formatting don't count)
203
+ - a claim no longer holds
204
+ - a figure is stale
205
+ - the generated files are out of date
206
+
207
+ It warns when a cited value changed since someone last read the sentences around
208
+ it (`vouch changes`, then `vouch ack KEY`), and when the paper contains a typed
209
+ number:
210
+
211
+ ```
212
+ ! bare-number 93.2% is typed by hand; it is evaluate.cifar.resnet.acc.mean: cite it as \vouch[.1pct]{...}
213
+ ! no-source 97.1% matches no recorded value: record it where it is computed, or remove it
214
+ ```
215
+
216
+ Converting a paper that already has typed numbers: `vouch suggest` shows, for each
217
+ one, the key that prints it (or that nothing does); `vouch suggest --apply` rewrites
218
+ the unambiguous ones.
219
+
220
+ Use it in a pre-commit hook (`vouch hook install`) and in CI:
221
+
222
+ ```yaml
223
+ # .github/workflows/paper.yml
224
+ name: paper
225
+ on: [push, pull_request]
226
+ jobs:
227
+ vouch:
228
+ runs-on: ubuntu-latest
229
+ steps:
230
+ - uses: actions/checkout@v4
231
+ - uses: actions/setup-python@v5
232
+ with: { python-version: "3.12" }
233
+ - run: pip install -e path/to/vouch # wherever vouch comes from
234
+ - run: vouch check --strict
235
+ ```
236
+
237
+ ## With Claude Code (or another agent)
238
+
239
+ ```console
240
+ $ vouch init --agents --yes
241
+ ```
242
+
243
+ This installs a skill (`.claude/skills/vouch/SKILL.md`), which teaches the agent to
244
+ record, find, cite, compare and wait for results; a short rules block in `CLAUDE.md`;
245
+ and a hook that checks each `.tex` file the agent edits. A typed number, a mistyped
246
+ key or a broken macro goes straight back to the agent in the same turn, with the
247
+ fix. `--stop-gate` also stops the agent from finishing while `vouch check --strict`
248
+ fails.
249
+
250
+ Any MCP client can use the same lookups as tools (search, get a value, cite,
251
+ compare, pending, changes, check, trace) with `vouch mcp`; `vouch init --agents mcp`
252
+ registers it in `.mcp.json`. Acknowledging changes stays a human action: there is
253
+ no tool for it.
254
+
255
+ ## Commands
256
+
257
+ | | |
258
+ |---|---|
259
+ | `vouch init` | set up `vouch.toml`, `.vouch/` and `paper/vouch.sty` |
260
+ | `vouch build` | evaluate `vouch_values.py`, write the generated LaTeX, report changes |
261
+ | `vouch check [--strict]` | the gate (exit 0 pass, 1 fail, 2 could not check) |
262
+ | `vouch explore` | browse every recorded value; copy the LaTeX that cites it |
263
+ | `vouch search` / `vouch cite` | find a key by words / the exact snippet to paste |
264
+ | `vouch compare A B [--write]` | arithmetic and significance between two values, as citable code |
265
+ | `vouch suggest [--apply]` | turn numbers typed into the paper into citations |
266
+ | `vouch todo` | values the paper cites that no run has recorded yet |
267
+ | `vouch ls` / `vouch trace` / `vouch status` | list keys, see where a value came from, see which runs are stale |
268
+ | `vouch changes` / `vouch ack` / `vouch review` | cited values that moved since they were last read |
269
+ | `vouch accept RUN --why ...` | a reviewed staleness that doesn't affect the result |
270
+ | `vouch run` / `vouch import` | record runs of other languages; register existing results files |
271
+ | `vouch sync [--strip]` | write each cited value into a `% vouch:` comment on its line |
272
+ | `vouch export --csv FILE` | the provenance table |
273
+ | `vouch catalog` | rewrite `.vouch/CATALOG.md` |
274
+ | `vouch init --agents` | set up Claude Code: skill, rules, edit hook |
275
+ | `vouch mcp` | the lookups as MCP tools, over stdio |
276
+
277
+ The full design is in [SPEC.md](SPEC.md). Browsable documentation:
278
+ https://dlfelps.github.io/vouch/.
@@ -0,0 +1,233 @@
1
+ # vouch
2
+
3
+ Every number in your paper, vouched for by the code that produced it.
4
+
5
+ **Full documentation: https://dlfelps.github.io/vouch/**
6
+
7
+ Experiments **record** their results as they run. The paper **cites** them by key,
8
+ `\vouch{key}`, and never contains a typed number. `vouch check` proves that every
9
+ cited value exists, comes from a run whose code hasn't changed since, and that
10
+ every claim still holds. When a cited value changes, vouch shows you the sentences
11
+ to re-read. Any number traces back to the function, arguments, seeds, command and
12
+ commit that produced it, from the terminal, a local web page, or by clicking it in
13
+ the PDF.
14
+
15
+ It is built for people and for LLM agents writing papers: the correct number is
16
+ always cheaper to use than a guessed one.
17
+
18
+ **New here? Start with the [tutorial](examples/tutorial/README.md)** (also
19
+ browsable at https://dlfelps.github.io/vouch/tutorial/): a two-second experiment
20
+ and a paper draft, taken step by step to a PDF whose values, table and figure
21
+ all come from the code.
22
+
23
+ ```console
24
+ $ pip install -e . # from a clone; Python ≥ 3.10, no dependencies
25
+ $ vouch init # finds your paper, writes vouch.toml, copies vouch.sty
26
+ ```
27
+
28
+ The base install is everything, the MCP server included, with no dependencies
29
+ (only `tomli` on Python 3.10). Extras: `.[pandas]` to record pandas DataFrames;
30
+ `.[mcp]` installs nothing more (`vouch mcp` needs nothing); `.[test]` for the test
31
+ suite, which includes the official MCP SDK to test `vouch mcp` against, and
32
+ matplotlib for the tutorial.
33
+
34
+ ## 1. Record
35
+
36
+ Decorate the function that computes your results. Every call is recorded, keyed by
37
+ the function and its arguments, together with the call itself: arguments, seeds,
38
+ each seed's result, how long it took, and the exact code that ran.
39
+
40
+ ```python
41
+ import vouch
42
+
43
+ @vouch.track(over="seed") # calls that differ only in seed -> mean ± std
44
+ def evaluate(dataset: str, model: str, seed: int = 0) -> dict:
45
+ ...
46
+ return {"acc": acc, "loss": loss}
47
+
48
+ for model in ("resnet", "vit"):
49
+ for seed in range(5):
50
+ evaluate("cifar", model, seed=seed)
51
+ # -> evaluate.cifar.resnet.acc, evaluate.cifar.resnet.loss, evaluate.cifar.resnet.time, ...
52
+ ```
53
+
54
+ Run the script as usual: `python train.py`. Formats and descriptions for whole
55
+ families of keys go in `vouch.toml`:
56
+
57
+ ```toml
58
+ [metrics]
59
+ "*.acc" = { fmt = ".1pct", better = "higher", desc = "top-1 test accuracy" }
60
+ "*.loss" = { fmt = ".3f", better = "lower", desc = "test loss" }
61
+ ```
62
+
63
+ Other ways to record, when a decorator doesn't fit:
64
+
65
+ | | |
66
+ |---|---|
67
+ | No code changes at all | list the function in `vouch.toml` (`[[track]] function = "train.py::evaluate"`) and run `python -m vouch.exec train.py` |
68
+ | A single number | `vouch.record("cifar.n_test", 10000, desc="test images")` |
69
+ | A dict or table of results | `vouch.record_all(metrics, prefix="cifar.resnet")`, `vouch.table("main", rows, row_key="model")` |
70
+ | A claim | `vouch.claim("resnet_wins", vouch.gt(acc_r, acc_v), desc="ResNet beats ViT")` |
71
+ | Another language | `vouch run vit_jl --dep src/ -- julia train.jl` (the program writes its values to `$VOUCH_VALUES`) |
72
+ | Results that already exist | `vouch import results.json --run eval --producer eval.py` (marked *imported*) |
73
+
74
+ ## 2. Find the key
75
+
76
+ ```console
77
+ $ vouch explore --open # a local page: script -> function -> keys; one click copies \vouch{...}
78
+ $ vouch search vit accuracy cifar # ranked lookup from the terminal
79
+ $ vouch cite evaluate.cifar.vit.acc # the exact snippet, what it renders as, its subfields
80
+ \vouch{evaluate.cifar.vit.acc} → 90.6 ± 0.4% (fmt .1pct)
81
+ \vouch[.2pct]{evaluate.cifar.vit.acc} → 90.59 ± 0.44% (fmt .2pct)
82
+ top-1 test accuracy · higher is better · fresh · run experiments.train
83
+ subfields: .mean 90.6% · .std 0.4% · .n 5 · .ci95 90.0-91.1% · .min 90.2% · .max 91.1%
84
+ ```
85
+
86
+ `.vouch/CATALOG.md`, rewritten by every build, lists every citable key on one line
87
+ each: the file an agent reads first.
88
+
89
+ Long generated keys can get short names in `vouch_values.py`:
90
+ `vouch.alias("resnet", "evaluate.cifar.resnet")` makes `\vouch{resnet.acc}` work.
91
+
92
+ ## 3. Cite
93
+
94
+ ```latex
95
+ \usepackage{vouch}
96
+ ...
97
+ ResNet reaches \vouch{evaluate.cifar.resnet.acc} top-1 accuracy
98
+ (\vouch[.3f]{evaluate.cifar.resnet.acc.mean} as a fraction).
99
+ \vouchclaim{resnet_wins}{ResNet outperforms ViT.}
100
+ \begin{tabular}{lr} \toprule Model & Acc \\ \midrule \vouchtable{main} \bottomrule \end{tabular}
101
+ ```
102
+
103
+ Then build the generated values and compile as usual:
104
+
105
+ ```console
106
+ $ vouch build
107
+ $ latexmk -pdf paper/main.tex
108
+ ```
109
+
110
+ In the draft, every number is a link to a "Value provenance" appendix: what the
111
+ value is, the call that produced it (`evaluate(dataset=cifar, model=resnet) over
112
+ seed=0..4 (5 calls)`), each seed's result, the run, the command and the commit.
113
+ `\usepackage[final]{vouch}` removes all of it for submission.
114
+
115
+ ## 4. Numbers computed from numbers
116
+
117
+ Differences, ratios, best-of and derived tables live in `vouch_values.py`, never
118
+ in anyone's head. `vouch build` evaluates them and records exactly which values
119
+ each one read.
120
+
121
+ ```python
122
+ import vouch
123
+
124
+ @vouch.derive("cifar.gap", fmt=".1f", unit="points", desc="ResNet minus ViT, points")
125
+ def gap(v):
126
+ return 100 * (v["evaluate.cifar.resnet.acc.mean"] - v["evaluate.cifar.vit.acc.mean"])
127
+
128
+ @vouch.claim("cifar.big_gap", desc="ResNet leads by more than a point")
129
+ def big_gap(v):
130
+ return vouch.gt(v["cifar.gap"], 1) # "2.3 > 1 (margin 130%)"; thin margins warn
131
+ ```
132
+
133
+ `vouch compare A B` does the arithmetic for you (difference, ratio, a Welch t-test
134
+ for two means ± std) and prints the `derive` and `claim` that make it citable;
135
+ `--write` adds them to `vouch_values.py`.
136
+
137
+ A number the paper needs before any run has produced it is a placeholder, never a
138
+ guess:
139
+
140
+ ```python
141
+ vouch.expect("imagenet.vit.acc", desc="ViT top-1 on ImageNet", producer="python train.py --dataset imagenet")
142
+ ```
143
+
144
+ The PDF shows `[pending: imagenet.vit.acc]`, `vouch todo` lists what is owed, and
145
+ the placeholder resolves once a run records the key.
146
+
147
+ ## 5. Check
148
+
149
+ ```console
150
+ $ vouch check
151
+ vouch check: paper/main.tex · 16 citations · 1/1 cited runs fresh
152
+ ✓ OK
153
+ ```
154
+
155
+ `vouch check` never runs your code. It fails when:
156
+ - a cited key doesn't exist
157
+ - the code behind a cited run changed since the run (comments and formatting don't count)
158
+ - a claim no longer holds
159
+ - a figure is stale
160
+ - the generated files are out of date
161
+
162
+ It warns when a cited value changed since someone last read the sentences around
163
+ it (`vouch changes`, then `vouch ack KEY`), and when the paper contains a typed
164
+ number:
165
+
166
+ ```
167
+ ! bare-number 93.2% is typed by hand; it is evaluate.cifar.resnet.acc.mean: cite it as \vouch[.1pct]{...}
168
+ ! no-source 97.1% matches no recorded value: record it where it is computed, or remove it
169
+ ```
170
+
171
+ Converting a paper that already has typed numbers: `vouch suggest` shows, for each
172
+ one, the key that prints it (or that nothing does); `vouch suggest --apply` rewrites
173
+ the unambiguous ones.
174
+
175
+ Use it in a pre-commit hook (`vouch hook install`) and in CI:
176
+
177
+ ```yaml
178
+ # .github/workflows/paper.yml
179
+ name: paper
180
+ on: [push, pull_request]
181
+ jobs:
182
+ vouch:
183
+ runs-on: ubuntu-latest
184
+ steps:
185
+ - uses: actions/checkout@v4
186
+ - uses: actions/setup-python@v5
187
+ with: { python-version: "3.12" }
188
+ - run: pip install -e path/to/vouch # wherever vouch comes from
189
+ - run: vouch check --strict
190
+ ```
191
+
192
+ ## With Claude Code (or another agent)
193
+
194
+ ```console
195
+ $ vouch init --agents --yes
196
+ ```
197
+
198
+ This installs a skill (`.claude/skills/vouch/SKILL.md`), which teaches the agent to
199
+ record, find, cite, compare and wait for results; a short rules block in `CLAUDE.md`;
200
+ and a hook that checks each `.tex` file the agent edits. A typed number, a mistyped
201
+ key or a broken macro goes straight back to the agent in the same turn, with the
202
+ fix. `--stop-gate` also stops the agent from finishing while `vouch check --strict`
203
+ fails.
204
+
205
+ Any MCP client can use the same lookups as tools (search, get a value, cite,
206
+ compare, pending, changes, check, trace) with `vouch mcp`; `vouch init --agents mcp`
207
+ registers it in `.mcp.json`. Acknowledging changes stays a human action: there is
208
+ no tool for it.
209
+
210
+ ## Commands
211
+
212
+ | | |
213
+ |---|---|
214
+ | `vouch init` | set up `vouch.toml`, `.vouch/` and `paper/vouch.sty` |
215
+ | `vouch build` | evaluate `vouch_values.py`, write the generated LaTeX, report changes |
216
+ | `vouch check [--strict]` | the gate (exit 0 pass, 1 fail, 2 could not check) |
217
+ | `vouch explore` | browse every recorded value; copy the LaTeX that cites it |
218
+ | `vouch search` / `vouch cite` | find a key by words / the exact snippet to paste |
219
+ | `vouch compare A B [--write]` | arithmetic and significance between two values, as citable code |
220
+ | `vouch suggest [--apply]` | turn numbers typed into the paper into citations |
221
+ | `vouch todo` | values the paper cites that no run has recorded yet |
222
+ | `vouch ls` / `vouch trace` / `vouch status` | list keys, see where a value came from, see which runs are stale |
223
+ | `vouch changes` / `vouch ack` / `vouch review` | cited values that moved since they were last read |
224
+ | `vouch accept RUN --why ...` | a reviewed staleness that doesn't affect the result |
225
+ | `vouch run` / `vouch import` | record runs of other languages; register existing results files |
226
+ | `vouch sync [--strip]` | write each cited value into a `% vouch:` comment on its line |
227
+ | `vouch export --csv FILE` | the provenance table |
228
+ | `vouch catalog` | rewrite `.vouch/CATALOG.md` |
229
+ | `vouch init --agents` | set up Claude Code: skill, rules, edit hook |
230
+ | `vouch mcp` | the lookups as MCP tools, over stdio |
231
+
232
+ The full design is in [SPEC.md](SPEC.md). Browsable documentation:
233
+ https://dlfelps.github.io/vouch/.