claimtrail 0.5.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 (56) hide show
  1. claimtrail-0.5.0/.github/workflows/publish.yml +22 -0
  2. claimtrail-0.5.0/.github/workflows/tests.yml +28 -0
  3. claimtrail-0.5.0/.gitignore +74 -0
  4. claimtrail-0.5.0/INTEGRATION.md +244 -0
  5. claimtrail-0.5.0/LICENSE +21 -0
  6. claimtrail-0.5.0/PKG-INFO +327 -0
  7. claimtrail-0.5.0/README.md +299 -0
  8. claimtrail-0.5.0/docs/history/qprov-engine-overview.pdf +0 -0
  9. claimtrail-0.5.0/examples/lending-review/pipeline.py +55 -0
  10. claimtrail-0.5.0/examples/lending-review/report_draft.md +17 -0
  11. claimtrail-0.5.0/examples/lending-review/run_demo.py +97 -0
  12. claimtrail-0.5.0/examples/q-numbers/claims.tex +41 -0
  13. claimtrail-0.5.0/examples/q-numbers/q_real_demo.py +33 -0
  14. claimtrail-0.5.0/examples/q-numbers/q_real_python.py +318 -0
  15. claimtrail-0.5.0/examples/q-numbers/run_example.py +133 -0
  16. claimtrail-0.5.0/pyproject.toml +45 -0
  17. claimtrail-0.5.0/src/claimtrail/__init__.py +110 -0
  18. claimtrail-0.5.0/src/claimtrail/assertions.py +183 -0
  19. claimtrail-0.5.0/src/claimtrail/audit_paper.py +656 -0
  20. claimtrail-0.5.0/src/claimtrail/audit_report.py +397 -0
  21. claimtrail-0.5.0/src/claimtrail/claims.py +290 -0
  22. claimtrail-0.5.0/src/claimtrail/cli.py +917 -0
  23. claimtrail-0.5.0/src/claimtrail/contrib/__init__.py +5 -0
  24. claimtrail-0.5.0/src/claimtrail/contrib/qnumbers.py +931 -0
  25. claimtrail-0.5.0/src/claimtrail/external.py +149 -0
  26. claimtrail-0.5.0/src/claimtrail/gitinfo.py +32 -0
  27. claimtrail-0.5.0/src/claimtrail/hardware.py +90 -0
  28. claimtrail-0.5.0/src/claimtrail/inputs.py +315 -0
  29. claimtrail-0.5.0/src/claimtrail/ledger.py +247 -0
  30. claimtrail-0.5.0/src/claimtrail/properties.py +150 -0
  31. claimtrail-0.5.0/src/claimtrail/quantities.py +165 -0
  32. claimtrail-0.5.0/src/claimtrail/query.py +30 -0
  33. claimtrail-0.5.0/src/claimtrail/serialize.py +232 -0
  34. claimtrail-0.5.0/src/claimtrail/store.py +1040 -0
  35. claimtrail-0.5.0/src/claimtrail/tracking.py +584 -0
  36. claimtrail-0.5.0/src/claimtrail/verify.py +200 -0
  37. claimtrail-0.5.0/src/qprov/__init__.py +23 -0
  38. claimtrail-0.5.0/tests/__init__.py +0 -0
  39. claimtrail-0.5.0/tests/conftest.py +23 -0
  40. claimtrail-0.5.0/tests/test_assertions.py +127 -0
  41. claimtrail-0.5.0/tests/test_audit_paper.py +290 -0
  42. claimtrail-0.5.0/tests/test_audit_report.py +136 -0
  43. claimtrail-0.5.0/tests/test_canonical_file.py +328 -0
  44. claimtrail-0.5.0/tests/test_claims.py +137 -0
  45. claimtrail-0.5.0/tests/test_cli.py +221 -0
  46. claimtrail-0.5.0/tests/test_compat.py +62 -0
  47. claimtrail-0.5.0/tests/test_external.py +203 -0
  48. claimtrail-0.5.0/tests/test_ledger.py +141 -0
  49. claimtrail-0.5.0/tests/test_paper_tag_gate.py +154 -0
  50. claimtrail-0.5.0/tests/test_properties.py +478 -0
  51. claimtrail-0.5.0/tests/test_quantities.py +75 -0
  52. claimtrail-0.5.0/tests/test_sage_integration.py +105 -0
  53. claimtrail-0.5.0/tests/test_serialize.py +94 -0
  54. claimtrail-0.5.0/tests/test_store.py +473 -0
  55. claimtrail-0.5.0/tests/test_tracking.py +142 -0
  56. claimtrail-0.5.0/tests/test_verify.py +57 -0
@@ -0,0 +1,22 @@
1
+ name: publish
2
+
3
+ # Publishes to PyPI when a version tag (v*) is pushed. Uses PyPI trusted
4
+ # publishing, so no API token is stored in the repo or in secrets.
5
+
6
+ on:
7
+ push:
8
+ tags: ["v*"]
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ permissions:
15
+ id-token: write
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+ - run: pip install build && python -m build
22
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,28 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ python-version: ["3.11", "3.12", "3.13"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ with:
17
+ fetch-depth: 0
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ - run: pip install -e ".[dev]"
22
+ - run: python -m pytest
23
+ - name: Run the example end to end
24
+ run: |
25
+ pip install sympy
26
+ cd examples/q-numbers && python run_example.py
27
+ - name: Run the lending-review demo end to end
28
+ run: python examples/lending-review/run_demo.py
@@ -0,0 +1,74 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+ MANIFEST
23
+
24
+ # Test / coverage artifacts
25
+ .pytest_cache/
26
+ .hypothesis/
27
+ .coverage
28
+ .coverage.*
29
+ htmlcov/
30
+ .tox/
31
+ .nox/
32
+ coverage.xml
33
+ *.cover
34
+
35
+ # Type checkers / linters
36
+ .mypy_cache/
37
+ .ruff_cache/
38
+
39
+ # Virtual envs
40
+ .venv/
41
+ venv/
42
+ env/
43
+
44
+ # IDE
45
+ .vscode/
46
+ .idea/
47
+ *.swp
48
+
49
+ # OS
50
+ .DS_Store
51
+ Thumbs.db
52
+
53
+ # claimtrail stores generated locally (the project keeps its own under .claimtrail/)
54
+ .claimtrail/
55
+ *.sqlite
56
+ *.sqlite-journal
57
+
58
+ # Example run artifacts
59
+ examples/q-numbers/.claimtrail/
60
+ examples/*/*.tex.bak
61
+
62
+ # Local-only override notes
63
+ *.local.md
64
+
65
+ # LaTeX source and build artifacts for engine-overview.pdf; only the PDF is committed
66
+ engine-overview.tex
67
+ engine-overview.aux
68
+ engine-overview.log
69
+ engine-overview.out
70
+ engine-overview.synctex.gz
71
+
72
+ # Demo outputs, regenerated by run_demo.py
73
+ examples/lending-review/data/
74
+ examples/lending-review/report.md
@@ -0,0 +1,244 @@
1
+ # claimtrail integration guide for paper authors
2
+
3
+ This guide shows how to attach claimtrail provenance to a LaTeX manuscript, so that
4
+ every numerical claim in the paper points to a recorded computation in the
5
+ claimtrail store and a reader can run `claimtrail verify <id>` to reproduce it.
6
+
7
+ ## The claimtrail store
8
+
9
+ By default the store is the nearest ancestor `.claimtrail/` directory, falling back
10
+ to `./.claimtrail`. Override it with the `CLAIMTRAIL_HOME` environment variable or
11
+ `claimtrail.set_store_root(path)`.
12
+
13
+ Store contents:
14
+
15
+ - `.claimtrail/claimtrail.sqlite` - SQLite metadata (computations, tags, claims)
16
+ - `.claimtrail/payloads/{id[:2]}/{id}.json.gz` - one gzipped JSON per computation,
17
+ holding inputs, outputs, source code, and captured stdout/stderr
18
+
19
+ A single store can hold the computations behind several papers. Filter a
20
+ paper's claims with the `paper=` tag.
21
+
22
+ ## Two ways a computation enters the store
23
+
24
+ ### (a) Live: the `@tracked` decorator
25
+
26
+ For computations you are writing now:
27
+
28
+ ```python
29
+ import claimtrail
30
+
31
+ @claimtrail.tracked(tags={"paper": "your-paper", "experiment": "X.Y"})
32
+ def compute_thing(N):
33
+ ...
34
+ return result
35
+ ```
36
+
37
+ Every call records a row keyed on
38
+ `blake2b(function_name | input_hash | code_sha)`. The same inputs and the same
39
+ code collapse to the same id, so repeats overwrite in place.
40
+
41
+ **If the function reads a file by path, declare it.** The `data_files`
42
+ parameter makes the file's *contents* part of the input hash, not just the path
43
+ string. Without it, two machines holding files under the same name with
44
+ different content silently collapse to one row:
45
+
46
+ ```python
47
+ @claimtrail.tracked(
48
+ tags={"paper": "your-paper"},
49
+ data_files=["csv_path"],
50
+ )
51
+ def scan_csv(csv_path, N):
52
+ ...
53
+ ```
54
+
55
+ The decorator replaces `csv_path` with a `canonical_file(...)` descriptor
56
+ before hashing. The descriptor carries the file's blake2b digest, size, and
57
+ mtime; the digest is what feeds the input hash. The resulting row has a
58
+ non-NULL `canonical_data_hash` column, so `claimtrail lint` will not flag it as
59
+ NOHASH.
60
+
61
+ ### (b) Retroactive: `register_external(...)`
62
+
63
+ For pre-existing computations whose output is already on disk:
64
+
65
+ ```python
66
+ import claimtrail
67
+
68
+ claimtrail.register_external(
69
+ function_name="my_search_v1", # logical name; part of the id
70
+ inputs={"target": "x", "N": 2000}, # dict; hashed for the id
71
+ outputs={"result": 0}, # any JSON-serializable value
72
+ code_path="path/to/script.py", # recorded in the payload
73
+ code_sha="3c0d5d7f...", # part of the id
74
+ runtime_seconds=12.5, # optional
75
+ tags={"paper": "your-paper", "retroactive": True},
76
+ source_file="path/to/original/output.json",
77
+ notes="anything useful",
78
+ )
79
+ ```
80
+
81
+ The id is again `blake2b(function_name | input_hash | code_sha)`. Re-running the
82
+ registration script is idempotent by design, so you can re-run it whenever you
83
+ add or change a record and the database stays clean.
84
+
85
+ ## Claims
86
+
87
+ A claim is a one-line factual statement, optionally linked to a computation.
88
+ Claims are what end up in the paper as `\fact{...}` macros.
89
+
90
+ ```python
91
+ claimtrail.claim(
92
+ "No polynomial of bidegree at most (6, 50) annihilates [x]_q modulo q^2000.",
93
+ computation_id="<some_comp_id>",
94
+ claim_id="emptiness_x", # stable id; re-runs overwrite
95
+ tags={"paper": "your-paper"}, # paper tag gates this claim
96
+ )
97
+ ```
98
+
99
+ ### The paper-tag gate
100
+
101
+ When a claim carries `tags={"paper": "..."}`, claimtrail requires a non-NULL
102
+ `computation_id`. Calls without one raise `UnbackedPaperClaimError`, so a
103
+ paper-bound statement can never silently render as `\provid{None}` in the
104
+ exported LaTeX.
105
+
106
+ If you genuinely need to stage a claim before its computation lands (for
107
+ example, writing a draft from notes while a long scan is still running), pass
108
+ `allow_unbacked=True` and back-attach later:
109
+
110
+ ```python
111
+ # Stage now:
112
+ claimtrail.claim(
113
+ "A statement to be backed once the scan finishes.",
114
+ tags={"paper": "your-paper"},
115
+ allow_unbacked=True,
116
+ claim_id="staged_claim",
117
+ )
118
+
119
+ # Back-attach after the computation registers:
120
+ claimtrail.claim(
121
+ "A statement to be backed once the scan finishes.",
122
+ tags={"paper": "your-paper"},
123
+ computation_id="<the_real_comp_id>",
124
+ claim_id="staged_claim", # same id; overwrites in place
125
+ )
126
+ ```
127
+
128
+ `claimtrail lint` flags every unbacked paper claim, so a pre-flight catches anything
129
+ left staged.
130
+
131
+ ### Three claim-id strategies
132
+
133
+ | Need | Use | Effect |
134
+ |---|---|---|
135
+ | One-off interactive claim | default | random hex id every call |
136
+ | Idempotent batch script | `deterministic_id=True` | id derived from `(text, comp_id, value_numeric)` |
137
+ | Stable claim that updates with new data | `claim_id="<your_key>"` | you control the id; overwrites in place |
138
+
139
+ Use `claim_id=...` for any claim whose text will evolve as you add
140
+ computations (for example a bound widening from `(6,50)` to `(7,50)`).
141
+ Re-running the registration script then updates the same row instead of leaving
142
+ stale claims.
143
+
144
+ ## Tags
145
+
146
+ Every computation and most claims carry tags. Tags are how the paper-level
147
+ filter works (`claimtrail export-latex --tag paper=your-paper`). Useful conventions:
148
+
149
+ - `paper`: short slug for the paper a record contributes to.
150
+ - `phase`: lifecycle stage (`part-1`, `validation`, ...).
151
+ - `retroactive`: `True` for records added via `register_external`.
152
+ - `type`: cross-cutting kinds (`verification`, `negative_control`, ...).
153
+
154
+ Find by tag:
155
+
156
+ ```python
157
+ claimtrail.find(tags={"paper": "your-paper", "phase": "part-1"}, limit=500)
158
+ ```
159
+
160
+ ## Generating `claims.tex`
161
+
162
+ The CLI exports every claim in the store as a `\fact{...}` macro with a
163
+ `\footnote{Provenance: \provid{<id>}}` attached. Filter by tag:
164
+
165
+ ```bash
166
+ python -m claimtrail.cli export-latex \
167
+ --tag paper=your-paper \
168
+ --output claims.tex
169
+ ```
170
+
171
+ The exporter runs a `latexify` pass over every `$...$` math span, turning
172
+ polynomial strings like `X*q^10` into clean LaTeX (`X q^{10}`). It is automatic
173
+ and idempotent.
174
+
175
+ ## Inlining into `main.tex`
176
+
177
+ Add to the preamble:
178
+
179
+ ```latex
180
+ % Provenance macros required by claims.tex
181
+ \newcommand{\provid}[1]{\texttt{\small #1}}
182
+ \newcommand{\fact}[1]{#1} % or wrap in a box, theorem env, etc.
183
+
184
+ \input{claims.tex}
185
+ ```
186
+
187
+ Then footnote each numerical statement with its claim id:
188
+
189
+ ```latex
190
+ \begin{theorem}
191
+ ... \footnote{Claim \provid{emptiness\_x}; reproducible via
192
+ \texttt{claimtrail verify <id>}.}
193
+ \end{theorem}
194
+ ```
195
+
196
+ Stable claim ids (`emptiness_x`) read better in the source than the 32-char
197
+ blake2b digests, which is one reason to use them.
198
+
199
+ ## Cross-checking with a second tool
200
+
201
+ For a result that rests on a single computer algebra system, run a second,
202
+ independent implementation and record both. If the two disagree, you want to
203
+ know before the number reaches the paper, not after. Record each side as its
204
+ own computation and tag them so a reviewer can see that both were checked.
205
+
206
+ ## Verifying a recorded computation
207
+
208
+ ```bash
209
+ claimtrail verify <comp_id>
210
+ ```
211
+
212
+ This re-imports the original function from its recorded module, re-invokes it
213
+ with the recorded args and kwargs, and compares the output hash byte for byte.
214
+ Determinism is required: any uncontrolled randomness makes verify fail. The
215
+ recommended pattern is to seed inside the decorated function so the seed shows
216
+ up in the input hash.
217
+
218
+ `verify` is only meaningful for `@tracked` computations. Records added via
219
+ `register_external` are immutable records of work done elsewhere and cannot be
220
+ re-run by claimtrail itself.
221
+
222
+ ## Common pitfalls
223
+
224
+ - **Non-stable inputs.** If the `inputs` dict varies between runs due to
225
+ insertion order or floating-point quirks, ids will differ. Use
226
+ deterministic, JSON-serializable types.
227
+ - **Claims pile up.** Without `claim_id` or `deterministic_id`, every
228
+ `claimtrail.claim(...)` call mints a new row, so re-running a script duplicates.
229
+ Pin claim ids in batch scripts.
230
+ - **Filename-only file inputs.** A tracked function that takes `csv_path` as a
231
+ plain string and reads it inside the body hashes only the path string. Two
232
+ files with the same name but different contents then collapse to one id.
233
+ Declare `data_files=["csv_path"]`, or wrap the call site in
234
+ `canonical_file(...)`. `claimtrail lint` flags these as NOHASH advisories.
235
+ - **Paper-tagged claim with no computation.** Calling `claimtrail.claim(...,
236
+ tags={"paper": "..."}, computation_id=None)` raises
237
+ `UnbackedPaperClaimError`. Stage with `allow_unbacked=True` and back-attach
238
+ before exporting LaTeX.
239
+
240
+ ## Asking for help
241
+
242
+ If something in claimtrail is not doing what this guide claims, prefer reading the
243
+ source over guessing: the package is small. The public API surface in
244
+ `src/claimtrail/__init__.py` is the source of truth for what exists.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Patrick Taylor
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.